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