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