diff options
-rw-r--r-- | src/boot/efi/splash.c | 156 |
1 files changed, 80 insertions, 76 deletions
diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 5bc1084e62..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; } @@ -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; - - assert(dst); - - alpha = (source & 0xff); - - /* convert src from RGBA to XRGB */ - src = source >> 8; +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]) { - /* 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; + assert(dib); - *dst = (rb | g); + 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); + + 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; + + 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]; - pixel_blend((uint32_t *)out, i); + 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 += 3; + 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, |