3 * Image handling functions
5 * Copyright (c) 2006 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
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.
38 #include "../refind/global.h"
39 #include "../refind/lib.h"
40 #include "refit_call_wrapper.h"
42 #define MAX_FILE_SIZE (1024*1024*1024)
45 // Basic image handling
48 EG_IMAGE
* egCreateImage(IN UINTN Width
, IN UINTN Height
, IN BOOLEAN HasAlpha
)
52 NewImage
= (EG_IMAGE
*) AllocatePool(sizeof(EG_IMAGE
));
55 NewImage
->PixelData
= (EG_PIXEL
*) AllocatePool(Width
* Height
* sizeof(EG_PIXEL
));
56 if (NewImage
->PixelData
== NULL
) {
61 NewImage
->Width
= Width
;
62 NewImage
->Height
= Height
;
63 NewImage
->HasAlpha
= HasAlpha
;
67 EG_IMAGE
* egCreateFilledImage(IN UINTN Width
, IN UINTN Height
, IN BOOLEAN HasAlpha
, IN EG_PIXEL
*Color
)
71 NewImage
= egCreateImage(Width
, Height
, HasAlpha
);
75 egFillImage(NewImage
, Color
);
79 EG_IMAGE
* egCopyImage(IN EG_IMAGE
*Image
)
83 NewImage
= egCreateImage(Image
->Width
, Image
->Height
, Image
->HasAlpha
);
87 CopyMem(NewImage
->PixelData
, Image
->PixelData
, Image
->Width
* Image
->Height
* sizeof(EG_PIXEL
));
91 VOID
egFreeImage(IN EG_IMAGE
*Image
)
94 if (Image
->PixelData
!= NULL
)
95 FreePool(Image
->PixelData
);
101 // Basic file operations
104 EFI_STATUS
egLoadFile(IN EFI_FILE
* BaseDir
, IN CHAR16
*FileName
,
105 OUT UINT8
**FileData
, OUT UINTN
*FileDataLength
)
108 EFI_FILE_HANDLE FileHandle
;
109 EFI_FILE_INFO
*FileInfo
;
114 Status
= refit_call5_wrapper(BaseDir
->Open
, BaseDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
115 if (EFI_ERROR(Status
)) {
119 FileInfo
= LibFileInfo(FileHandle
);
120 if (FileInfo
== NULL
) {
121 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
122 return EFI_NOT_FOUND
;
124 ReadSize
= FileInfo
->FileSize
;
125 if (ReadSize
> MAX_FILE_SIZE
)
126 ReadSize
= MAX_FILE_SIZE
;
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
;
136 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &BufferSize
, Buffer
);
137 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
138 if (EFI_ERROR(Status
)) {
144 *FileDataLength
= BufferSize
;
148 static EFI_GUID ESPGuid
= { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
150 static EFI_STATUS
egFindESP(OUT EFI_FILE_HANDLE
*RootDir
)
153 UINTN HandleCount
= 0;
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
;
166 EFI_STATUS
egSaveFile(IN EFI_FILE
* BaseDir OPTIONAL
, IN CHAR16
*FileName
,
167 IN UINT8
*FileData
, IN UINTN FileDataLength
)
170 EFI_FILE_HANDLE FileHandle
;
173 if (BaseDir
== NULL
) {
174 Status
= egFindESP(&BaseDir
);
175 if (EFI_ERROR(Status
))
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
))
184 BufferSize
= FileDataLength
;
185 Status
= refit_call3_wrapper(FileHandle
->Write
, FileHandle
, &BufferSize
, FileData
);
186 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
192 // Loading images from files and embedded data
195 static CHAR16
* egFindExtension(IN CHAR16
*FileName
)
199 for (i
= StrLen(FileName
); i
>= 0; i
--) {
200 if (FileName
[i
] == '.')
201 return FileName
+ i
+ 1;
202 if (FileName
[i
] == '/' || FileName
[i
] == '\\')
205 return FileName
+ StrLen(FileName
);
208 static EG_IMAGE
* egDecodeAny(IN UINT8
*FileData
, IN UINTN FileDataLength
,
209 IN CHAR16
*Format
, IN UINTN IconSize
, IN BOOLEAN WantAlpha
)
211 EG_IMAGE
*NewImage
= NULL
;
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
);
227 EG_IMAGE
* egLoadImage(IN EFI_FILE
* BaseDir
, IN CHAR16
*FileName
, IN BOOLEAN WantAlpha
)
231 UINTN FileDataLength
;
234 if (BaseDir
== NULL
|| FileName
== NULL
)
238 Status
= egLoadFile(BaseDir
, FileName
, &FileData
, &FileDataLength
);
239 if (EFI_ERROR(Status
))
243 NewImage
= egDecodeAny(FileData
, FileDataLength
, egFindExtension(FileName
), 128, WantAlpha
);
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
)
260 UINTN FileDataLength
;
261 CHAR16
*FileName
, FileName2
[256];
264 if (BaseDir
== NULL
|| Path
== NULL
)
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
))
278 NewImage
= egDecodeAny(FileData
, FileDataLength
, egFindExtension(Path
), IconSize
, TRUE
);
282 } // EG_IMAGE *egLoadIcon()
284 EG_IMAGE
* egDecodeImage(IN UINT8
*FileData
, IN UINTN FileDataLength
, IN CHAR16
*Format
, IN BOOLEAN WantAlpha
)
286 return egDecodeAny(FileData
, FileDataLength
, Format
, 128, WantAlpha
);
289 EG_IMAGE
* egPrepareEmbeddedImage(IN EG_EMBEDDED_IMAGE
*EmbeddedImage
, IN BOOLEAN WantAlpha
)
297 if (EmbeddedImage
->PixelMode
> EG_MAX_EIPIXELMODE
||
298 (EmbeddedImage
->CompressMode
!= EG_EICOMPMODE_NONE
&& EmbeddedImage
->CompressMode
!= EG_EICOMPMODE_RLE
))
301 // allocate image structure and pixel buffer
302 NewImage
= egCreateImage(EmbeddedImage
->Width
, EmbeddedImage
->Height
, WantAlpha
);
303 if (NewImage
== NULL
)
306 CompData
= (UINT8
*)EmbeddedImage
->Data
; // drop const
307 CompLen
= EmbeddedImage
->DataLength
;
308 PixelCount
= EmbeddedImage
->Width
* EmbeddedImage
->Height
;
310 // FUTURE: for EG_EICOMPMODE_EFICOMPRESS, decompress whole data block here
312 if (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY
||
313 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY_ALPHA
) {
315 // copy grayscale plane and expand
316 if (EmbeddedImage
->CompressMode
== EG_EICOMPMODE_RLE
) {
317 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, r
), PixelCount
);
319 egInsertPlane(CompData
, PLPTR(NewImage
, r
), PixelCount
);
320 CompData
+= PixelCount
;
322 egCopyPlane(PLPTR(NewImage
, r
), PLPTR(NewImage
, g
), PixelCount
);
323 egCopyPlane(PLPTR(NewImage
, r
), PLPTR(NewImage
, b
), PixelCount
);
325 } else if (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR
||
326 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR_ALPHA
) {
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
);
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
;
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
);
351 if (WantAlpha
&& (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY_ALPHA
||
352 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR_ALPHA
||
353 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_ALPHA
)) {
356 if (EmbeddedImage
->CompressMode
== EG_EICOMPMODE_RLE
) {
357 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, a
), PixelCount
);
359 egInsertPlane(CompData
, PLPTR(NewImage
, a
), PixelCount
);
360 CompData
+= PixelCount
;
364 egSetPlane(PLPTR(NewImage
, a
), WantAlpha
? 255 : 0, PixelCount
);
374 VOID
egRestrictImageArea(IN EG_IMAGE
*Image
,
375 IN UINTN AreaPosX
, IN UINTN AreaPosY
,
376 IN OUT UINTN
*AreaWidth
, IN OUT UINTN
*AreaHeight
)
378 if (AreaPosX
>= Image
->Width
|| AreaPosY
>= Image
->Height
) {
379 // out of bounds, operation has no effect
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
;
391 VOID
egFillImage(IN OUT EG_IMAGE
*CompImage
, IN EG_PIXEL
*Color
)
398 if (!CompImage
->HasAlpha
)
401 PixelPtr
= CompImage
->PixelData
;
402 for (i
= 0; i
< CompImage
->Width
* CompImage
->Height
; i
++, PixelPtr
++)
403 *PixelPtr
= FillColor
;
406 VOID
egFillImageArea(IN OUT EG_IMAGE
*CompImage
,
407 IN UINTN AreaPosX
, IN UINTN AreaPosY
,
408 IN UINTN AreaWidth
, IN UINTN AreaHeight
,
414 EG_PIXEL
*PixelBasePtr
;
416 egRestrictImageArea(CompImage
, AreaPosX
, AreaPosY
, &AreaWidth
, &AreaHeight
);
420 if (!CompImage
->HasAlpha
)
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
;
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
)
438 EG_PIXEL
*TopPtr
, *CompPtr
;
440 for (y
= 0; y
< Height
; y
++) {
442 CompPtr
= CompBasePtr
;
443 for (x
= 0; x
< Width
; x
++) {
447 TopBasePtr
+= TopLineOffset
;
448 CompBasePtr
+= CompLineOffset
;
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
)
457 EG_PIXEL
*TopPtr
, *CompPtr
;
462 for (y
= 0; y
< Height
; y
++) {
464 CompPtr
= CompBasePtr
;
465 for (x
= 0; x
< Width
; x
++) {
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;
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;
481 TopBasePtr
+= TopLineOffset
;
482 CompBasePtr
+= CompLineOffset
;
486 VOID
egComposeImage(IN OUT EG_IMAGE
*CompImage
, IN EG_IMAGE
*TopImage
, IN UINTN PosX
, IN UINTN PosY
)
488 UINTN CompWidth
, CompHeight
;
490 CompWidth
= TopImage
->Width
;
491 CompHeight
= TopImage
->Height
;
492 egRestrictImageArea(CompImage
, PosX
, PosY
, &CompWidth
, &CompHeight
);
496 if (CompImage
->HasAlpha
) {
497 CompImage
->HasAlpha
= FALSE
;
498 egSetPlane(PLPTR(CompImage
, a
), 0, CompImage
->Width
* CompImage
->Height
);
501 if (TopImage
->HasAlpha
)
502 egRawCompose(CompImage
->PixelData
+ PosY
* CompImage
->Width
+ PosX
, TopImage
->PixelData
,
503 CompWidth
, CompHeight
, CompImage
->Width
, TopImage
->Width
);
505 egRawCopy(CompImage
->PixelData
+ PosY
* CompImage
->Width
+ PosX
, TopImage
->PixelData
,
506 CompWidth
, CompHeight
, CompImage
->Width
, TopImage
->Width
);
510 EG_IMAGE
* egEnsureImageSize(IN EG_IMAGE
*Image
, IN UINTN Width
, IN UINTN Height
, IN EG_PIXEL
*Color
)
516 if (Image
->Width
== Width
&& Image
->Height
== Height
)
519 NewImage
= egCreateFilledImage(Width
, Height
, Image
->HasAlpha
, Color
);
520 if (NewImage
== NULL
) {
524 egComposeImage(NewImage
, Image
, 0, 0);
531 // misc internal functions
534 VOID
egInsertPlane(IN UINT8
*SrcDataPtr
, IN UINT8
*DestPlanePtr
, IN UINTN PixelCount
)
538 for (i
= 0; i
< PixelCount
; i
++) {
539 *DestPlanePtr
= *SrcDataPtr
++;
544 VOID
egSetPlane(IN UINT8
*DestPlanePtr
, IN UINT8 Value
, IN UINTN PixelCount
)
548 for (i
= 0; i
< PixelCount
; i
++) {
549 *DestPlanePtr
= Value
;
554 VOID
egCopyPlane(IN UINT8
*SrcPlanePtr
, IN UINT8
*DestPlanePtr
, IN UINTN PixelCount
)
558 for (i
= 0; i
< PixelCount
; i
++) {
559 *DestPlanePtr
= *SrcPlanePtr
;
560 DestPlanePtr
+= 4, SrcPlanePtr
+= 4;