chiark / gitweb /
3f2aa27ddd207c91dfcca4501027d4a6fbcea4f5
[elogind.git] / src / boot / efi / splash.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
15  * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
16  */
17
18 #include <efi.h>
19 #include <efilib.h>
20
21 #include "util.h"
22 #include "splash.h"
23
24 struct bmp_file {
25         CHAR8 signature[2];
26         UINT32 size;
27         UINT16 reserved[2];
28         UINT32 offset;
29 } __attribute__((packed));
30
31 /* we require at least BITMAPINFOHEADER, later versions are
32    accepted, but their features ignored */
33 struct bmp_dib {
34         UINT32 size;
35         UINT32 x;
36         UINT32 y;
37         UINT16 planes;
38         UINT16 depth;
39         UINT32 compression;
40         UINT32 image_size;
41         INT32 x_pixel_meter;
42         INT32 y_pixel_meter;
43         UINT32 colors_used;
44         UINT32 colors_important;
45 } __attribute__((packed));
46
47 struct bmp_map {
48         UINT8 blue;
49         UINT8 green;
50         UINT8 red;
51         UINT8 reserved;
52 } __attribute__((packed));
53
54 EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
55                             struct bmp_map **ret_map, UINT8 **pixmap) {
56         struct bmp_file *file;
57         struct bmp_dib *dib;
58         struct bmp_map *map;
59         UINTN row_size;
60
61         if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
62                 return EFI_INVALID_PARAMETER;
63
64         /* check file header */
65         file = (struct bmp_file *)bmp;
66         if (file->signature[0] != 'B' || file->signature[1] != 'M')
67                 return EFI_INVALID_PARAMETER;
68         if (file->size != size)
69                 return EFI_INVALID_PARAMETER;
70         if (file->size < file->offset)
71                 return EFI_INVALID_PARAMETER;
72
73         /*  check device-independent bitmap */
74         dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
75         if (dib->size < sizeof(struct bmp_dib))
76                 return EFI_UNSUPPORTED;
77
78         switch (dib->depth) {
79         case 1:
80         case 4:
81         case 8:
82         case 24:
83                 if (dib->compression != 0)
84                         return EFI_UNSUPPORTED;
85
86                 break;
87
88         case 16:
89         case 32:
90                 if (dib->compression != 0 && dib->compression != 3)
91                         return EFI_UNSUPPORTED;
92
93                 break;
94
95         default:
96                 return EFI_UNSUPPORTED;
97         }
98
99         row_size = (((dib->depth * dib->x) + 31) / 32) * 4;
100         if (file->size - file->offset <  dib->y * row_size)
101                 return EFI_INVALID_PARAMETER;
102         if (row_size * dib->y > 64 * 1024 * 1024)
103                 return EFI_INVALID_PARAMETER;
104
105         /* check color table */
106         map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
107         if (file->offset < sizeof(struct bmp_file) + dib->size)
108                 return EFI_INVALID_PARAMETER;
109
110         if (file->offset > sizeof(struct bmp_file) + dib->size) {
111                 UINT32 map_count;
112                 UINTN map_size;
113
114                 if (dib->colors_used)
115                         map_count = dib->colors_used;
116                 else {
117                         switch (dib->depth) {
118                         case 1:
119                         case 4:
120                         case 8:
121                                 map_count = 1 << dib->depth;
122                                 break;
123
124                         default:
125                                 map_count = 0;
126                                 break;
127                         }
128                 }
129
130                 map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
131                 if (map_size != sizeof(struct bmp_map) * map_count)
132                         return EFI_INVALID_PARAMETER;
133         }
134
135         *ret_map = map;
136         *ret_dib = dib;
137         *pixmap = bmp + file->offset;
138
139         return EFI_SUCCESS;
140 }
141
142 static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
143         UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
144
145         alpha = (source & 0xff);
146
147         /* convert src from RGBA to XRGB */
148         src = source >> 8;
149
150         /* decompose into RB and G components */
151         src_rb = (src & 0xff00ff);
152         src_g  = (src & 0x00ff00);
153
154         dst_rb = (*dst & 0xff00ff);
155         dst_g  = (*dst & 0x00ff00);
156
157         /* blend */
158         rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
159         g  = ((((src_g  -  dst_g) * alpha + 0x008000) >> 8) +  dst_g) & 0x00ff00;
160
161         *dst = (rb | g);
162 }
163
164 EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
165                       struct bmp_dib *dib, struct bmp_map *map,
166                       UINT8 *pixmap) {
167         UINT8 *in;
168         UINTN y;
169
170         /* transform and copy pixels */
171         in = pixmap;
172         for (y = 0; y < dib->y; y++) {
173                 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
174                 UINTN row_size;
175                 UINTN x;
176
177                 out = &buf[(dib->y - y - 1) * dib->x];
178                 for (x = 0; x < dib->x; x++, in++, out++) {
179                         switch (dib->depth) {
180                         case 1: {
181                                 UINTN i;
182
183                                 for (i = 0; i < 8 && x < dib->x; i++) {
184                                         out->Red = map[((*in) >> (7 - i)) & 1].red;
185                                         out->Green = map[((*in) >> (7 - i)) & 1].green;
186                                         out->Blue = map[((*in) >> (7 - i)) & 1].blue;
187                                         out++;
188                                         x++;
189                                 }
190                                 out--;
191                                 x--;
192                                 break;
193                         }
194
195                         case 4: {
196                                 UINTN i;
197
198                                 i = (*in) >> 4;
199                                 out->Red = map[i].red;
200                                 out->Green = map[i].green;
201                                 out->Blue = map[i].blue;
202                                 if (x < (dib->x - 1)) {
203                                         out++;
204                                         x++;
205                                         i = (*in) & 0x0f;
206                                         out->Red = map[i].red;
207                                         out->Green = map[i].green;
208                                         out->Blue = map[i].blue;
209                                 }
210                                 break;
211                         }
212
213                         case 8:
214                                 out->Red = map[*in].red;
215                                 out->Green = map[*in].green;
216                                 out->Blue = map[*in].blue;
217                                 break;
218
219                         case 16: {
220                                 UINT16 i = *(UINT16 *) in;
221
222                                 out->Red = (i & 0x7c00) >> 7;
223                                 out->Green = (i & 0x3e0) >> 2;
224                                 out->Blue = (i & 0x1f) << 3;
225                                 in += 1;
226                                 break;
227                         }
228
229                         case 24:
230                                 out->Red = in[2];
231                                 out->Green = in[1];
232                                 out->Blue = in[0];
233                                 in += 2;
234                                 break;
235
236                         case 32: {
237                                 UINT32 i = *(UINT32 *) in;
238
239                                 pixel_blend((UINT32 *)out, i);
240
241                                 in += 3;
242                                 break;
243                         }
244                         }
245                 }
246
247                 /* add row padding; new lines always start at 32 bit boundary */
248                 row_size = in - pixmap;
249                 in += ((row_size + 3) & ~3) - row_size;
250         }
251
252         return EFI_SUCCESS;
253 }
254
255 EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
256         EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {};
257         EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
258         EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
259         struct bmp_dib *dib;
260         struct bmp_map *map;
261         UINT8 *pixmap;
262         UINT64 blt_size;
263         VOID *blt = NULL;
264         UINTN x_pos = 0;
265         UINTN y_pos = 0;
266         EFI_STATUS err;
267
268         if (!background) {
269                 if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
270                         pixel.Red = 0xc0;
271                         pixel.Green = 0xc0;
272                         pixel.Blue = 0xc0;
273                 }
274                 background = &pixel;
275         }
276
277         err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
278         if (EFI_ERROR(err))
279                 return err;
280
281         err = bmp_parse_header(content, len, &dib, &map, &pixmap);
282         if (EFI_ERROR(err))
283                 goto err;
284
285         if(dib->x < GraphicsOutput->Mode->Info->HorizontalResolution)
286                 x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2;
287         if(dib->y < GraphicsOutput->Mode->Info->VerticalResolution)
288                 y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2;
289
290         uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
291                           (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background,
292                           EfiBltVideoFill, 0, 0, 0, 0,
293                           GraphicsOutput->Mode->Info->HorizontalResolution,
294                           GraphicsOutput->Mode->Info->VerticalResolution, 0);
295
296         /* EFI buffer */
297         blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
298         blt = AllocatePool(blt_size);
299         if (!blt)
300                 return EFI_OUT_OF_RESOURCES;
301
302         err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
303                                 blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
304                                 dib->x, dib->y, 0);
305         if (EFI_ERROR(err))
306                 goto err;
307
308         err = bmp_to_blt(blt, dib, map, pixmap);
309         if (EFI_ERROR(err))
310                 goto err;
311
312         err = graphics_mode(TRUE);
313         if (EFI_ERROR(err))
314                 goto err;
315
316         err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
317                                 blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos,
318                                 dib->x, dib->y, 0);
319 err:
320         FreePool(blt);
321         return err;
322 }