X-Git-Url: https://code.delx.au/refind/blobdiff_plain/75afe82dbf9803baa447499a1e7942a4ca342535..HEAD:/libeg/image.c diff --git a/libeg/image.c b/libeg/image.c index 5fc915d..5fcd288 100644 --- a/libeg/image.c +++ b/libeg/image.c @@ -33,16 +33,48 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * Modifications copyright (c) 2012-2015 Roderick W. Smith + * + * Modifications distributed under the terms of the GNU General Public + * License (GPL) version 3 (GPLv3), or (at your option) any later version. + * + */ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "libegint.h" #include "../refind/global.h" #include "../refind/lib.h" #include "../refind/screen.h" +#include "../refind/mystrings.h" #include "../include/refit_call_wrapper.h" #include "lodepng.h" #define MAX_FILE_SIZE (1024*1024*1024) +// Multiplier for pseudo-floating-point operations in egScaleImage(). +// A value of 4096 should keep us within limits on 32-bit systems, but I've +// seen some minor artifacts at this level, so give it a bit more precision +// on 64-bit systems.... +#if defined(EFIX64) | defined(EFIAARCH64) +#define FP_MULTIPLIER (UINTN) 65536 +#else +#define FP_MULTIPLIER (UINTN) 4096 +#endif + #ifndef __MAKEWITH_GNUEFI #define LibLocateHandle gBS->LocateHandleBuffer #define LibOpenRoot EfiLibOpenRoot @@ -61,7 +93,7 @@ EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha) return NULL; NewImage->PixelData = (EG_PIXEL *) AllocatePool(Width * Height * sizeof(EG_PIXEL)); if (NewImage->PixelData == NULL) { - FreePool(NewImage); + egFreeImage(NewImage); return NULL; } @@ -85,9 +117,10 @@ EG_IMAGE * egCreateFilledImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAl EG_IMAGE * egCopyImage(IN EG_IMAGE *Image) { - EG_IMAGE *NewImage; + EG_IMAGE *NewImage = NULL; - NewImage = egCreateImage(Image->Width, Image->Height, Image->HasAlpha); + if (Image != NULL) + NewImage = egCreateImage(Image->Width, Image->Height, Image->HasAlpha); if (NewImage == NULL) return NULL; @@ -116,6 +149,77 @@ EG_IMAGE * egCropImage(IN EG_IMAGE *Image, IN UINTN StartX, IN UINTN StartY, IN return NewImage; } // EG_IMAGE * egCropImage() +// The following function implements a bilinear image scaling algorithm, based on +// code presented at http://tech-algorithm.com/articles/bilinear-image-scaling/. +// Resize an image; returns pointer to resized image if successful, NULL otherwise. +// Calling function is responsible for freeing allocated memory. +// NOTE: x_ratio, y_ratio, x_diff, and y_diff should really be float values; +// however, I've found that my 32-bit Mac Mini has a buggy EFI (or buggy CPU?), which +// causes this function to hang on float-to-UINT8 conversions on some (but not all!) +// float values. Therefore, this function uses integer arithmetic but multiplies +// all values by FP_MULTIPLIER to achieve something resembling the sort of precision +// needed for good results. +EG_IMAGE * egScaleImage(IN EG_IMAGE *Image, IN UINTN NewWidth, IN UINTN NewHeight) { + EG_IMAGE *NewImage = NULL; + EG_PIXEL a, b, c, d; + UINTN x, y, Index ; + UINTN i, j; + UINTN Offset = 0; + UINTN x_ratio, y_ratio, x_diff, y_diff; + + if ((Image == NULL) || (Image->Height == 0) || (Image->Width == 0) || (NewWidth == 0) || (NewHeight == 0)) + return NULL; + + if ((Image->Width == NewWidth) && (Image->Height == NewHeight)) + return (egCopyImage(Image)); + + NewImage = egCreateImage(NewWidth, NewHeight, Image->HasAlpha); + if (NewImage == NULL) + return NULL; + + x_ratio = ((Image->Width - 1) * FP_MULTIPLIER) / NewWidth; + y_ratio = ((Image->Height - 1) * FP_MULTIPLIER) / NewHeight; + + for (i = 0; i < NewHeight; i++) { + for (j = 0; j < NewWidth; j++) { + x = (j * (Image->Width - 1)) / NewWidth; + y = (i * (Image->Height - 1)) / NewHeight; + x_diff = (x_ratio * j) - x * FP_MULTIPLIER; + y_diff = (y_ratio * i) - y * FP_MULTIPLIER; + Index = ((y * Image->Width) + x); + a = Image->PixelData[Index]; + b = Image->PixelData[Index + 1]; + c = Image->PixelData[Index + Image->Width]; + d = Image->PixelData[Index + Image->Width + 1]; + + // blue element + NewImage->PixelData[Offset].b = ((a.b) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) + + (b.b) * (x_diff) * (FP_MULTIPLIER - y_diff) + + (c.b) * (y_diff) * (FP_MULTIPLIER - x_diff) + + (d.b) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER); + + // green element + NewImage->PixelData[Offset].g = ((a.g) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) + + (b.g) * (x_diff) * (FP_MULTIPLIER - y_diff) + + (c.g) * (y_diff) * (FP_MULTIPLIER - x_diff) + + (d.g) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER); + + // red element + NewImage->PixelData[Offset].r = ((a.r) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) + + (b.r) * (x_diff) * (FP_MULTIPLIER - y_diff) + + (c.r) * (y_diff) * (FP_MULTIPLIER - x_diff) + + (d.r) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER); + + // alpha element + NewImage->PixelData[Offset++].a = ((a.a) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) + + (b.a) * (x_diff) * (FP_MULTIPLIER - y_diff) + + (c.a) * (y_diff) * (FP_MULTIPLIER - x_diff) + + (d.a) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER); + } // for (j...) + } // for (i...) + return NewImage; +} // EG_IMAGE * egScaleImage() + VOID egFreeImage(IN EG_IMAGE *Image) { if (Image != NULL) { @@ -129,7 +233,7 @@ VOID egFreeImage(IN EG_IMAGE *Image) // Basic file operations // -EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength) +EFI_STATUS egLoadFile(IN EFI_FILE *BaseDir, IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength) { EFI_STATUS Status; EFI_FILE_HANDLE FileHandle; @@ -138,6 +242,9 @@ EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, OUT UINT8 **Fil UINTN BufferSize; UINT8 *Buffer; + if ((BaseDir == NULL) || (FileName == NULL)) + return EFI_NOT_FOUND; + Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(Status)) { return Status; @@ -174,7 +281,7 @@ EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, OUT UINT8 **Fil static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; -static EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir) +EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir) { EFI_STATUS Status; UINTN HandleCount = 0; @@ -204,7 +311,7 @@ EFI_STATUS egSaveFile(IN EFI_FILE* BaseDir OPTIONAL, IN CHAR16 *FileName, } Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (EFI_ERROR(Status)) return Status; @@ -251,7 +358,7 @@ EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN Wan return NULL; // decode it - NewImage = egDecodeAny(FileData, FileDataLength, 128, WantAlpha); + NewImage = egDecodeAny(FileData, FileDataLength, 128 /* arbitrary value */, WantAlpha); FreePool(FileData); return NewImage; @@ -264,7 +371,7 @@ EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize) EFI_STATUS Status; UINT8 *FileData; UINTN FileDataLength; - EG_IMAGE *NewImage; + EG_IMAGE *Image, *NewImage; if (BaseDir == NULL || Path == NULL) return NULL; @@ -275,17 +382,39 @@ EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize) return NULL; // decode it - NewImage = egDecodeAny(FileData, FileDataLength, IconSize, TRUE); + Image = egDecodeAny(FileData, FileDataLength, IconSize, TRUE); FreePool(FileData); - if ((NewImage->Width != IconSize) || (NewImage->Height != IconSize)) { - Print(L"Warning: Attempt to load icon of the wrong size from '%s'\n", Path); - MyFreePool(NewImage); - NewImage = NULL; + if ((Image->Width != IconSize) || (Image->Height != IconSize)) { + NewImage = egScaleImage(Image, IconSize, IconSize); + if (!NewImage) + Print(L"Warning: Unable to scale icon of the wrong size from '%s'\n", Path); + egFreeImage(Image); + Image = NewImage; } - return NewImage; + return Image; } // EG_IMAGE *egLoadIcon() +// Returns an icon of any type from the specified subdirectory using the specified +// base name. All directory references are relative to BaseDir. For instance, if +// SubdirName is "myicons" and BaseName is "os_linux", this function will return +// an image based on "myicons/os_linux.icns" or "myicons/os_linux.png", in that +// order of preference. Returns NULL if no such file is a valid icon file. +EG_IMAGE * egLoadIconAnyType(IN EFI_FILE *BaseDir, IN CHAR16 *SubdirName, IN CHAR16 *BaseName, IN UINTN IconSize) { + EG_IMAGE *Image = NULL; + CHAR16 *Extension; + CHAR16 FileName[256]; + UINTN i = 0; + + while (((Extension = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) && (Image == NULL)) { + SPrint(FileName, 255, L"%s\\%s.%s", SubdirName, BaseName, Extension); + Image = egLoadIcon(BaseDir, FileName, IconSize); + MyFreePool(Extension); + } // while() + + return Image; +} // EG_IMAGE *egLoadIconAnyType() + // Returns an icon with any extension in ICON_EXTENSIONS from either the directory // specified by GlobalConfig.IconsDir or DEFAULT_ICONS_DIR. The input BaseName // should be the icon name without an extension. For instance, if BaseName is @@ -295,30 +424,16 @@ EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize) // order of preference. Returns NULL if no such icon can be found. All file // references are relative to SelfDir. EG_IMAGE * egFindIcon(IN CHAR16 *BaseName, IN UINTN IconSize) { - CHAR16 *LoadDir, *Extension; - CHAR16 FileName[256]; - UINTN i; EG_IMAGE *Image = NULL; - if (GlobalConfig.IconsDir != NULL) - LoadDir = GlobalConfig.IconsDir; - else - LoadDir = StrDuplicate(DEFAULT_ICONS_DIR); - - while ((LoadDir != NULL) && (Image == NULL)) { - i = 0; - while (((Extension = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) && (Image == NULL)) { - SPrint(FileName, 255, L"%s\\%s.%s", LoadDir, BaseName, Extension); - Image = egLoadIcon(SelfDir, FileName, IconSize); - MyFreePool(Extension); - } // while() - if (LoadDir == GlobalConfig.IconsDir) { - LoadDir = StrDuplicate(DEFAULT_ICONS_DIR); - } else { - MyFreePool(LoadDir); - LoadDir = NULL; - } // if/else - } // while() + if (GlobalConfig.IconsDir != NULL) { + Image = egLoadIconAnyType(SelfDir, GlobalConfig.IconsDir, BaseName, IconSize); + } + + if (Image == NULL) { + Image = egLoadIconAnyType(SelfDir, DEFAULT_ICONS_DIR, BaseName, IconSize); + } + return Image; } // EG_IMAGE * egFindIcon() @@ -539,27 +654,6 @@ VOID egComposeImage(IN OUT EG_IMAGE *CompImage, IN EG_IMAGE *TopImage, IN UINTN } } /* VOID egComposeImage() */ -EG_IMAGE * egEnsureImageSize(IN EG_IMAGE *Image, IN UINTN Width, IN UINTN Height, IN EG_PIXEL *Color) -{ - EG_IMAGE *NewImage; - - if (Image == NULL) - return NULL; - if (Image->Width == Width && Image->Height == Height) - return Image; - - NewImage = egCreateFilledImage(Width, Height, Image->HasAlpha, Color); - if (NewImage == NULL) { - egFreeImage(Image); - return NULL; - } - Image->HasAlpha = FALSE; - egComposeImage(NewImage, Image, 0, 0); - egFreeImage(Image); - - return NewImage; -} - // // misc internal functions //