From: srs5694 Date: Sat, 8 Mar 2014 19:55:11 +0000 (-0500) Subject: New image-scaling code; used for icons and (optionally) for scaling X-Git-Url: https://code.delx.au/refind/commitdiff_plain/cff588e1b33df1c292b3ae11a880e89092221b5f?hp=a2da4c58d1f2ed0afa306dbf2fe0c4ba9b847007 New image-scaling code; used for icons and (optionally) for scaling banners (latter set by new banner_scale option in refind.conf). --- diff --git a/NEWS.txt b/NEWS.txt index a1f8687..ba21e8f 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,6 +1,19 @@ 0.7.8 (?/??/2014): ------------------ +- rEFInd now automatically scales icons to fit the standard icon sizes. + This won't have any effect with the icons that come with rEFInd, but it + can help if you want to use another icon, since you needn't scale it in a + graphics program before using it. Note that rEFInd uses bitmap icons, so + scaling by a huge amount (say, a 16x16 icon to fit the standard 128x128 + OS icon) is not likely to look good. + +- Added new option, banner_scale, that tells rEFInd how to handle banners: + Set to "noscale" (the default), banners are not scaled, although they'll + be cropped if they're too big for the display. This is the same as the + behavior in previous versions. Set to "fillscreen", rEFInd now scales the + banner image (larger or smaller) to fill the display. + - Adjusted the post-installation script in refind.spec (used to generate RPMs, and therefore also indirectly Debian packages) to search for existing shim program files under the filesnames shim.efi and shimx64.efi diff --git a/docs/refind/index.html b/docs/refind/index.html index 74e018d..22723c5 100644 --- a/docs/refind/index.html +++ b/docs/refind/index.html @@ -206,6 +206,8 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

  • Matthew J. Garrett, the developer of the shim boot loader to manage Secure Boot, maintains a blog in which he often writes about EFI issues.
  • +
  • Adam Williamson has written a good summary of what EFI is and how it works.
  • +
  • J. A. Watson has a review of rEFInd on an HP laptop on ZDNet. He had serious problems because of the HP's UEFI bugs, but finally got it to work.
  • James Jesudason has a tutorial on installing Ubuntu 13.04 beta on a Macbook Retina Pro on this blog page. I'd recommend using a Linux filesystem driver to read the kernel directly from a Linux filesystem rather than copy the kernel to the OS X partition as in the tutorial, but either method will work.
  • diff --git a/libeg/image.c b/libeg/image.c index 9f22628..530589a 100644 --- a/libeg/image.c +++ b/libeg/image.c @@ -125,6 +125,67 @@ 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. +EG_IMAGE * egScaleImage(EG_IMAGE *Image, UINTN NewWidth, UINTN NewHeight) { + EG_IMAGE *NewImage = NULL; + EG_PIXEL a, b, c, d; + UINTN x, y, Index ; + UINTN i, j; + UINTN Offset = 0; + float 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 = ((float)(Image->Width-1))/NewWidth ; + y_ratio = ((float)(Image->Height-1))/NewHeight ; + + for (i = 0; i < NewHeight; i++) { + for (j = 0; j < NewWidth; j++) { + x = (UINTN)(x_ratio * j) ; + y = (UINTN)(y_ratio * i) ; + x_diff = (x_ratio * j) - x ; + y_diff = (y_ratio * i) - y ; + 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 + // Yb = Ab(1-Image->Width)(1-Image->Height) + Bb(Image->Width)(1-Image->Height) + Cb(Image->Height)(1-Image->Width) + Db(wh) + NewImage->PixelData[Offset].b = (a.b)*(1-x_diff)*(1-y_diff) + (b.b)*(x_diff)*(1-y_diff) + + (c.b)*(y_diff)*(1-x_diff) + (d.b)*(x_diff*y_diff); + + // green element + // Yg = Ag(1-Image->Width)(1-Image->Height) + Bg(Image->Width)(1-Image->Height) + Cg(Image->Height)(1-Image->Width) + Dg(wh) + NewImage->PixelData[Offset].g = (a.g)*(1-x_diff)*(1-y_diff) + (b.g)*(x_diff)*(1-y_diff) + + (c.g)*(y_diff)*(1-x_diff) + (d.g)*(x_diff*y_diff); + + // red element + // Yr = Ar(1-Image->Width)(1-Image->Height) + Br(Image->Width)(1-Image->Height) + Cr(Image->Height)(1-Image->Width) + Dr(wh) + NewImage->PixelData[Offset].r = (a.r)*(1-x_diff)*(1-y_diff) + (b.r)*(x_diff)*(1-y_diff) + + (c.r)*(y_diff)*(1-x_diff) + (d.r)*(x_diff*y_diff); + + // alpha element + NewImage->PixelData[Offset++].a = (a.a)*(1-x_diff)*(1-y_diff) + (b.a)*(x_diff)*(1-y_diff) + + (c.a)*(y_diff)*(1-x_diff) + (d.a)*(x_diff*y_diff); + + } // for (j...) + } // for (i...) + return NewImage; +} // EG_IMAGE * egScaleImage() + VOID egFreeImage(IN EG_IMAGE *Image) { if (Image != NULL) { @@ -273,7 +334,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; @@ -284,15 +345,17 @@ 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); + MyFreePool(Image); + Image = NewImage; } - return NewImage; + return Image; } // EG_IMAGE *egLoadIcon() // Returns an icon of any type from the specified subdirectory using the specified diff --git a/libeg/libeg.h b/libeg/libeg.h index c8426d1..e94f1eb 100644 --- a/libeg/libeg.h +++ b/libeg/libeg.h @@ -100,6 +100,7 @@ EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha); EG_IMAGE * egCreateFilledImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha, IN EG_PIXEL *Color); EG_IMAGE * egCopyImage(IN EG_IMAGE *Image); EG_IMAGE * egCropImage(IN EG_IMAGE *Image, IN UINTN StartX, IN UINTN StartY, IN UINTN Width, IN UINTN Height); +EG_IMAGE * egScaleImage(EG_IMAGE *Image, UINTN NewWidth, UINTN NewHeight); VOID egFreeImage(IN EG_IMAGE *Image); EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN WantAlpha); diff --git a/libeg/load_icns.c b/libeg/load_icns.c index f357ac3..ce9e7ab 100644 --- a/libeg/load_icns.c +++ b/libeg/load_icns.c @@ -36,6 +36,8 @@ #include "libegint.h" +#define MAX_ICNS_SIZES 4 + // // Decompress .icns RLE data // @@ -97,11 +99,13 @@ EG_IMAGE * egDecodeICNS(IN UINT8 *FileData, IN UINTN FileDataLength, IN UINTN Ic EG_IMAGE *NewImage; UINT8 *Ptr, *BufferEnd, *DataPtr, *MaskPtr; UINT32 BlockLen, DataLen, MaskLen; - UINTN FetchPixelSize, PixelCount, i; + UINTN PixelCount, i; UINT8 *CompData; UINTN CompLen; UINT8 *SrcPtr; EG_PIXEL *DestPtr; + UINTN SizesToTry[MAX_ICNS_SIZES + 1] = {IconSize, 128, 48, 32, 16}; + UINTN SizeToTry = 0; if (FileDataLength < 8 || FileData == NULL || FileData[0] != 'i' || FileData[1] != 'c' || FileData[2] != 'n' || FileData[3] != 's') { @@ -109,68 +113,70 @@ EG_IMAGE * egDecodeICNS(IN UINT8 *FileData, IN UINTN FileDataLength, IN UINTN Ic return NULL; } - FetchPixelSize = IconSize; for (;;) { DataPtr = NULL; DataLen = 0; MaskPtr = NULL; MaskLen = 0; - Ptr = FileData + 8; - BufferEnd = FileData + FileDataLength; - // iterate over tagged blocks in the file - while (Ptr + 8 <= BufferEnd) { - BlockLen = ((UINT32)Ptr[4] << 24) + ((UINT32)Ptr[5] << 16) + ((UINT32)Ptr[6] << 8) + (UINT32)Ptr[7]; - if (Ptr + BlockLen > BufferEnd) // block continues beyond end of file - break; - - // extract the appropriate blocks for each pixel size - if (FetchPixelSize == 128) { - if (Ptr[0] == 'i' && Ptr[1] == 't' && Ptr[2] == '3' && Ptr[3] == '2') { - if (Ptr[8] == 0 && Ptr[9] == 0 && Ptr[10] == 0 && Ptr[11] == 0) { - DataPtr = Ptr + 12; - DataLen = BlockLen - 12; - } - } else if (Ptr[0] == 't' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { - MaskPtr = Ptr + 8; - MaskLen = BlockLen - 8; - } - - } else if (FetchPixelSize == 48) { - if (Ptr[0] == 'i' && Ptr[1] == 'h' && Ptr[2] == '3' && Ptr[3] == '2') { - DataPtr = Ptr + 8; - DataLen = BlockLen - 8; - } else if (Ptr[0] == 'h' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { - MaskPtr = Ptr + 8; - MaskLen = BlockLen - 8; - } - - } else if (FetchPixelSize == 32) { - if (Ptr[0] == 'i' && Ptr[1] == 'l' && Ptr[2] == '3' && Ptr[3] == '2') { - DataPtr = Ptr + 8; - DataLen = BlockLen - 8; - } else if (Ptr[0] == 'l' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { - MaskPtr = Ptr + 8; - MaskLen = BlockLen - 8; - } - - } else if (FetchPixelSize == 16) { - if (Ptr[0] == 'i' && Ptr[1] == 's' && Ptr[2] == '3' && Ptr[3] == '2') { - DataPtr = Ptr + 8; - DataLen = BlockLen - 8; - } else if (Ptr[0] == 's' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { - MaskPtr = Ptr + 8; - MaskLen = BlockLen - 8; - } - - } - - Ptr += BlockLen; - } + do { + IconSize = SizesToTry[SizeToTry]; + Ptr = FileData + 8; + BufferEnd = FileData + FileDataLength; + // iterate over tagged blocks in the file + while (Ptr + 8 <= BufferEnd) { + BlockLen = ((UINT32)Ptr[4] << 24) + ((UINT32)Ptr[5] << 16) + ((UINT32)Ptr[6] << 8) + (UINT32)Ptr[7]; + if (Ptr + BlockLen > BufferEnd) // block continues beyond end of file + break; + + // extract the appropriate blocks for each pixel size + if (IconSize == 128) { + if (Ptr[0] == 'i' && Ptr[1] == 't' && Ptr[2] == '3' && Ptr[3] == '2') { + if (Ptr[8] == 0 && Ptr[9] == 0 && Ptr[10] == 0 && Ptr[11] == 0) { + DataPtr = Ptr + 12; + DataLen = BlockLen - 12; + } + } else if (Ptr[0] == 't' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { + MaskPtr = Ptr + 8; + MaskLen = BlockLen - 8; + } + + } else if (IconSize == 48) { + if (Ptr[0] == 'i' && Ptr[1] == 'h' && Ptr[2] == '3' && Ptr[3] == '2') { + DataPtr = Ptr + 8; + DataLen = BlockLen - 8; + } else if (Ptr[0] == 'h' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { + MaskPtr = Ptr + 8; + MaskLen = BlockLen - 8; + } + + } else if (IconSize == 32) { + if (Ptr[0] == 'i' && Ptr[1] == 'l' && Ptr[2] == '3' && Ptr[3] == '2') { + DataPtr = Ptr + 8; + DataLen = BlockLen - 8; + } else if (Ptr[0] == 'l' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { + MaskPtr = Ptr + 8; + MaskLen = BlockLen - 8; + } + + } else if (IconSize == 16) { + if (Ptr[0] == 'i' && Ptr[1] == 's' && Ptr[2] == '3' && Ptr[3] == '2') { + DataPtr = Ptr + 8; + DataLen = BlockLen - 8; + } else if (Ptr[0] == 's' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') { + MaskPtr = Ptr + 8; + MaskLen = BlockLen - 8; + } + + } + + Ptr += BlockLen; + } + } while ((DataPtr == NULL) && (SizeToTry++ < MAX_ICNS_SIZES)); /* FUTURE: try to load a different size and scale it later - if (DataPtr == NULL && FetchPixelSize == 32) { - FetchPixelSize = 128; + if (DataPtr == NULL && IconSize == 32) { + IconSize = 128; continue; } */ @@ -181,10 +187,10 @@ EG_IMAGE * egDecodeICNS(IN UINT8 *FileData, IN UINTN FileDataLength, IN UINTN Ic return NULL; // no image found // allocate image structure and buffer - NewImage = egCreateImage(FetchPixelSize, FetchPixelSize, WantAlpha); + NewImage = egCreateImage(IconSize, IconSize, WantAlpha); if (NewImage == NULL) return NULL; - PixelCount = FetchPixelSize * FetchPixelSize; + PixelCount = IconSize * IconSize; if (DataLen < PixelCount * 3) { diff --git a/refind.conf-sample b/refind.conf-sample index 053beb5..9d84357 100644 --- a/refind.conf-sample +++ b/refind.conf-sample @@ -51,6 +51,14 @@ timeout 20 #banner hostname.bmp #banner mybanner.png +# Specify how to handle banners that aren't exactly the same as the screen +# size: +# noscale - Crop if too big, show with border if too small +# fillscreen - Fill the screen +# Default is noscale +# +#banner_scale fillscreen + # Custom images for the selection background. There is a big one (144 x 144) # for the OS icons, and a small one (64 x 64) for the function icons in the # second row. If only a small image is given, that one is also used for @@ -150,7 +158,7 @@ timeout 20 # reboot - a tag to reboot the computer # firmware - a tag to reboot the computer into the firmware's # user interface (ignored on older computers) -# Default is shell,memtest,apple_recovery,mok_tool,about,shutdown,reboot,firmware +# Default is shell,memtest,apple_recovery,windows_recovery,mok_tool,about,shutdown,reboot,firmware # #showtools shell, memtest, mok_tool, about, reboot, exit, firmware diff --git a/refind/config.c b/refind/config.c index c56c54f..fd7f677 100644 --- a/refind/config.c +++ b/refind/config.c @@ -558,6 +558,15 @@ VOID ReadConfig(CHAR16 *FileName) } else if (StriCmp(TokenList[0], L"banner") == 0) { HandleString(TokenList, TokenCount, &(GlobalConfig.BannerFileName)); + } else if ((StriCmp(TokenList[0], L"banner_scale") == 0) && (TokenCount == 2)) { + if (StriCmp(TokenList[1], L"noscale") == 0) { + GlobalConfig.BannerScale = BANNER_NOSCALE; + } else if (StriCmp(TokenList[1], L"fillscreen") == 0) { + GlobalConfig.BannerScale = BANNER_FILLSCREEN; + } else { + Print(L" unknown banner_type flag: '%s'\n", TokenList[1]); + } // if/else + } else if (StriCmp(TokenList[0], L"selection_small") == 0) { HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionSmallFileName)); diff --git a/refind/global.h b/refind/global.h index ee48764..6641d8b 100644 --- a/refind/global.h +++ b/refind/global.h @@ -124,6 +124,10 @@ #define FS_TYPE_BTRFS 7 #define FS_TYPE_ISO9660 8 +// How to scale banner images +#define BANNER_NOSCALE 0 +#define BANNER_FILLSCREEN 1 + // Names of binaries that can manage MOKs.... #define MOK_NAMES L"MokManager.efi,HashTool.efi,HashTool-signed.efi" // Directories to search for these MOK-managing programs. Note that SelfDir is @@ -239,6 +243,7 @@ typedef struct { UINTN LegacyType; UINTN ScanDelay; UINTN ScreensaverTime; + UINTN BannerScale; CHAR16 *BannerFileName; EG_IMAGE *ScreenBackground; CHAR16 *ConfigFilename; diff --git a/refind/main.c b/refind/main.c index f8428d5..3581067 100644 --- a/refind/main.c +++ b/refind/main.c @@ -132,6 +132,7 @@ static REFIT_MENU_SCREEN MainMenu = { L"Main Menu", NULL, 0, NULL, 0, NULL static REFIT_MENU_SCREEN AboutMenu = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" }; REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, 0, + BANNER_NOSCALE, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, { TAG_SHELL, TAG_MEMTEST, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0 } diff --git a/refind/screen.c b/refind/screen.c index 1676cb6..49c7a94 100644 --- a/refind/screen.c +++ b/refind/screen.c @@ -433,33 +433,38 @@ VOID SwitchToGraphicsAndClear(VOID) BltClearScreen(TRUE); } -VOID BltClearScreen(IN BOOLEAN ShowBanner) +VOID BltClearScreen(BOOLEAN ShowBanner) { - static EG_IMAGE *Banner = NULL, *CroppedBanner; + static EG_IMAGE *Banner = NULL; + EG_IMAGE *NewBanner = NULL; INTN BannerPosX, BannerPosY; EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 }; if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) { // load banner on first call if (Banner == NULL) { - if (GlobalConfig.BannerFileName == NULL) { - Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE); - } else { + if (GlobalConfig.BannerFileName) Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE); - if (Banner && ((Banner->Width > UGAWidth) || (Banner->Height > UGAHeight))) { - CroppedBanner = egCropImage(Banner, 0, 0, (Banner->Width > UGAWidth) ? UGAWidth : Banner->Width, - (Banner->Height > UGAHeight) ? UGAHeight : Banner->Height); - MyFreePool(Banner); - Banner = CroppedBanner; - } // if image too big - if (Banner == NULL) { - Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE); - } // if unusable image - } - if (Banner != NULL) - MenuBackgroundPixel = Banner->PixelData[0]; + if (Banner == NULL) + Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE); } + if (Banner) { + if (GlobalConfig.BannerScale == BANNER_FILLSCREEN) { + if ((Banner->Height != UGAHeight) || (Banner->Width != UGAWidth)) { + NewBanner = egScaleImage(Banner, UGAWidth, UGAHeight); + } // if + } else if ((Banner->Width > UGAWidth) || (Banner->Height > UGAHeight)) { + NewBanner = egCropImage(Banner, 0, 0, (Banner->Width > UGAWidth) ? UGAWidth : Banner->Width, + (Banner->Height > UGAHeight) ? UGAHeight : Banner->Height); + } // if/elseif + if (NewBanner) { + MyFreePool(Banner); + Banner = NewBanner; + } + MenuBackgroundPixel = Banner->PixelData[0]; + } // if Banner exists + // clear and draw banner if (GlobalConfig.ScreensaverTime != -1) egClearScreen(&MenuBackgroundPixel); @@ -476,7 +481,7 @@ VOID BltClearScreen(IN BOOLEAN ShowBanner) BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY); } - } else { + } else { // not showing banner // clear to menu background color egClearScreen(&MenuBackgroundPixel); } @@ -484,7 +489,8 @@ VOID BltClearScreen(IN BOOLEAN ShowBanner) GraphicsScreenDirty = FALSE; egFreeImage(GlobalConfig.ScreenBackground); GlobalConfig.ScreenBackground = egCopyScreen(); -} /* VOID BltClearScreen() */ +} // VOID BltClearScreen() + VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos) {