banners (latter set by new banner_scale option in refind.conf).
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
<li>Matthew J. Garrett, the developer of the shim boot loader to manage Secure Boot, maintains <a href="http://mjg59.dreamwidth.org/">a blog</a> in which he often writes about EFI issues.</li>
+<li>Adam Williamson has written a good <a href="https://www.happyassassin.net/2014/01/25/uefi-boot-how-does-that-actually-work-then/">summary of what EFI is and how it works.</a></li>
+
<li>J. A. Watson has a <a href="http://www.zdnet.com/the-refind-boot-loader-for-uefi-systems-7000010275/">review of rEFInd on an HP laptop</a> on ZDNet. He had serious problems because of the HP's UEFI bugs, but finally got it to work.</li>
<li>James Jesudason has a tutorial on installing Ubuntu 13.04 beta on a Macbook Retina Pro on <a href="http://randomtutor.blogspot.com/2013_02_01_archive.html">this blog page.</a> 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.</li>
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) {
EFI_STATUS Status;
UINT8 *FileData;
UINTN FileDataLength;
- EG_IMAGE *NewImage;
+ EG_IMAGE *Image, *NewImage;
if (BaseDir == NULL || Path == NULL)
return NULL;
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
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);
#include "libegint.h"
+#define MAX_ICNS_SIZES 4
+
//
// Decompress .icns RLE data
//
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') {
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;
}
*/
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) {
#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
# 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
} 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));
#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
UINTN LegacyType;
UINTN ScanDelay;
UINTN ScreensaverTime;
+ UINTN BannerScale;
CHAR16 *BannerFileName;
EG_IMAGE *ScreenBackground;
CHAR16 *ConfigFilename;
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 }
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);
BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY);
}
- } else {
+ } else { // not showing banner
// clear to menu background color
egClearScreen(&MenuBackgroundPixel);
}
GraphicsScreenDirty = FALSE;
egFreeImage(GlobalConfig.ScreenBackground);
GlobalConfig.ScreenBackground = egCopyScreen();
-} /* VOID BltClearScreen() */
+} // VOID BltClearScreen()
+
VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
{