Merge pull request #25498 from medhefgo/stub-splash
stub: Fix splash alpha blending
This commit is contained in:
+79
-75
@@ -39,16 +39,11 @@ struct bmp_map {
|
||||
|
||||
static EFI_STATUS bmp_parse_header(
|
||||
const uint8_t *bmp,
|
||||
UINTN size,
|
||||
size_t size,
|
||||
struct bmp_dib **ret_dib,
|
||||
struct bmp_map **ret_map,
|
||||
const uint8_t **pixmap) {
|
||||
|
||||
struct bmp_file *file;
|
||||
struct bmp_dib *dib;
|
||||
struct bmp_map *map;
|
||||
UINTN row_size;
|
||||
|
||||
assert(bmp);
|
||||
assert(ret_dib);
|
||||
assert(ret_map);
|
||||
@@ -58,7 +53,7 @@ static EFI_STATUS bmp_parse_header(
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/* check file header */
|
||||
file = (struct bmp_file *)bmp;
|
||||
struct bmp_file *file = (struct bmp_file *) bmp;
|
||||
if (file->signature[0] != 'B' || file->signature[1] != 'M')
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (file->size != size)
|
||||
@@ -67,7 +62,7 @@ static EFI_STATUS bmp_parse_header(
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/* check device-independent bitmap */
|
||||
dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
|
||||
struct bmp_dib *dib = (struct bmp_dib *) (bmp + sizeof(struct bmp_file));
|
||||
if (dib->size < sizeof(struct bmp_dib))
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
@@ -92,38 +87,26 @@ static EFI_STATUS bmp_parse_header(
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4;
|
||||
size_t row_size = ((size_t) dib->depth * dib->x + 31) / 32 * 4;
|
||||
if (file->size - file->offset < dib->y * row_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (row_size * dib->y > 64 * 1024 * 1024)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/* check color table */
|
||||
map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
|
||||
struct bmp_map *map = (struct bmp_map *) (bmp + sizeof(struct bmp_file) + dib->size);
|
||||
if (file->offset < sizeof(struct bmp_file) + dib->size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (file->offset > sizeof(struct bmp_file) + dib->size) {
|
||||
uint32_t map_count;
|
||||
UINTN map_size;
|
||||
uint32_t map_count = 0;
|
||||
|
||||
if (dib->colors_used)
|
||||
map_count = dib->colors_used;
|
||||
else {
|
||||
switch (dib->depth) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 8:
|
||||
map_count = 1 << dib->depth;
|
||||
break;
|
||||
else if (IN_SET(dib->depth, 1, 4, 8))
|
||||
map_count = 1 << dib->depth;
|
||||
|
||||
default:
|
||||
map_count = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
|
||||
size_t map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
|
||||
if (map_size != sizeof(struct bmp_map) * map_count)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -135,28 +118,51 @@ static EFI_STATUS bmp_parse_header(
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static void pixel_blend(uint32_t *dst, const uint32_t source) {
|
||||
uint32_t alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
|
||||
enum Channels { R, G, B, A, _CHANNELS_MAX };
|
||||
static void read_channel_maks(
|
||||
const struct bmp_dib *dib,
|
||||
uint32_t channel_mask[static _CHANNELS_MAX],
|
||||
uint8_t channel_shift[static _CHANNELS_MAX],
|
||||
uint8_t channel_scale[static _CHANNELS_MAX]) {
|
||||
|
||||
assert(dst);
|
||||
assert(dib);
|
||||
|
||||
alpha = (source & 0xff);
|
||||
if (IN_SET(dib->depth, 16, 32) && dib->size >= sizeof(*dib) + 3 * sizeof(uint32_t)) {
|
||||
uint32_t *mask = (uint32_t *) ((uint8_t *) dib + sizeof(*dib));
|
||||
channel_mask[R] = mask[R];
|
||||
channel_mask[G] = mask[G];
|
||||
channel_mask[B] = mask[B];
|
||||
channel_shift[R] = __builtin_ctz(mask[R]);
|
||||
channel_shift[G] = __builtin_ctz(mask[G]);
|
||||
channel_shift[B] = __builtin_ctz(mask[B]);
|
||||
channel_scale[R] = 0xff / ((1 << __builtin_popcount(mask[R])) - 1);
|
||||
channel_scale[G] = 0xff / ((1 << __builtin_popcount(mask[G])) - 1);
|
||||
channel_scale[B] = 0xff / ((1 << __builtin_popcount(mask[B])) - 1);
|
||||
|
||||
/* convert src from RGBA to XRGB */
|
||||
src = source >> 8;
|
||||
|
||||
/* decompose into RB and G components */
|
||||
src_rb = (src & 0xff00ff);
|
||||
src_g = (src & 0x00ff00);
|
||||
|
||||
dst_rb = (*dst & 0xff00ff);
|
||||
dst_g = (*dst & 0x00ff00);
|
||||
|
||||
/* blend */
|
||||
rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
|
||||
g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00;
|
||||
|
||||
*dst = (rb | g);
|
||||
if (dib->size >= sizeof(*dib) + 4 * sizeof(uint32_t) && mask[A] != 0) {
|
||||
channel_mask[A] = mask[A];
|
||||
channel_shift[A] = __builtin_ctz(mask[A]);
|
||||
channel_scale[A] = 0xff / ((1 << __builtin_popcount(mask[A])) - 1);
|
||||
} else {
|
||||
channel_mask[A] = 0;
|
||||
channel_shift[A] = 0;
|
||||
channel_scale[A] = 0;
|
||||
}
|
||||
} else {
|
||||
bool bpp16 = dib->depth == 16;
|
||||
channel_mask[R] = bpp16 ? 0x7C00 : 0xFF0000;
|
||||
channel_mask[G] = bpp16 ? 0x03E0 : 0x00FF00;
|
||||
channel_mask[B] = bpp16 ? 0x001F : 0x0000FF;
|
||||
channel_mask[A] = bpp16 ? 0x0000 : 0x000000;
|
||||
channel_shift[R] = bpp16 ? 0xA : 0x10;
|
||||
channel_shift[G] = bpp16 ? 0x5 : 0x08;
|
||||
channel_shift[B] = bpp16 ? 0x0 : 0x00;
|
||||
channel_shift[A] = bpp16 ? 0x0 : 0x00;
|
||||
channel_scale[R] = bpp16 ? 0x08 : 0x1;
|
||||
channel_scale[G] = bpp16 ? 0x08 : 0x1;
|
||||
channel_scale[B] = bpp16 ? 0x08 : 0x1;
|
||||
channel_scale[A] = bpp16 ? 0x00 : 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
static EFI_STATUS bmp_to_blt(
|
||||
@@ -172,17 +178,19 @@ static EFI_STATUS bmp_to_blt(
|
||||
assert(map);
|
||||
assert(pixmap);
|
||||
|
||||
uint32_t channel_mask[_CHANNELS_MAX];
|
||||
uint8_t channel_shift[_CHANNELS_MAX], channel_scale[_CHANNELS_MAX];
|
||||
read_channel_maks(dib, channel_mask, channel_shift, channel_scale);
|
||||
|
||||
/* transform and copy pixels */
|
||||
in = pixmap;
|
||||
for (UINTN y = 0; y < dib->y; y++) {
|
||||
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
|
||||
UINTN row_size;
|
||||
for (uint32_t y = 0; y < dib->y; y++) {
|
||||
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out = &buf[(dib->y - y - 1) * dib->x];
|
||||
|
||||
out = &buf[(dib->y - y - 1) * dib->x];
|
||||
for (UINTN x = 0; x < dib->x; x++, in++, out++) {
|
||||
for (uint32_t x = 0; x < dib->x; x++, in++, out++) {
|
||||
switch (dib->depth) {
|
||||
case 1: {
|
||||
for (UINTN i = 0; i < 8 && x < dib->x; i++) {
|
||||
for (unsigned i = 0; i < 8 && x < dib->x; i++) {
|
||||
out->Red = map[((*in) >> (7 - i)) & 1].red;
|
||||
out->Green = map[((*in) >> (7 - i)) & 1].green;
|
||||
out->Blue = map[((*in) >> (7 - i)) & 1].blue;
|
||||
@@ -195,9 +203,7 @@ static EFI_STATUS bmp_to_blt(
|
||||
}
|
||||
|
||||
case 4: {
|
||||
UINTN i;
|
||||
|
||||
i = (*in) >> 4;
|
||||
unsigned i = (*in) >> 4;
|
||||
out->Red = map[i].red;
|
||||
out->Green = map[i].green;
|
||||
out->Blue = map[i].blue;
|
||||
@@ -218,16 +224,6 @@ static EFI_STATUS bmp_to_blt(
|
||||
out->Blue = map[*in].blue;
|
||||
break;
|
||||
|
||||
case 16: {
|
||||
uint16_t i = *(uint16_t *) in;
|
||||
|
||||
out->Red = (i & 0x7c00) >> 7;
|
||||
out->Green = (i & 0x3e0) >> 2;
|
||||
out->Blue = (i & 0x1f) << 3;
|
||||
in += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 24:
|
||||
out->Red = in[2];
|
||||
out->Green = in[1];
|
||||
@@ -235,34 +231,42 @@ static EFI_STATUS bmp_to_blt(
|
||||
in += 2;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
case 32: {
|
||||
uint32_t i = *(uint32_t *) in;
|
||||
uint32_t i = dib->depth == 16 ? *(uint16_t *) in : *(uint32_t *) in;
|
||||
|
||||
pixel_blend((uint32_t *)out, i);
|
||||
uint8_t r = ((i & channel_mask[R]) >> channel_shift[R]) * channel_scale[R],
|
||||
g = ((i & channel_mask[G]) >> channel_shift[G]) * channel_scale[G],
|
||||
b = ((i & channel_mask[B]) >> channel_shift[B]) * channel_scale[B],
|
||||
a = 0xFFu;
|
||||
if (channel_mask[A] != 0)
|
||||
a = ((i & channel_mask[A]) >> channel_shift[A]) * channel_scale[A];
|
||||
|
||||
in += 3;
|
||||
out->Red = (out->Red * (0xFFu - a) + r * a) >> 8;
|
||||
out->Green = (out->Green * (0xFFu - a) + g * a) >> 8;
|
||||
out->Blue = (out->Blue * (0xFFu - a) + b * a) >> 8;
|
||||
|
||||
in += dib->depth == 16 ? 1 : 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add row padding; new lines always start at 32 bit boundary */
|
||||
row_size = in - pixmap;
|
||||
size_t row_size = in - pixmap;
|
||||
in += ((row_size + 3) & ~3) - row_size;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS graphics_splash(const uint8_t *content, UINTN len) {
|
||||
EFI_STATUS graphics_splash(const uint8_t *content, size_t len) {
|
||||
EFI_GRAPHICS_OUTPUT_BLT_PIXEL background = {};
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
|
||||
struct bmp_dib *dib;
|
||||
struct bmp_map *map;
|
||||
const uint8_t *pixmap;
|
||||
_cleanup_free_ void *blt = NULL;
|
||||
UINTN x_pos = 0;
|
||||
UINTN y_pos = 0;
|
||||
size_t x_pos = 0, y_pos = 0;
|
||||
EFI_STATUS err;
|
||||
|
||||
if (len == 0)
|
||||
@@ -297,9 +301,9 @@ EFI_STATUS graphics_splash(const uint8_t *content, UINTN len) {
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
/* EFI buffer */
|
||||
blt = xnew(EFI_GRAPHICS_OUTPUT_BLT_PIXEL, dib->x * dib->y);
|
||||
|
||||
/* Read in current screen content to perform proper alpha blending. */
|
||||
_cleanup_free_ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt = xnew(
|
||||
EFI_GRAPHICS_OUTPUT_BLT_PIXEL, dib->x * dib->y);
|
||||
err = GraphicsOutput->Blt(
|
||||
GraphicsOutput, blt,
|
||||
EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
|
||||
|
||||
Reference in New Issue
Block a user