From dde03dd2a843b05d65885ce1242e43c8cabb9924 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Wed, 23 Nov 2022 13:57:34 +0100 Subject: [PATCH 1/2] stub: Fix splash alpha blending How to interpret the pixel format depends on the masks in the DIB header (if present). Also, 16bpp (unlike 24bpp) can carry an alpha channel. This was previously not accounted for. --- src/boot/efi/splash.c | 91 ++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 5bc1084e62..8b6c881dd1 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -135,28 +135,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,6 +195,10 @@ 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++) { @@ -218,16 +245,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,12 +252,22 @@ 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; } } From 27e1f25ef7ba918c7bb221de149353c141570848 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Thu, 24 Nov 2022 14:03:01 +0100 Subject: [PATCH 2/2] stub: Small code style changes No actual changes in behavior. --- src/boot/efi/splash.c | 63 ++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 8b6c881dd1..25df97eb21 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -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; } @@ -201,15 +184,13 @@ static EFI_STATUS bmp_to_blt( /* 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; @@ -222,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; @@ -274,22 +253,20 @@ static EFI_STATUS bmp_to_blt( } /* 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) @@ -324,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,