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 "../refind/screen.h"
41 #include "../include/refit_call_wrapper.h"
44 #define MAX_FILE_SIZE (1024*1024*1024)
46 #ifndef __MAKEWITH_GNUEFI
47 #define LibLocateHandle gBS->LocateHandleBuffer
48 #define LibOpenRoot EfiLibOpenRoot
52 // Basic image handling
55 EG_IMAGE
* egCreateImage(IN UINTN Width
, IN UINTN Height
, IN BOOLEAN HasAlpha
)
59 NewImage
= (EG_IMAGE
*) AllocatePool(sizeof(EG_IMAGE
));
62 NewImage
->PixelData
= (EG_PIXEL
*) AllocatePool(Width
* Height
* sizeof(EG_PIXEL
));
63 if (NewImage
->PixelData
== NULL
) {
68 NewImage
->Width
= Width
;
69 NewImage
->Height
= Height
;
70 NewImage
->HasAlpha
= HasAlpha
;
74 EG_IMAGE
* egCreateFilledImage(IN UINTN Width
, IN UINTN Height
, IN BOOLEAN HasAlpha
, IN EG_PIXEL
*Color
)
78 NewImage
= egCreateImage(Width
, Height
, HasAlpha
);
82 egFillImage(NewImage
, Color
);
86 EG_IMAGE
* egCopyImage(IN EG_IMAGE
*Image
)
90 NewImage
= egCreateImage(Image
->Width
, Image
->Height
, Image
->HasAlpha
);
94 CopyMem(NewImage
->PixelData
, Image
->PixelData
, Image
->Width
* Image
->Height
* sizeof(EG_PIXEL
));
98 // Returns a smaller image composed of the specified crop area from the larger area.
99 // If the specified area is larger than is in the original, returns NULL.
100 EG_IMAGE
* egCropImage(IN EG_IMAGE
*Image
, IN UINTN StartX
, IN UINTN StartY
, IN UINTN Width
, IN UINTN Height
) {
101 EG_IMAGE
*NewImage
= NULL
;
104 if (((StartX
+ Width
) > Image
->Width
) || ((StartY
+ Height
) > Image
->Height
))
107 NewImage
= egCreateImage(Width
, Height
, Image
->HasAlpha
);
108 if (NewImage
== NULL
)
111 for (y
= 0; y
< Height
; y
++) {
112 for (x
= 0; x
< Width
; x
++) {
113 NewImage
->PixelData
[y
* NewImage
->Width
+ x
] = Image
->PixelData
[(y
+ StartY
) * Image
->Width
+ x
+ StartX
];
117 } // EG_IMAGE * egCropImage()
119 VOID
egFreeImage(IN EG_IMAGE
*Image
)
122 if (Image
->PixelData
!= NULL
)
123 FreePool(Image
->PixelData
);
129 // Basic file operations
132 EFI_STATUS
egLoadFile(IN EFI_FILE
* BaseDir
, IN CHAR16
*FileName
, OUT UINT8
**FileData
, OUT UINTN
*FileDataLength
)
135 EFI_FILE_HANDLE FileHandle
;
136 EFI_FILE_INFO
*FileInfo
;
141 Status
= refit_call5_wrapper(BaseDir
->Open
, BaseDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
142 if (EFI_ERROR(Status
)) {
146 FileInfo
= LibFileInfo(FileHandle
);
147 if (FileInfo
== NULL
) {
148 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
149 return EFI_NOT_FOUND
;
151 ReadSize
= FileInfo
->FileSize
;
152 if (ReadSize
> MAX_FILE_SIZE
)
153 ReadSize
= MAX_FILE_SIZE
;
156 BufferSize
= (UINTN
)ReadSize
; // was limited to 1 GB above, so this is safe
157 Buffer
= (UINT8
*) AllocatePool(BufferSize
);
158 if (Buffer
== NULL
) {
159 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
160 return EFI_OUT_OF_RESOURCES
;
163 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &BufferSize
, Buffer
);
164 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
165 if (EFI_ERROR(Status
)) {
171 *FileDataLength
= BufferSize
;
175 static EFI_GUID ESPGuid
= { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
177 static EFI_STATUS
egFindESP(OUT EFI_FILE_HANDLE
*RootDir
)
180 UINTN HandleCount
= 0;
183 Status
= LibLocateHandle(ByProtocol
, &ESPGuid
, NULL
, &HandleCount
, &Handles
);
184 if (!EFI_ERROR(Status
) && HandleCount
> 0) {
185 *RootDir
= LibOpenRoot(Handles
[0]);
186 if (*RootDir
== NULL
)
187 Status
= EFI_NOT_FOUND
;
193 EFI_STATUS
egSaveFile(IN EFI_FILE
* BaseDir OPTIONAL
, IN CHAR16
*FileName
,
194 IN UINT8
*FileData
, IN UINTN FileDataLength
)
197 EFI_FILE_HANDLE FileHandle
;
200 if (BaseDir
== NULL
) {
201 Status
= egFindESP(&BaseDir
);
202 if (EFI_ERROR(Status
))
206 Status
= refit_call5_wrapper(BaseDir
->Open
, BaseDir
, &FileHandle
, FileName
,
207 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
208 if (EFI_ERROR(Status
))
211 BufferSize
= FileDataLength
;
212 Status
= refit_call3_wrapper(FileHandle
->Write
, FileHandle
, &BufferSize
, FileData
);
213 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
219 // Loading images from files and embedded data
222 // Decode the specified image data. The IconSize parameter is relevant only
223 // for ICNS, for which it selects which ICNS sub-image is decoded.
224 // Returns a pointer to the resulting EG_IMAGE or NULL if decoding failed.
225 static EG_IMAGE
* egDecodeAny(IN UINT8
*FileData
, IN UINTN FileDataLength
, IN UINTN IconSize
, IN BOOLEAN WantAlpha
)
227 EG_IMAGE
*NewImage
= NULL
;
229 NewImage
= egDecodeICNS(FileData
, FileDataLength
, IconSize
, WantAlpha
);
230 if (NewImage
== NULL
)
231 NewImage
= egDecodePNG(FileData
, FileDataLength
, IconSize
, WantAlpha
);
232 if (NewImage
== NULL
)
233 NewImage
= egDecodeBMP(FileData
, FileDataLength
, IconSize
, WantAlpha
);
238 EG_IMAGE
* egLoadImage(IN EFI_FILE
* BaseDir
, IN CHAR16
*FileName
, IN BOOLEAN WantAlpha
)
242 UINTN FileDataLength
;
245 if (BaseDir
== NULL
|| FileName
== NULL
)
249 Status
= egLoadFile(BaseDir
, FileName
, &FileData
, &FileDataLength
);
250 if (EFI_ERROR(Status
))
254 NewImage
= egDecodeAny(FileData
, FileDataLength
, 128, WantAlpha
);
260 // Load an icon from (BaseDir)/Path, extracting the icon of size IconSize x IconSize.
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
)
266 UINTN FileDataLength
;
269 if (BaseDir
== NULL
|| Path
== NULL
)
273 Status
= egLoadFile(BaseDir
, Path
, &FileData
, &FileDataLength
);
274 if (EFI_ERROR(Status
))
278 NewImage
= egDecodeAny(FileData
, FileDataLength
, IconSize
, TRUE
);
280 if ((NewImage
->Width
!= IconSize
) || (NewImage
->Height
!= IconSize
)) {
281 Print(L
"Warning: Attempt to load icon of the wrong size from '%s'\n", Path
);
282 MyFreePool(NewImage
);
287 } // EG_IMAGE *egLoadIcon()
289 // Returns an icon with any extension in ICON_EXTENSIONS from either the directory
290 // specified by GlobalConfig.IconsDir or DEFAULT_ICONS_DIR. The input BaseName
291 // should be the icon name without an extension. For instance, if BaseName is
292 // os_linux, GlobalConfig.IconsDir is myicons, DEFAULT_ICONS_DIR is icons, and
293 // ICON_EXTENSIONS is "icns,png", this function will return myicons/os_linux.icns,
294 // myicons/os_linux.png, icons/os_linux.icns, or icons/os_linux.png, in that
295 // order of preference. Returns NULL if no such icon can be found. All file
296 // references are relative to SelfDir.
297 EG_IMAGE
* egFindIcon(IN CHAR16
*BaseName
, IN UINTN IconSize
) {
298 CHAR16
*LoadDir
, *Extension
;
299 CHAR16 FileName
[256];
301 EG_IMAGE
*Image
= NULL
;
303 if (GlobalConfig
.IconsDir
!= NULL
)
304 LoadDir
= GlobalConfig
.IconsDir
;
306 LoadDir
= StrDuplicate(DEFAULT_ICONS_DIR
);
308 while ((LoadDir
!= NULL
) && (Image
== NULL
)) {
310 while (((Extension
= FindCommaDelimited(ICON_EXTENSIONS
, i
++)) != NULL
) && (Image
== NULL
)) {
311 SPrint(FileName
, 255, L
"%s\\%s.%s", LoadDir
, BaseName
, Extension
);
312 Image
= egLoadIcon(SelfDir
, FileName
, IconSize
);
313 MyFreePool(Extension
);
315 if (LoadDir
== GlobalConfig
.IconsDir
) {
316 LoadDir
= StrDuplicate(DEFAULT_ICONS_DIR
);
323 } // EG_IMAGE * egFindIcon()
325 EG_IMAGE
* egPrepareEmbeddedImage(IN EG_EMBEDDED_IMAGE
*EmbeddedImage
, IN BOOLEAN WantAlpha
)
333 if (EmbeddedImage
->PixelMode
> EG_MAX_EIPIXELMODE
||
334 (EmbeddedImage
->CompressMode
!= EG_EICOMPMODE_NONE
&& EmbeddedImage
->CompressMode
!= EG_EICOMPMODE_RLE
))
337 // allocate image structure and pixel buffer
338 NewImage
= egCreateImage(EmbeddedImage
->Width
, EmbeddedImage
->Height
, WantAlpha
);
339 if (NewImage
== NULL
)
342 CompData
= (UINT8
*)EmbeddedImage
->Data
; // drop const
343 CompLen
= EmbeddedImage
->DataLength
;
344 PixelCount
= EmbeddedImage
->Width
* EmbeddedImage
->Height
;
346 // FUTURE: for EG_EICOMPMODE_EFICOMPRESS, decompress whole data block here
348 if (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY
||
349 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY_ALPHA
) {
351 // copy grayscale plane and expand
352 if (EmbeddedImage
->CompressMode
== EG_EICOMPMODE_RLE
) {
353 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, r
), PixelCount
);
355 egInsertPlane(CompData
, PLPTR(NewImage
, r
), PixelCount
);
356 CompData
+= PixelCount
;
358 egCopyPlane(PLPTR(NewImage
, r
), PLPTR(NewImage
, g
), PixelCount
);
359 egCopyPlane(PLPTR(NewImage
, r
), PLPTR(NewImage
, b
), PixelCount
);
361 } else if (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR
||
362 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR_ALPHA
) {
365 if (EmbeddedImage
->CompressMode
== EG_EICOMPMODE_RLE
) {
366 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, r
), PixelCount
);
367 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, g
), PixelCount
);
368 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, b
), PixelCount
);
370 egInsertPlane(CompData
, PLPTR(NewImage
, r
), PixelCount
);
371 CompData
+= PixelCount
;
372 egInsertPlane(CompData
, PLPTR(NewImage
, g
), PixelCount
);
373 CompData
+= PixelCount
;
374 egInsertPlane(CompData
, PLPTR(NewImage
, b
), PixelCount
);
375 CompData
+= PixelCount
;
380 // set color planes to black
381 egSetPlane(PLPTR(NewImage
, r
), 0, PixelCount
);
382 egSetPlane(PLPTR(NewImage
, g
), 0, PixelCount
);
383 egSetPlane(PLPTR(NewImage
, b
), 0, PixelCount
);
387 if (WantAlpha
&& (EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_GRAY_ALPHA
||
388 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_COLOR_ALPHA
||
389 EmbeddedImage
->PixelMode
== EG_EIPIXELMODE_ALPHA
)) {
392 if (EmbeddedImage
->CompressMode
== EG_EICOMPMODE_RLE
) {
393 egDecompressIcnsRLE(&CompData
, &CompLen
, PLPTR(NewImage
, a
), PixelCount
);
395 egInsertPlane(CompData
, PLPTR(NewImage
, a
), PixelCount
);
396 CompData
+= PixelCount
;
400 egSetPlane(PLPTR(NewImage
, a
), WantAlpha
? 255 : 0, PixelCount
);
410 VOID
egRestrictImageArea(IN EG_IMAGE
*Image
,
411 IN UINTN AreaPosX
, IN UINTN AreaPosY
,
412 IN OUT UINTN
*AreaWidth
, IN OUT UINTN
*AreaHeight
)
414 if (AreaPosX
>= Image
->Width
|| AreaPosY
>= Image
->Height
) {
415 // out of bounds, operation has no effect
419 // calculate affected area
420 if (*AreaWidth
> Image
->Width
- AreaPosX
)
421 *AreaWidth
= Image
->Width
- AreaPosX
;
422 if (*AreaHeight
> Image
->Height
- AreaPosY
)
423 *AreaHeight
= Image
->Height
- AreaPosY
;
427 VOID
egFillImage(IN OUT EG_IMAGE
*CompImage
, IN EG_PIXEL
*Color
)
434 if (!CompImage
->HasAlpha
)
437 PixelPtr
= CompImage
->PixelData
;
438 for (i
= 0; i
< CompImage
->Width
* CompImage
->Height
; i
++, PixelPtr
++)
439 *PixelPtr
= FillColor
;
442 VOID
egFillImageArea(IN OUT EG_IMAGE
*CompImage
,
443 IN UINTN AreaPosX
, IN UINTN AreaPosY
,
444 IN UINTN AreaWidth
, IN UINTN AreaHeight
,
450 EG_PIXEL
*PixelBasePtr
;
452 egRestrictImageArea(CompImage
, AreaPosX
, AreaPosY
, &AreaWidth
, &AreaHeight
);
456 if (!CompImage
->HasAlpha
)
459 PixelBasePtr
= CompImage
->PixelData
+ AreaPosY
* CompImage
->Width
+ AreaPosX
;
460 for (y
= 0; y
< AreaHeight
; y
++) {
461 PixelPtr
= PixelBasePtr
;
462 for (x
= 0; x
< AreaWidth
; x
++, PixelPtr
++)
463 *PixelPtr
= FillColor
;
464 PixelBasePtr
+= CompImage
->Width
;
469 VOID
egRawCopy(IN OUT EG_PIXEL
*CompBasePtr
, IN EG_PIXEL
*TopBasePtr
,
470 IN UINTN Width
, IN UINTN Height
,
471 IN UINTN CompLineOffset
, IN UINTN TopLineOffset
)
474 EG_PIXEL
*TopPtr
, *CompPtr
;
476 for (y
= 0; y
< Height
; y
++) {
478 CompPtr
= CompBasePtr
;
479 for (x
= 0; x
< Width
; x
++) {
483 TopBasePtr
+= TopLineOffset
;
484 CompBasePtr
+= CompLineOffset
;
488 VOID
egRawCompose(IN OUT EG_PIXEL
*CompBasePtr
, IN EG_PIXEL
*TopBasePtr
,
489 IN UINTN Width
, IN UINTN Height
,
490 IN UINTN CompLineOffset
, IN UINTN TopLineOffset
)
493 EG_PIXEL
*TopPtr
, *CompPtr
;
498 for (y
= 0; y
< Height
; y
++) {
500 CompPtr
= CompBasePtr
;
501 for (x
= 0; x
< Width
; x
++) {
503 RevAlpha
= 255 - Alpha
;
504 Temp
= (UINTN
)CompPtr
->b
* RevAlpha
+ (UINTN
)TopPtr
->b
* Alpha
+ 0x80;
505 CompPtr
->b
= (Temp
+ (Temp
>> 8)) >> 8;
506 Temp
= (UINTN
)CompPtr
->g
* RevAlpha
+ (UINTN
)TopPtr
->g
* Alpha
+ 0x80;
507 CompPtr
->g
= (Temp
+ (Temp
>> 8)) >> 8;
508 Temp
= (UINTN
)CompPtr
->r
* RevAlpha
+ (UINTN
)TopPtr
->r
* Alpha
+ 0x80;
509 CompPtr
->r
= (Temp
+ (Temp
>> 8)) >> 8;
511 CompPtr->b = ((UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha) / 255;
512 CompPtr->g = ((UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha) / 255;
513 CompPtr->r = ((UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha) / 255;
517 TopBasePtr
+= TopLineOffset
;
518 CompBasePtr
+= CompLineOffset
;
522 VOID
egComposeImage(IN OUT EG_IMAGE
*CompImage
, IN EG_IMAGE
*TopImage
, IN UINTN PosX
, IN UINTN PosY
)
524 UINTN CompWidth
, CompHeight
;
526 CompWidth
= TopImage
->Width
;
527 CompHeight
= TopImage
->Height
;
528 egRestrictImageArea(CompImage
, PosX
, PosY
, &CompWidth
, &CompHeight
);
532 if (TopImage
->HasAlpha
) {
533 egRawCompose(CompImage
->PixelData
+ PosY
* CompImage
->Width
+ PosX
, TopImage
->PixelData
,
534 CompWidth
, CompHeight
, CompImage
->Width
, TopImage
->Width
);
536 egRawCopy(CompImage
->PixelData
+ PosY
* CompImage
->Width
+ PosX
, TopImage
->PixelData
,
537 CompWidth
, CompHeight
, CompImage
->Width
, TopImage
->Width
);
540 } /* VOID egComposeImage() */
542 EG_IMAGE
* egEnsureImageSize(IN EG_IMAGE
*Image
, IN UINTN Width
, IN UINTN Height
, IN EG_PIXEL
*Color
)
548 if (Image
->Width
== Width
&& Image
->Height
== Height
)
551 NewImage
= egCreateFilledImage(Width
, Height
, Image
->HasAlpha
, Color
);
552 if (NewImage
== NULL
) {
556 Image
->HasAlpha
= FALSE
;
557 egComposeImage(NewImage
, Image
, 0, 0);
564 // misc internal functions
567 VOID
egInsertPlane(IN UINT8
*SrcDataPtr
, IN UINT8
*DestPlanePtr
, IN UINTN PixelCount
)
571 for (i
= 0; i
< PixelCount
; i
++) {
572 *DestPlanePtr
= *SrcDataPtr
++;
577 VOID
egSetPlane(IN UINT8
*DestPlanePtr
, IN UINT8 Value
, IN UINTN PixelCount
)
581 for (i
= 0; i
< PixelCount
; i
++) {
582 *DestPlanePtr
= Value
;
587 VOID
egCopyPlane(IN UINT8
*SrcPlanePtr
, IN UINT8
*DestPlanePtr
, IN UINTN PixelCount
)
591 for (i
= 0; i
< PixelCount
; i
++) {
592 *DestPlanePtr
= *SrcPlanePtr
;
593 DestPlanePtr
+= 4, SrcPlanePtr
+= 4;