]> code.delx.au - refind/blob - libeg/image.c
TianoCore build support; new use_graphics_for refind.conf token
[refind] / libeg / image.c
1 /*
2 * libeg/image.c
3 * Image handling functions
4 *
5 * Copyright (c) 2006 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "libegint.h"
38 #include "../refind/global.h"
39 #include "../refind/lib.h"
40 #include "../refind/screen.h"
41 #include "../include/refit_call_wrapper.h"
42
43 #define MAX_FILE_SIZE (1024*1024*1024)
44
45 #ifndef __MAKEWITH_GNUEFI
46 #define LibLocateHandle gBS->LocateHandleBuffer
47 #define LibOpenRoot EfiLibOpenRoot
48 #endif
49
50 //
51 // Basic image handling
52 //
53
54 EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
55 {
56 EG_IMAGE *NewImage;
57
58 NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
59 if (NewImage == NULL)
60 return NULL;
61 NewImage->PixelData = (EG_PIXEL *) AllocatePool(Width * Height * sizeof(EG_PIXEL));
62 if (NewImage->PixelData == NULL) {
63 FreePool(NewImage);
64 return NULL;
65 }
66
67 NewImage->Width = Width;
68 NewImage->Height = Height;
69 NewImage->HasAlpha = HasAlpha;
70 return NewImage;
71 }
72
73 EG_IMAGE * egCreateFilledImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha, IN EG_PIXEL *Color)
74 {
75 EG_IMAGE *NewImage;
76
77 NewImage = egCreateImage(Width, Height, HasAlpha);
78 if (NewImage == NULL)
79 return NULL;
80
81 egFillImage(NewImage, Color);
82 return NewImage;
83 }
84
85 EG_IMAGE * egCopyImage(IN EG_IMAGE *Image)
86 {
87 EG_IMAGE *NewImage;
88
89 NewImage = egCreateImage(Image->Width, Image->Height, Image->HasAlpha);
90 if (NewImage == NULL)
91 return NULL;
92
93 CopyMem(NewImage->PixelData, Image->PixelData, Image->Width * Image->Height * sizeof(EG_PIXEL));
94 return NewImage;
95 }
96
97 VOID egFreeImage(IN EG_IMAGE *Image)
98 {
99 if (Image != NULL) {
100 if (Image->PixelData != NULL)
101 FreePool(Image->PixelData);
102 FreePool(Image);
103 }
104 }
105
106 //
107 // Basic file operations
108 //
109
110 EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName,
111 OUT UINT8 **FileData, OUT UINTN *FileDataLength)
112 {
113 EFI_STATUS Status;
114 EFI_FILE_HANDLE FileHandle;
115 EFI_FILE_INFO *FileInfo;
116 UINT64 ReadSize;
117 UINTN BufferSize;
118 UINT8 *Buffer;
119
120 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
121 if (EFI_ERROR(Status)) {
122 return Status;
123 }
124
125 FileInfo = LibFileInfo(FileHandle);
126 if (FileInfo == NULL) {
127 refit_call1_wrapper(FileHandle->Close, FileHandle);
128 return EFI_NOT_FOUND;
129 }
130 ReadSize = FileInfo->FileSize;
131 if (ReadSize > MAX_FILE_SIZE)
132 ReadSize = MAX_FILE_SIZE;
133 FreePool(FileInfo);
134
135 BufferSize = (UINTN)ReadSize; // was limited to 1 GB above, so this is safe
136 Buffer = (UINT8 *) AllocatePool(BufferSize);
137 if (Buffer == NULL) {
138 refit_call1_wrapper(FileHandle->Close, FileHandle);
139 return EFI_OUT_OF_RESOURCES;
140 }
141
142 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &BufferSize, Buffer);
143 refit_call1_wrapper(FileHandle->Close, FileHandle);
144 if (EFI_ERROR(Status)) {
145 FreePool(Buffer);
146 return Status;
147 }
148
149 *FileData = Buffer;
150 *FileDataLength = BufferSize;
151 return EFI_SUCCESS;
152 }
153
154 static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
155
156 static EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir)
157 {
158 EFI_STATUS Status;
159 UINTN HandleCount = 0;
160 EFI_HANDLE *Handles;
161
162 Status = LibLocateHandle(ByProtocol, &ESPGuid, NULL, &HandleCount, &Handles);
163 if (!EFI_ERROR(Status) && HandleCount > 0) {
164 *RootDir = LibOpenRoot(Handles[0]);
165 if (*RootDir == NULL)
166 Status = EFI_NOT_FOUND;
167 FreePool(Handles);
168 }
169 return Status;
170 }
171
172 EFI_STATUS egSaveFile(IN EFI_FILE* BaseDir OPTIONAL, IN CHAR16 *FileName,
173 IN UINT8 *FileData, IN UINTN FileDataLength)
174 {
175 EFI_STATUS Status;
176 EFI_FILE_HANDLE FileHandle;
177 UINTN BufferSize;
178
179 if (BaseDir == NULL) {
180 Status = egFindESP(&BaseDir);
181 if (EFI_ERROR(Status))
182 return Status;
183 }
184
185 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName,
186 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
187 if (EFI_ERROR(Status))
188 return Status;
189
190 BufferSize = FileDataLength;
191 Status = refit_call3_wrapper(FileHandle->Write, FileHandle, &BufferSize, FileData);
192 refit_call1_wrapper(FileHandle->Close, FileHandle);
193
194 return Status;
195 }
196
197 //
198 // Loading images from files and embedded data
199 //
200
201 static CHAR16 * egFindExtension(IN CHAR16 *FileName)
202 {
203 UINTN i;
204
205 for (i = StrLen(FileName); i >= 0; i--) {
206 if (FileName[i] == '.')
207 return FileName + i + 1;
208 if (FileName[i] == '/' || FileName[i] == '\\')
209 break;
210 }
211 return FileName + StrLen(FileName);
212 }
213
214 static EG_IMAGE * egDecodeAny(IN UINT8 *FileData, IN UINTN FileDataLength,
215 IN CHAR16 *Format, IN UINTN IconSize, IN BOOLEAN WantAlpha)
216 {
217 EG_IMAGE *NewImage = NULL;
218
219 // Note: The UEFI implementation in Gigabyte's Hybrid EFI is buggy and does
220 // a case-sensitive comparison in StriCmp rather than the case-insensitive
221 // comparison that the spec says should be done. As a workaround, we repeat
222 // the comparison twice here.
223 // dispatch by extension
224 if ((StriCmp(Format, L"BMP") == 0) || (StriCmp(Format, L"bmp") == 0)) {
225 NewImage = egDecodeBMP(FileData, FileDataLength, IconSize, WantAlpha);
226 } else if ((StriCmp(Format, L"ICNS") == 0) || (StriCmp(Format, L"icns") == 0)) {
227 NewImage = egDecodeICNS(FileData, FileDataLength, IconSize, WantAlpha);
228 } // if/else
229
230 return NewImage;
231 }
232
233 EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN WantAlpha)
234 {
235 EFI_STATUS Status;
236 UINT8 *FileData;
237 UINTN FileDataLength;
238 EG_IMAGE *NewImage;
239
240 if (BaseDir == NULL || FileName == NULL)
241 return NULL;
242
243 // load file
244 Status = egLoadFile(BaseDir, FileName, &FileData, &FileDataLength);
245 if (EFI_ERROR(Status))
246 return NULL;
247
248 // decode it
249 NewImage = egDecodeAny(FileData, FileDataLength, egFindExtension(FileName), 128, WantAlpha);
250 FreePool(FileData);
251
252 return NewImage;
253 }
254
255 // Load an icon from (BaseDir)/Path, extracting the icon of size IconSize x IconSize.
256 // If the initial attempt is unsuccessful, try again, replacing the directory
257 // component of Path with DEFAULT_ICONS_DIR.
258 // Note: The assumption is that BaseDir points to rEFInd's home directory and Path
259 // includes one subdirectory level. If this changes in future revisions, it may be
260 // necessary to alter the code that tries again with DEFAULT_ICONS_DIR.
261 // Returns a pointer to the image data, or NULL if the icon could not be loaded.
262 EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize)
263 {
264 EFI_STATUS Status;
265 UINT8 *FileData;
266 UINTN FileDataLength;
267 CHAR16 *FileName, FileName2[256];
268 EG_IMAGE *NewImage;
269
270 if (BaseDir == NULL || Path == NULL)
271 return NULL;
272
273 // load file
274 Status = egLoadFile(BaseDir, Path, &FileData, &FileDataLength);
275 if (EFI_ERROR(Status)) {
276 FileName = Basename(Path); // Note: FileName is a pointer within Path; DON'T FREE IT!
277 SPrint(FileName2, 255, L"%s\\%s", DEFAULT_ICONS_DIR, FileName);
278 Status = egLoadFile(BaseDir, FileName2, &FileData, &FileDataLength);
279 if (EFI_ERROR(Status))
280 return NULL;
281 }
282
283 // decode it
284 NewImage = egDecodeAny(FileData, FileDataLength, egFindExtension(Path), IconSize, TRUE);
285 FreePool(FileData);
286
287 return NewImage;
288 } // EG_IMAGE *egLoadIcon()
289
290 EG_IMAGE * egDecodeImage(IN UINT8 *FileData, IN UINTN FileDataLength, IN CHAR16 *Format, IN BOOLEAN WantAlpha)
291 {
292 return egDecodeAny(FileData, FileDataLength, Format, 128, WantAlpha);
293 }
294
295 EG_IMAGE * egPrepareEmbeddedImage(IN EG_EMBEDDED_IMAGE *EmbeddedImage, IN BOOLEAN WantAlpha)
296 {
297 EG_IMAGE *NewImage;
298 UINT8 *CompData;
299 UINTN CompLen;
300 UINTN PixelCount;
301
302 // sanity check
303 if (EmbeddedImage->PixelMode > EG_MAX_EIPIXELMODE ||
304 (EmbeddedImage->CompressMode != EG_EICOMPMODE_NONE && EmbeddedImage->CompressMode != EG_EICOMPMODE_RLE))
305 return NULL;
306
307 // allocate image structure and pixel buffer
308 NewImage = egCreateImage(EmbeddedImage->Width, EmbeddedImage->Height, WantAlpha);
309 if (NewImage == NULL)
310 return NULL;
311
312 CompData = (UINT8 *)EmbeddedImage->Data; // drop const
313 CompLen = EmbeddedImage->DataLength;
314 PixelCount = EmbeddedImage->Width * EmbeddedImage->Height;
315
316 // FUTURE: for EG_EICOMPMODE_EFICOMPRESS, decompress whole data block here
317
318 if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY ||
319 EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA) {
320
321 // copy grayscale plane and expand
322 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
323 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
324 } else {
325 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
326 CompData += PixelCount;
327 }
328 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, g), PixelCount);
329 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, b), PixelCount);
330
331 } else if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR ||
332 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA) {
333
334 // copy color planes
335 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
336 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
337 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, g), PixelCount);
338 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, b), PixelCount);
339 } else {
340 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
341 CompData += PixelCount;
342 egInsertPlane(CompData, PLPTR(NewImage, g), PixelCount);
343 CompData += PixelCount;
344 egInsertPlane(CompData, PLPTR(NewImage, b), PixelCount);
345 CompData += PixelCount;
346 }
347
348 } else {
349
350 // set color planes to black
351 egSetPlane(PLPTR(NewImage, r), 0, PixelCount);
352 egSetPlane(PLPTR(NewImage, g), 0, PixelCount);
353 egSetPlane(PLPTR(NewImage, b), 0, PixelCount);
354
355 }
356
357 if (WantAlpha && (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA ||
358 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA ||
359 EmbeddedImage->PixelMode == EG_EIPIXELMODE_ALPHA)) {
360
361 // copy alpha plane
362 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
363 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, a), PixelCount);
364 } else {
365 egInsertPlane(CompData, PLPTR(NewImage, a), PixelCount);
366 CompData += PixelCount;
367 }
368
369 } else {
370 egSetPlane(PLPTR(NewImage, a), WantAlpha ? 255 : 0, PixelCount);
371 }
372
373 return NewImage;
374 }
375
376 //
377 // Compositing
378 //
379
380 VOID egRestrictImageArea(IN EG_IMAGE *Image,
381 IN UINTN AreaPosX, IN UINTN AreaPosY,
382 IN OUT UINTN *AreaWidth, IN OUT UINTN *AreaHeight)
383 {
384 if (AreaPosX >= Image->Width || AreaPosY >= Image->Height) {
385 // out of bounds, operation has no effect
386 *AreaWidth = 0;
387 *AreaHeight = 0;
388 } else {
389 // calculate affected area
390 if (*AreaWidth > Image->Width - AreaPosX)
391 *AreaWidth = Image->Width - AreaPosX;
392 if (*AreaHeight > Image->Height - AreaPosY)
393 *AreaHeight = Image->Height - AreaPosY;
394 }
395 }
396
397 VOID egFillImage(IN OUT EG_IMAGE *CompImage, IN EG_PIXEL *Color)
398 {
399 UINTN i;
400 EG_PIXEL FillColor;
401 EG_PIXEL *PixelPtr;
402
403 FillColor = *Color;
404 if (!CompImage->HasAlpha)
405 FillColor.a = 0;
406
407 PixelPtr = CompImage->PixelData;
408 for (i = 0; i < CompImage->Width * CompImage->Height; i++, PixelPtr++)
409 *PixelPtr = FillColor;
410 }
411
412 VOID egFillImageArea(IN OUT EG_IMAGE *CompImage,
413 IN UINTN AreaPosX, IN UINTN AreaPosY,
414 IN UINTN AreaWidth, IN UINTN AreaHeight,
415 IN EG_PIXEL *Color)
416 {
417 UINTN x, y;
418 EG_PIXEL FillColor;
419 EG_PIXEL *PixelPtr;
420 EG_PIXEL *PixelBasePtr;
421
422 egRestrictImageArea(CompImage, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight);
423
424 if (AreaWidth > 0) {
425 FillColor = *Color;
426 if (!CompImage->HasAlpha)
427 FillColor.a = 0;
428
429 PixelBasePtr = CompImage->PixelData + AreaPosY * CompImage->Width + AreaPosX;
430 for (y = 0; y < AreaHeight; y++) {
431 PixelPtr = PixelBasePtr;
432 for (x = 0; x < AreaWidth; x++, PixelPtr++)
433 *PixelPtr = FillColor;
434 PixelBasePtr += CompImage->Width;
435 }
436 }
437 }
438
439 VOID egRawCopy(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
440 IN UINTN Width, IN UINTN Height,
441 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
442 {
443 UINTN x, y;
444 EG_PIXEL *TopPtr, *CompPtr;
445
446 for (y = 0; y < Height; y++) {
447 TopPtr = TopBasePtr;
448 CompPtr = CompBasePtr;
449 for (x = 0; x < Width; x++) {
450 *CompPtr = *TopPtr;
451 TopPtr++, CompPtr++;
452 }
453 TopBasePtr += TopLineOffset;
454 CompBasePtr += CompLineOffset;
455 }
456 }
457
458 VOID egRawCompose(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
459 IN UINTN Width, IN UINTN Height,
460 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
461 {
462 UINTN x, y;
463 EG_PIXEL *TopPtr, *CompPtr;
464 UINTN Alpha;
465 UINTN RevAlpha;
466 UINTN Temp;
467
468 for (y = 0; y < Height; y++) {
469 TopPtr = TopBasePtr;
470 CompPtr = CompBasePtr;
471 for (x = 0; x < Width; x++) {
472 Alpha = TopPtr->a;
473 RevAlpha = 255 - Alpha;
474 Temp = (UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha + 0x80;
475 CompPtr->b = (Temp + (Temp >> 8)) >> 8;
476 Temp = (UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha + 0x80;
477 CompPtr->g = (Temp + (Temp >> 8)) >> 8;
478 Temp = (UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha + 0x80;
479 CompPtr->r = (Temp + (Temp >> 8)) >> 8;
480 /*
481 CompPtr->b = ((UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha) / 255;
482 CompPtr->g = ((UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha) / 255;
483 CompPtr->r = ((UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha) / 255;
484 */
485 TopPtr++, CompPtr++;
486 }
487 TopBasePtr += TopLineOffset;
488 CompBasePtr += CompLineOffset;
489 }
490 }
491
492 VOID egComposeImage(IN OUT EG_IMAGE *CompImage, IN EG_IMAGE *TopImage, IN UINTN PosX, IN UINTN PosY)
493 {
494 UINTN CompWidth, CompHeight;
495
496 CompWidth = TopImage->Width;
497 CompHeight = TopImage->Height;
498 egRestrictImageArea(CompImage, PosX, PosY, &CompWidth, &CompHeight);
499
500 // compose
501 if (CompWidth > 0) {
502 if (CompImage->HasAlpha) {
503 CompImage->HasAlpha = FALSE;
504 egSetPlane(PLPTR(CompImage, a), 0, CompImage->Width * CompImage->Height);
505 }
506
507 if (TopImage->HasAlpha)
508 egRawCompose(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
509 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
510 else
511 egRawCopy(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
512 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
513 }
514 }
515
516 EG_IMAGE * egEnsureImageSize(IN EG_IMAGE *Image, IN UINTN Width, IN UINTN Height, IN EG_PIXEL *Color)
517 {
518 EG_IMAGE *NewImage;
519
520 if (Image == NULL)
521 return NULL;
522 if (Image->Width == Width && Image->Height == Height)
523 return Image;
524
525 NewImage = egCreateFilledImage(Width, Height, Image->HasAlpha, Color);
526 if (NewImage == NULL) {
527 egFreeImage(Image);
528 return NULL;
529 }
530 egComposeImage(NewImage, Image, 0, 0);
531 egFreeImage(Image);
532
533 return NewImage;
534 }
535
536 //
537 // misc internal functions
538 //
539
540 VOID egInsertPlane(IN UINT8 *SrcDataPtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
541 {
542 UINTN i;
543
544 for (i = 0; i < PixelCount; i++) {
545 *DestPlanePtr = *SrcDataPtr++;
546 DestPlanePtr += 4;
547 }
548 }
549
550 VOID egSetPlane(IN UINT8 *DestPlanePtr, IN UINT8 Value, IN UINTN PixelCount)
551 {
552 UINTN i;
553
554 for (i = 0; i < PixelCount; i++) {
555 *DestPlanePtr = Value;
556 DestPlanePtr += 4;
557 }
558 }
559
560 VOID egCopyPlane(IN UINT8 *SrcPlanePtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
561 {
562 UINTN i;
563
564 for (i = 0; i < PixelCount; i++) {
565 *DestPlanePtr = *SrcPlanePtr;
566 DestPlanePtr += 4, SrcPlanePtr += 4;
567 }
568 }
569
570 /* EOF */