1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "proto/graphics-output.h"
7 #include "unaligned-fundamental.h"
17 /* we require at least BITMAPINFOHEADER, later versions are
18 accepted, but their features ignored */
27 int32_t x_pixel_meter
;
28 int32_t y_pixel_meter
;
30 uint32_t colors_important
;
31 uint32_t channel_mask_r
;
32 uint32_t channel_mask_g
;
33 uint32_t channel_mask_b
;
34 uint32_t channel_mask_a
;
37 #define SIZEOF_BMP_DIB offsetof(struct bmp_dib, channel_mask_r)
38 #define SIZEOF_BMP_DIB_RGB offsetof(struct bmp_dib, channel_mask_a)
39 #define SIZEOF_BMP_DIB_RGBA sizeof(struct bmp_dib)
48 static EFI_STATUS
bmp_parse_header(
51 struct bmp_dib
**ret_dib
,
52 struct bmp_map
**ret_map
,
53 const uint8_t **pixmap
) {
60 if (size
< sizeof(struct bmp_file
) + SIZEOF_BMP_DIB
)
61 return EFI_INVALID_PARAMETER
;
63 /* check file header */
64 struct bmp_file
*file
= (struct bmp_file
*) bmp
;
65 if (file
->signature
[0] != 'B' || file
->signature
[1] != 'M')
66 return EFI_INVALID_PARAMETER
;
67 if (file
->size
!= size
)
68 return EFI_INVALID_PARAMETER
;
69 if (file
->size
< file
->offset
)
70 return EFI_INVALID_PARAMETER
;
72 /* check device-independent bitmap */
73 struct bmp_dib
*dib
= (struct bmp_dib
*) (bmp
+ sizeof(struct bmp_file
));
74 if (dib
->size
< SIZEOF_BMP_DIB
)
75 return EFI_UNSUPPORTED
;
82 if (dib
->compression
!= 0)
83 return EFI_UNSUPPORTED
;
89 if (dib
->compression
!= 0 && dib
->compression
!= 3)
90 return EFI_UNSUPPORTED
;
95 return EFI_UNSUPPORTED
;
98 size_t row_size
= ((size_t) dib
->depth
* dib
->x
+ 31) / 32 * 4;
99 if (file
->size
- file
->offset
< dib
->y
* row_size
)
100 return EFI_INVALID_PARAMETER
;
101 if (row_size
* dib
->y
> 64 * 1024 * 1024)
102 return EFI_INVALID_PARAMETER
;
104 /* check color table */
105 struct bmp_map
*map
= (struct bmp_map
*) (bmp
+ sizeof(struct bmp_file
) + dib
->size
);
106 if (file
->offset
< sizeof(struct bmp_file
) + dib
->size
)
107 return EFI_INVALID_PARAMETER
;
109 if (file
->offset
> sizeof(struct bmp_file
) + dib
->size
) {
110 uint32_t map_count
= 0;
112 if (dib
->colors_used
)
113 map_count
= dib
->colors_used
;
114 else if (IN_SET(dib
->depth
, 1, 4, 8))
115 map_count
= 1 << dib
->depth
;
117 size_t map_size
= file
->offset
- (sizeof(struct bmp_file
) + dib
->size
);
118 if (map_size
!= sizeof(struct bmp_map
) * map_count
)
119 return EFI_INVALID_PARAMETER
;
124 *pixmap
= bmp
+ file
->offset
;
129 enum Channels
{ R
, G
, B
, A
, _CHANNELS_MAX
};
130 static void read_channel_maks(
131 const struct bmp_dib
*dib
,
132 uint32_t channel_mask
[static _CHANNELS_MAX
],
133 uint8_t channel_shift
[static _CHANNELS_MAX
],
134 uint8_t channel_scale
[static _CHANNELS_MAX
]) {
138 if (IN_SET(dib
->depth
, 16, 32) && dib
->size
>= SIZEOF_BMP_DIB_RGB
) {
139 channel_mask
[R
] = dib
->channel_mask_r
;
140 channel_mask
[G
] = dib
->channel_mask_g
;
141 channel_mask
[B
] = dib
->channel_mask_b
;
142 channel_shift
[R
] = __builtin_ctz(dib
->channel_mask_r
);
143 channel_shift
[G
] = __builtin_ctz(dib
->channel_mask_g
);
144 channel_shift
[B
] = __builtin_ctz(dib
->channel_mask_b
);
145 channel_scale
[R
] = 0xff / ((1 << popcount(dib
->channel_mask_r
)) - 1);
146 channel_scale
[G
] = 0xff / ((1 << popcount(dib
->channel_mask_g
)) - 1);
147 channel_scale
[B
] = 0xff / ((1 << popcount(dib
->channel_mask_b
)) - 1);
149 if (dib
->size
>= SIZEOF_BMP_DIB_RGBA
&& dib
->channel_mask_a
!= 0) {
150 channel_mask
[A
] = dib
->channel_mask_a
;
151 channel_shift
[A
] = __builtin_ctz(dib
->channel_mask_a
);
152 channel_scale
[A
] = 0xff / ((1 << popcount(dib
->channel_mask_a
)) - 1);
155 channel_shift
[A
] = 0;
156 channel_scale
[A
] = 0;
159 bool bpp16
= dib
->depth
== 16;
160 channel_mask
[R
] = bpp16
? 0x7C00 : 0xFF0000;
161 channel_mask
[G
] = bpp16
? 0x03E0 : 0x00FF00;
162 channel_mask
[B
] = bpp16
? 0x001F : 0x0000FF;
163 channel_mask
[A
] = bpp16
? 0x0000 : 0x000000;
164 channel_shift
[R
] = bpp16
? 0xA : 0x10;
165 channel_shift
[G
] = bpp16
? 0x5 : 0x08;
166 channel_shift
[B
] = bpp16
? 0x0 : 0x00;
167 channel_shift
[A
] = bpp16
? 0x0 : 0x00;
168 channel_scale
[R
] = bpp16
? 0x08 : 0x1;
169 channel_scale
[G
] = bpp16
? 0x08 : 0x1;
170 channel_scale
[B
] = bpp16
? 0x08 : 0x1;
171 channel_scale
[A
] = bpp16
? 0x00 : 0x0;
175 static EFI_STATUS
bmp_to_blt(
176 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*buf
,
179 const uint8_t *pixmap
) {
188 uint32_t channel_mask
[_CHANNELS_MAX
];
189 uint8_t channel_shift
[_CHANNELS_MAX
], channel_scale
[_CHANNELS_MAX
];
190 read_channel_maks(dib
, channel_mask
, channel_shift
, channel_scale
);
192 /* transform and copy pixels */
194 for (uint32_t y
= 0; y
< dib
->y
; y
++) {
195 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*out
= &buf
[(dib
->y
- y
- 1) * dib
->x
];
197 for (uint32_t x
= 0; x
< dib
->x
; x
++, in
++, out
++) {
198 switch (dib
->depth
) {
200 for (unsigned i
= 0; i
< 8 && x
< dib
->x
; i
++) {
201 out
->Red
= map
[((*in
) >> (7 - i
)) & 1].red
;
202 out
->Green
= map
[((*in
) >> (7 - i
)) & 1].green
;
203 out
->Blue
= map
[((*in
) >> (7 - i
)) & 1].blue
;
213 unsigned i
= (*in
) >> 4;
214 out
->Red
= map
[i
].red
;
215 out
->Green
= map
[i
].green
;
216 out
->Blue
= map
[i
].blue
;
217 if (x
< (dib
->x
- 1)) {
221 out
->Red
= map
[i
].red
;
222 out
->Green
= map
[i
].green
;
223 out
->Blue
= map
[i
].blue
;
229 out
->Red
= map
[*in
].red
;
230 out
->Green
= map
[*in
].green
;
231 out
->Blue
= map
[*in
].blue
;
243 uint32_t i
= dib
->depth
== 16 ? unaligned_read_ne16(in
) :
244 unaligned_read_ne32(in
);
246 uint8_t r
= ((i
& channel_mask
[R
]) >> channel_shift
[R
]) * channel_scale
[R
],
247 g
= ((i
& channel_mask
[G
]) >> channel_shift
[G
]) * channel_scale
[G
],
248 b
= ((i
& channel_mask
[B
]) >> channel_shift
[B
]) * channel_scale
[B
],
250 if (channel_mask
[A
] != 0)
251 a
= ((i
& channel_mask
[A
]) >> channel_shift
[A
]) * channel_scale
[A
];
253 out
->Red
= (out
->Red
* (0xFFu
- a
) + r
* a
) >> 8;
254 out
->Green
= (out
->Green
* (0xFFu
- a
) + g
* a
) >> 8;
255 out
->Blue
= (out
->Blue
* (0xFFu
- a
) + b
* a
) >> 8;
257 in
+= dib
->depth
== 16 ? 1 : 3;
263 /* add row padding; new lines always start at 32 bit boundary */
264 size_t row_size
= in
- pixmap
;
265 in
+= ((row_size
+ 3) & ~3) - row_size
;
271 EFI_STATUS
graphics_splash(const uint8_t *content
, size_t len
) {
272 EFI_GRAPHICS_OUTPUT_BLT_PIXEL background
= {};
273 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
= NULL
;
276 const uint8_t *pixmap
;
277 size_t x_pos
= 0, y_pos
= 0;
285 if (strcaseeq16(ST
->FirmwareVendor
, u
"Apple")) {
286 background
.Red
= 0xc0;
287 background
.Green
= 0xc0;
288 background
.Blue
= 0xc0;
291 err
= BS
->LocateProtocol(MAKE_GUID_PTR(EFI_GRAPHICS_OUTPUT_PROTOCOL
), NULL
, (void **) &GraphicsOutput
);
292 if (err
!= EFI_SUCCESS
)
295 err
= bmp_parse_header(content
, len
, &dib
, &map
, &pixmap
);
296 if (err
!= EFI_SUCCESS
)
299 if (dib
->x
< GraphicsOutput
->Mode
->Info
->HorizontalResolution
)
300 x_pos
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
- dib
->x
) / 2;
301 if (dib
->y
< GraphicsOutput
->Mode
->Info
->VerticalResolution
)
302 y_pos
= (GraphicsOutput
->Mode
->Info
->VerticalResolution
- dib
->y
) / 2;
304 err
= GraphicsOutput
->Blt(
305 GraphicsOutput
, &background
,
306 EfiBltVideoFill
, 0, 0, 0, 0,
307 GraphicsOutput
->Mode
->Info
->HorizontalResolution
,
308 GraphicsOutput
->Mode
->Info
->VerticalResolution
, 0);
309 if (err
!= EFI_SUCCESS
)
312 /* Read in current screen content to perform proper alpha blending. */
313 _cleanup_free_ EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*blt
= xnew(
314 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
, dib
->x
* dib
->y
);
315 err
= GraphicsOutput
->Blt(
317 EfiBltVideoToBltBuffer
, x_pos
, y_pos
, 0, 0,
319 if (err
!= EFI_SUCCESS
)
322 err
= bmp_to_blt(blt
, dib
, map
, pixmap
);
323 if (err
!= EFI_SUCCESS
)
326 err
= graphics_mode(true);
327 if (err
!= EFI_SUCCESS
)
330 return GraphicsOutput
->Blt(
332 EfiBltBufferToVideo
, 0, 0, x_pos
, y_pos
,