/* * a class to keep definitions for all theme settings */ extern "C" { #include } #include "libegint.h" #include "../refit/screen.h" #include "../refit/lib.h" #include "XTheme.h" #include "nanosvg.h" #ifndef DEBUG_ALL #define DEBUG_XTHEME 1 #else #define DEBUG_XTHEME DEBUG_ALL #endif #if DEBUG_XTHEME == 0 #define DBG(...) #else #define DBG(...) DebugLog(DEBUG_XTHEME, __VA_ARGS__) #endif CONST CHAR8* IconsNames[] = { "func_about", "func_options", "func_clover", "func_secureboot", "func_secureboot_config", "func_reset", "func_shutdown", "func_help", "tool_shell", //8 "tool_part", "tool_rescue", "pointer",//11 "vol_internal", "vol_external", "vol_optical", "vol_firewire", "vol_clover" , "vol_internal_hfs" , //17 "vol_internal_apfs", "vol_internal_ntfs", "vol_internal_ext3" , "vol_recovery",//21 // not used? will be skipped while theme parsing "logo", "selection_small", "selection_big", //BUILTIN_SELECTION_BIG=24 we keep this numeration //os icons "os_mac", //0 + 25 "os_tiger", "os_leo", "os_snow", "os_lion", "os_cougar", "os_mav", "os_yos", "os_cap", //33 "os_sierra", "os_hsierra", "os_moja", //36 "os_cata", //37 //there is no reserve for 10.16, next oses should be added to the end of the list "os_linux", //13 + 25 = 38 "os_ubuntu", "os_suse", "os_freebsd", //16+25 = 41 "os_freedos", "os_win", "os_vista", "radio_button", //20+25 = 45 "radio_button_selected", "checkbox", //22+25 = 47 "checkbox_checked", "scrollbar_background", //24 - present here for SVG theme but should be done more common way "scrollbar_holder", "os_unknown", //51 == ICON_OTHER_OS "os_clover", //52 == ICON_CLOVER //other oses will be added below "" }; const INTN IconsNamesSize = sizeof(IconsNames) / sizeof(IconsNames[0]); //icons class //if ImageNight is not set then Image should be used #define DEC_BUILTIN_ICON(id, ico) { \ Empty = EFI_ERROR(Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico))); \ } #define DEC_BUILTIN_ICON2(id, ico, dark) { \ Empty = EFI_ERROR(Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico))); \ ImageNight.FromPNG(ACCESS_EMB_DATA(dark), ACCESS_EMB_SIZE(dark)); \ } XIcon::XIcon(INTN Index, bool TakeEmbedded) : Id(Index), Name(), Image(), ImageNight(), Native(false), ImageSVG(nullptr), ImageSVGnight(nullptr) { // Id = Index; // Name.setEmpty(); // Native = false; // ImageSVG = nullptr; // ImageSVGnight = nullptr; if (Index >= BUILTIN_ICON_FUNC_ABOUT && Index < IconsNamesSize) { //full table Name.takeValueFrom(IconsNames[Index]); } if (TakeEmbedded) { GetEmbedded(); } } XIcon& XIcon::operator=(const XIcon& src) { Id = src.Id; Name = src.Name; if (!src.isEmpty()) { Image = src.Image; if (!src.ImageNight.isEmpty()) { ImageNight = src.ImageNight; } setFilled(); //this moment we copy pointers. Later it will be class variables ImageSVG = src.ImageSVG; ImageSVGnight = src.ImageSVGnight; } return *this; } void XIcon::GetEmbedded() { switch (Id) { case BUILTIN_ICON_FUNC_ABOUT: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_ABOUT, emb_func_about, emb_dark_func_about) break; case BUILTIN_ICON_FUNC_OPTIONS: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_OPTIONS, emb_func_options, emb_dark_func_options) break; case BUILTIN_ICON_FUNC_CLOVER: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_CLOVER, emb_func_clover, emb_dark_func_clover) break; case BUILTIN_ICON_FUNC_SECURE_BOOT: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_SECURE_BOOT, emb_func_secureboot, emb_dark_func_secureboot) break; case BUILTIN_ICON_FUNC_SECURE_BOOT_CONFIG: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_SECURE_BOOT_CONFIG, emb_func_secureboot_config, emb_dark_func_secureboot_config) break; case BUILTIN_ICON_FUNC_RESET: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_RESET, emb_func_reset, emb_dark_func_reset) break; case BUILTIN_ICON_FUNC_EXIT: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_EXIT, emb_func_exit, emb_dark_func_exit) break; case BUILTIN_ICON_FUNC_HELP: DEC_BUILTIN_ICON2(BUILTIN_ICON_FUNC_HELP, emb_func_help, emb_dark_func_help) break; case BUILTIN_ICON_TOOL_SHELL: DEC_BUILTIN_ICON2(BUILTIN_ICON_TOOL_SHELL, emb_func_shell, emb_dark_func_shell) break; case BUILTIN_ICON_BANNER: DEC_BUILTIN_ICON2(BUILTIN_ICON_BANNER, emb_logo, emb_dark_logo) break; case BUILTIN_SELECTION_SMALL: DEC_BUILTIN_ICON2(BUILTIN_SELECTION_SMALL, emb_selection_small, emb_dark_selection_small) break; case BUILTIN_SELECTION_BIG: DEC_BUILTIN_ICON2(BUILTIN_SELECTION_BIG, emb_selection_big, emb_dark_selection_big) break; //next icons have no dark image case BUILTIN_ICON_POINTER: DEC_BUILTIN_ICON(BUILTIN_ICON_POINTER, emb_pointer) break; case BUILTIN_ICON_VOL_INTERNAL: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL, emb_vol_internal) break; case BUILTIN_ICON_VOL_EXTERNAL: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_EXTERNAL, emb_vol_external) break; case BUILTIN_ICON_VOL_OPTICAL: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_OPTICAL, emb_vol_optical) break; case BUILTIN_ICON_VOL_BOOTER: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_BOOTER, emb_vol_internal_booter) break; case BUILTIN_ICON_VOL_INTERNAL_HFS: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL_HFS, emb_vol_internal_hfs) break; case BUILTIN_ICON_VOL_INTERNAL_APFS: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL_APFS, emb_vol_internal_apfs) break; case BUILTIN_ICON_VOL_INTERNAL_NTFS: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL_NTFS, emb_vol_internal_ntfs) break; case BUILTIN_ICON_VOL_INTERNAL_EXT3: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL_EXT3, emb_vol_internal_ext) break; case BUILTIN_ICON_VOL_INTERNAL_REC: DEC_BUILTIN_ICON(BUILTIN_ICON_VOL_INTERNAL_REC, emb_vol_internal_recovery) break; case BUILTIN_RADIO_BUTTON: DEC_BUILTIN_ICON(BUILTIN_RADIO_BUTTON, emb_radio_button) break; case BUILTIN_RADIO_BUTTON_SELECTED: DEC_BUILTIN_ICON(BUILTIN_RADIO_BUTTON_SELECTED, emb_radio_button_selected) break; case BUILTIN_CHECKBOX: DEC_BUILTIN_ICON(BUILTIN_CHECKBOX, emb_checkbox) break; case BUILTIN_CHECKBOX_CHECKED: DEC_BUILTIN_ICON(BUILTIN_CHECKBOX_CHECKED, emb_checkbox_checked) break; case BUILTIN_ICON_SELECTION: Name.takeValueFrom("selection_indicator"); DEC_BUILTIN_ICON(BUILTIN_ICON_SELECTION, emb_selection_indicator) break; default: // Image.setEmpty(); //done by ctor? break; } //something to do else? } XIcon::~XIcon() {} //copy from XImage for our purpose EFI_STATUS XIcon::LoadXImage(EFI_FILE *BaseDir, const char* IconName) { return LoadXImage(BaseDir, XStringW().takeValueFrom(IconName)); } EFI_STATUS XIcon::LoadXImage(EFI_FILE *BaseDir, const wchar_t* LIconName) { return LoadXImage(BaseDir, XStringW().takeValueFrom(LIconName)); } //dont call this procedure for SVG theme BaseDir == NULL? //it can be used for other files EFI_STATUS XIcon::LoadXImage(EFI_FILE *BaseDir, const XStringW& IconName) { EFI_STATUS Status = Image.LoadXImage(BaseDir, IconName); ImageNight.LoadXImage(BaseDir, IconName + L"_night"_XSW); if (!EFI_ERROR(Status)) setFilled(); return Status; } XImage* XIcon::GetBest(bool night, bool *free) { #if 1 if (ImageSVG) { NSVGimage* sImage = (NSVGimage*)ImageSVGnight; if (!night || !ImageSVGnight) sImage = (NSVGimage*)ImageSVG; float Scale = sImage->scale; NSVGrasterizer* rast = nsvgCreateRasterizer(); float Height = sImage->height * Scale; float Width = sImage->width * Scale; int iWidth = (int)(Width + 0.5f); int iHeight = (int)(Height + 0.5f); XImage* NewImage = new XImage(iWidth, iHeight); //TODO creating new XImage we have to delete it after use if (sImage->shapes == NULL) { if (free) *free = true; return NewImage; } float bounds[4]; nsvg__imageBounds(sImage, bounds); float tx = 0.f, ty = 0.f; float realWidth = (bounds[2] - bounds[0]) * Scale; float realHeight = (bounds[3] - bounds[1]) * Scale; tx = (Width - realWidth) * 0.5f; ty = (Height - realHeight) * 0.5f; nsvgRasterize(rast, sImage, tx, ty, Scale, Scale, (UINT8*)NewImage->GetPixelPtr(0,0), iWidth, iHeight, iWidth*4); nsvgDeleteRasterizer(rast); // if (night) ImageNight = *NewImage; // else Image = *NewImage; // delete NewImage; if (free) *free = true; return NewImage; } #endif XImage* RetImage = (night && !ImageNight.isEmpty())? &ImageNight : &Image; if (free) *free = false; return RetImage; } //xtheme class XTheme::XTheme() { Init(); } XTheme::~XTheme() { //nothing todo? } void XTheme::Init() { // DisableFlags = 0; HideBadges = 0; HideUIFlags = 0; // TextOnly = FALSE; Font = FONT_GRAY; // FONT_TYPE CharWidth = 9; SelectionColor = 0x80808080; SelectionBackgroundPixel = { 0xef, 0xef, 0xef, 0xff }; FontFileName.setEmpty(); // Theme.takeValueFrom("embedded"); embedded = false; BannerFileName.setEmpty(); SelectionSmallFileName.setEmpty(); SelectionBigFileName.setEmpty(); SelectionIndicatorName.setEmpty(); DefaultSelection.setEmpty(); BackgroundName.setEmpty(); BackgroundScale = imNone; // SCALING BackgroundSharp = 0; BackgroundDark = FALSE; //TODO should be set to true if Night theme // CustomIcons = FALSE; //TODO don't know how to handle with SVG theme SelectionOnTop = FALSE; BootCampStyle = FALSE; BadgeOffsetX = 0xFFFF; //default offset BadgeOffsetY = 0xFFFF; BadgeScale = 4; // TODO now we have float scale = BadgeScale/16 ThemeDesignWidth = 0xFFFF; ThemeDesignHeight = 0xFFFF; BannerPosX = 0xFFFF; // the value out of range [0,1000] means default BannerPosY = 0xFFFF; BannerEdgeHorizontal = 0; BannerEdgeVertical = 0; BannerNudgeX = 0; BannerNudgeY = 0; BanHeight = 0; VerticalLayout = FALSE; NonSelectedGrey = FALSE; MainEntriesSize = 128; TileXSpace = 8; TileYSpace = 24; Proportional = FALSE; // ShowOptimus = FALSE; // DarkEmbedded = FALSE; //looks like redundant, we always check Night or Daylight TypeSVG = FALSE; // Codepage = 0xC0; //this is for PNG theme // CodepageSize = 0xC0; // INTN CodepageSize; //extended latin Scale = 1.0f; CentreShift = 0.0f; Daylight = true; LayoutHeight = 376; LayoutBannerOffset = 64; //default value if not set LayoutButtonOffset = 0; //default value if not set LayoutTextOffset = 0; //default value if not set LayoutAnimMoveForMenuX = 0; //default value if not set row0TileSize = 144; row1TileSize = 64; FontWidth = 9; FontHeight = 18; TextHeight = 19; Cinema.setEmpty(); } /* * what if the icon is not found or name is wrong? * probably it whould return Empty image * Image.isEmpty() == true */ //const XImage& XTheme::GetIcon(const char* Name) //{ // return GetIcon(XString().takeValueFrom(Name)); //} // //const XImage& XTheme::GetIcon(const CHAR16* Name) //{ // return GetIcon(XString().takeValueFrom(Name)); //} static XImage NullImage; static XIcon DummyIcon; static XIcon NullIcon; const XIcon& XTheme::GetIcon(const XString8& Name) { for (size_t i = 0; i < Icons.size(); i++) { if (Icons[i].Name == Name) //night icon has same name as daylight icon { return GetIcon(Icons[i].Id); } } return NullIcon; //if name is not found } XIcon* XTheme::GetIconP(const XString8& Name) { for (size_t i = 0; i < Icons.size(); i++) { if (Icons[i].Name == Name) //night icon has same name as daylight icon { return GetIconP(Icons[i].Id); } } return &NullIcon; //if name is not found } bool XTheme::CheckNative(INTN Id) { for (size_t i = 0; i < Icons.size(); i++) { if (Icons[i].Id == Id) { return Icons[i].Native; } } return false; } const XIcon& XTheme::GetIcon(INTN Id) { return GetIconAlt(Id, -1); } XIcon* XTheme::GetIconP(INTN Id) { return &GetIconAlt(Id, -1); } /* * Get Icon with this ID=id, for example VOL_INTERNAL_HFS * if not found then search for ID=Alt with Native attribute set, for example VOL_INTERNAL * if not found then check embedded with ID=Id * if not found then check embedded with ID=Alt */ XIcon& XTheme::GetIconAlt(INTN Id, INTN Alt) //if not found then take embedded { INTN IdFound = -1; INTN AltFound = -1; for (size_t i = 0; i < Icons.size() && (IdFound < 0 || (Alt >= 0 && AltFound < 0)); i++) { if (Icons[i].Id == Id) { IdFound = i; } if (Icons[i].Id == Alt) { AltFound = i; } } // if icon is empty, try to fill it with alternative if (IdFound >= 0 && Icons[IdFound].Image.isEmpty()) { // check for native ID=Alt, if Alt was specified if (Alt >= 0 && AltFound >= 0 && Icons[AltFound].Native && !Icons[AltFound].Image.isEmpty()) { // using Alt icon Icons[IdFound].Image = Icons[AltFound].Image; Icons[IdFound].ImageNight = Icons[AltFound].ImageNight; Icons[IdFound].setFilled(); } else { // check for embedded with ID=Id XIcon *NewIcon = new XIcon(Id, true); if (NewIcon->Image.isEmpty()) { // check for embedded with ID=Alt NewIcon = new XIcon(Alt, true); } if (!NewIcon->Image.isEmpty()) { // using Embedded icon Icons[IdFound].Image = NewIcon->Image; Icons[IdFound].ImageNight = NewIcon->ImageNight; Icons[IdFound].setFilled(); } } } if (IdFound >= 0 && !Icons[IdFound].Image.isEmpty()) { // icon not empty, return it // if (!Daylight && !Icons[IdFound].ImageNight.isEmpty()) { // DBG("got night icon %lld name{%s}\n", Id, IconsNames[IdFound]); // return Icons[IdFound].ImageNight; // } //if daylight or night icon absent // DBG("got day icon %lld name{%s}\n", Id, IconsNames[IdFound]); // return Icons[IdFound].Image; return Icons[IdFound]; //check daylight at draw } return NullIcon; //such Id is not found in the database } const XIcon& XTheme::LoadOSIcon(const CHAR16* OSIconName) { return LoadOSIcon(XString8().takeValueFrom(OSIconName)); } const XIcon& XTheme::LoadOSIcon(const XString8& Full) { // input value can be L"win", L"ubuntu,linux", L"moja,mac" set by GetOSIconName (OSVersion) XString8 First; XString8 Second; XString8 Third; const XIcon *ReturnIcon; UINTN Comma = Full.indexOf(','); UINTN Size = Full.length(); DBG("IconName=%s comma=%lld size=%lld\n", Full.c_str(), Comma, Size); if (Comma != MAX_XSIZE) { //Comma First = "os_"_XS8 + Full.subString(0, Comma); ReturnIcon = &GetIcon(First); DBG(" first=%s\n", First.c_str()); if (!ReturnIcon->isEmpty()) return *ReturnIcon; //else search second name Second = "os_"_XS8 + Full.subString(Comma + 1, Size - Comma - 1); //moreover names can be triple L"chrome,grub,linux" UINTN SecondComma = Second.indexOf(','); if (Comma == MAX_XSIZE) { ReturnIcon = &GetIcon(Second); if (!ReturnIcon->isEmpty()) return *ReturnIcon; } else { First = Second.subString(0, SecondComma); ReturnIcon = &GetIcon(First); if (!ReturnIcon->isEmpty()) return *ReturnIcon; Third = "os_"_XS8 + Second.subString(SecondComma + 1, Size - SecondComma - 1); ReturnIcon = &GetIcon(Third); if (!ReturnIcon->isEmpty()) return *ReturnIcon; } DBG(" Second=%s\n", Second.c_str()); if (!ReturnIcon->isEmpty()) return *ReturnIcon; } else { ReturnIcon = &GetIcon("os_"_XS8 + Full); DBG(" Full=%s\n", Full.c_str()); if (!ReturnIcon->isEmpty()) return *ReturnIcon; } // else something if (DummyIcon.isEmpty()) { //initialize once per session DummyIcon.Image.DummyImage(MainEntriesSize); DummyIcon.setFilled(); } return DummyIcon; } void XTheme::FillByEmbedded() { embedded = true; Theme.takeValueFrom("embedded"); SelectionColor = 0xA0A0A080; SelectionBackgroundPixel = { 0xa0, 0xa0, 0xa0, 0x80 }; Icons.Empty(); for (INTN i = 0; i < BUILTIN_ICON_COUNT; ++i) { //this is embedded icon count XIcon* NewIcon = new XIcon(i, true); Icons.AddReference(NewIcon, true); } BigBack.setEmpty(); Background = XImage(UGAWidth, UGAHeight); if (Daylight) { Banner.FromPNG(ACCESS_EMB_DATA(emb_logo), emb_logo_size); } else { Banner.FromPNG(ACCESS_EMB_DATA(emb_dark_logo), emb_dark_logo_size); } //and buttons Buttons[0].FromPNG(ACCESS_EMB_DATA(emb_radio_button), ACCESS_EMB_SIZE(emb_radio_button)); Buttons[1].FromPNG(ACCESS_EMB_DATA(emb_radio_button_selected), ACCESS_EMB_SIZE(emb_radio_button_selected)); Buttons[2].FromPNG(ACCESS_EMB_DATA(emb_checkbox), ACCESS_EMB_SIZE(emb_checkbox)); Buttons[3].FromPNG(ACCESS_EMB_DATA(emb_checkbox_checked), ACCESS_EMB_SIZE(emb_checkbox_checked)); if (Daylight) { SelectionImages[0].FromPNG(ACCESS_EMB_DATA(emb_selection_big), ACCESS_EMB_SIZE(emb_selection_big)); SelectionImages[2].FromPNG(ACCESS_EMB_DATA(emb_selection_small), ACCESS_EMB_SIZE(emb_selection_small)); } else { SelectionImages[0].FromPNG(ACCESS_EMB_DATA(emb_dark_selection_big), ACCESS_EMB_SIZE(emb_dark_selection_big)); SelectionImages[2].FromPNG(ACCESS_EMB_DATA(emb_dark_selection_small), ACCESS_EMB_SIZE(emb_dark_selection_small)); } SelectionImages[4].FromPNG(ACCESS_EMB_DATA(emb_selection_indicator), ACCESS_EMB_SIZE(emb_selection_indicator)); } void XTheme::ClearScreen() //and restore background and banner { EFI_GRAPHICS_OUTPUT_BLT_PIXEL FirstBannerPixel = MenuBackgroundPixel; if (BanHeight < 2) { BanHeight = ((UGAHeight - (int)(LayoutHeight * Scale)) >> 1); } if (!(HideUIFlags & HIDEUI_FLAG_BANNER)) { //Banner image prepared before if (!Banner.isEmpty()) { FirstBannerPixel = Banner.GetPixel(0,0); BannerPlace.Width = Banner.GetWidth(); BannerPlace.Height = (BanHeight >= Banner.GetHeight()) ? Banner.GetHeight() : BanHeight; BannerPlace.XPos = BannerPosX; BannerPlace.YPos = BannerPosY; if (!TypeSVG) { // Check if new style placement value was used for banner in theme.plist if ((BannerPosX >=0 && BannerPosX <=1000) && (BannerPosY >=0 && BannerPosY <=1000)) { // Check if screen size being used is different from theme origination size. // If yes, then recalculate the placement % value. // This is necessary because screen can be a different size, but banner is not scaled. BannerPlace.XPos = HybridRepositioning(BannerEdgeHorizontal, BannerPosX, BannerPlace.Width, UGAWidth, ThemeDesignWidth ); BannerPlace.YPos = HybridRepositioning(BannerEdgeVertical, BannerPosY, BannerPlace.Height, UGAHeight, ThemeDesignHeight); // Check if banner is required to be nudged. BannerPlace.XPos = CalculateNudgePosition(BannerPlace.XPos, BannerNudgeX, Banner.GetWidth(), UGAWidth); BannerPlace.YPos = CalculateNudgePosition(BannerPlace.YPos, BannerNudgeY, Banner.GetHeight(), UGAHeight); // DBG("banner position new style\n"); } else { // Use rEFIt default (no placement values speicifed) BannerPlace.XPos = (UGAWidth >= Banner.GetWidth() ) ? (UGAWidth - Banner.GetWidth() ) >> 1 : 0; BannerPlace.YPos = (BanHeight >= Banner.GetHeight()) ? (BanHeight - Banner.GetHeight()) : 0; // DBG("banner position old style\n"); } } } } DBG("BannerPlace at Clear Screen [%lld,%lld]\n", BannerPlace.XPos, BannerPlace.YPos); //Then prepare Background from BigBack if (Background.GetWidth() != UGAWidth || Background.GetHeight() != UGAHeight) { // should we type UGAWidth and UGAHeight as UINTN to avoid cast ? // Resolution changed or empty background Background = XImage(UGAWidth, UGAHeight); } // now we are sure Background has UGA sizes float BigScale; float BigScaleY; if (!BigBack.isEmpty()) { switch (BackgroundScale) { case imScale: BigScale = (float)UGAWidth/BigBack.GetWidth(); BigScaleY = (float)UGAHeight/BigBack.GetHeight(); Background.CopyScaled(BigBack, MAX(BigScale, BigScaleY)); break; case imCrop: { INTN x = UGAWidth - BigBack.GetWidth(); INTN x1, x2, y1, y2; if (x >= 0) { x1 = x >> 1; x2 = 0; x = BigBack.GetWidth(); } else { x1 = 0; x2 = (-x) >> 1; x = UGAWidth; } INTN y = UGAHeight - BigBack.GetHeight(); if (y >= 0) { y1 = y >> 1; y2 = 0; y = BigBack.GetHeight(); } else { y1 = 0; y2 = (-y) >> 1; y = UGAHeight; } const EG_RECT BackRect = EG_RECT(x1, y1, x, y); const EG_RECT BigRect = EG_RECT(x2, y2, x, y); // DBG("crop to x,y: %lld, %lld\n", x, y); Background.CopyRect(BigBack, BackRect, BigRect); break; } case imTile: { INTN x = (BigBack.GetWidth() * ((UGAWidth - 1) / BigBack.GetWidth() + 1) - UGAWidth) >> 1; INTN y = (BigBack.GetHeight() * ((UGAHeight - 1) / BigBack.GetHeight() + 1) - UGAHeight) >> 1; EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p1 = Background.GetPixelPtr(0, 0); for (INTN j = 0; j < UGAHeight; j++) { for (INTN i = 0; i < UGAWidth; i++) { *p1++ = BigBack.GetPixel((i + x) % BigBack.GetWidth(), (j + y) % BigBack.GetHeight()); } } // DBG("back copy tiled\n"); break; } case imNone: default: // already scaled Background = BigBack; // DBG("back copy equal\n"); break; } } else { // no background loaded, fill by default if (!embedded) { BlueBackgroundPixel = FirstBannerPixel; } else if (Daylight) { // embedded light BlueBackgroundPixel = StdBackgroundPixel; } else { // embedded dark BlueBackgroundPixel = DarkEmbeddedBackgroundPixel; } Background.Fill(BlueBackgroundPixel); //blue opaque. May be better to set black opaque? } //join Banner and Background for menu drawing if (!Banner.isEmpty()) { Background.Compose(BannerPlace.XPos, BannerPlace.YPos, Banner, true); } Background.DrawWithoutCompose(0, 0, UGAWidth, UGAHeight); } //use this only for PNG theme void XTheme::FillByDir() //assume ThemeDir is defined by InitTheme() procedure { EFI_STATUS Status; Icons.Empty(); for (INTN i = 0; i < IconsNamesSize; ++i) { //scan full table Status = EFI_NOT_FOUND; XIcon* NewIcon = new XIcon(i); //initialize without embedded switch (i) { case BUILTIN_SELECTION_SMALL: Status = NewIcon->Image.LoadXImage(ThemeDir, SelectionSmallFileName); break; case BUILTIN_SELECTION_BIG: Status = NewIcon->Image.LoadXImage(ThemeDir, SelectionBigFileName); break; } if (EFI_ERROR(Status)) { Status = NewIcon->Image.LoadXImage(ThemeDir, IconsNames[i]); } NewIcon->Native = !EFI_ERROR(Status); if (!EFI_ERROR(Status)) { NewIcon->setFilled(); NewIcon->ImageNight.LoadXImage(ThemeDir, SWPrintf("%s_night", IconsNames[i])); } Icons.AddReference(NewIcon, true); if (EFI_ERROR(Status)) { if (i >= BUILTIN_ICON_VOL_INTERNAL_HFS && i <= BUILTIN_ICON_VOL_INTERNAL_REC) { // call to GetIconAlt will get alternate/embedded into Icon if missing GetIconAlt(i, BUILTIN_ICON_VOL_INTERNAL); } else if (i == BUILTIN_SELECTION_BIG) { GetIconAlt(i, BUILTIN_SELECTION_SMALL); } } } if (BootCampStyle) { XIcon *NewIcon = new XIcon(BUILTIN_ICON_SELECTION); // load indicator selection image Status = NewIcon->Image.LoadXImage(ThemeDir, SelectionIndicatorName); if (EFI_ERROR(Status)) { Status = NewIcon->Image.LoadXImage(ThemeDir, "selection_indicator"); } Icons.AddReference(NewIcon, true); } SelectionBackgroundPixel.Red = (SelectionColor >> 24) & 0xFF; SelectionBackgroundPixel.Green = (SelectionColor >> 16) & 0xFF; SelectionBackgroundPixel.Blue = (SelectionColor >> 8) & 0xFF; SelectionBackgroundPixel.Reserved = (SelectionColor >> 0) & 0xFF; //TODO - make them XIcon SelectionImages[2] = *GetIconP(BUILTIN_SELECTION_SMALL)->GetBest(!Daylight); SelectionImages[0] = *GetIconP(BUILTIN_SELECTION_BIG)->GetBest(!Daylight); if (BootCampStyle) { SelectionImages[4] = *GetIconP(BUILTIN_ICON_SELECTION)->GetBest(!Daylight); } //and buttons Buttons[0] = *GetIconP(BUILTIN_RADIO_BUTTON)->GetBest(!Daylight); Buttons[1] = *GetIconP(BUILTIN_RADIO_BUTTON_SELECTED)->GetBest(!Daylight); Buttons[2] = *GetIconP(BUILTIN_CHECKBOX)->GetBest(!Daylight); Buttons[3] = *GetIconP(BUILTIN_CHECKBOX_CHECKED)->GetBest(!Daylight); //load banner and background Banner.LoadXImage(ThemeDir, BannerFileName); Status = BigBack.LoadXImage(ThemeDir, BackgroundName); if (EFI_ERROR(Status) && !Banner.isEmpty()) { //take first pixel from banner const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& firstPixel = Banner.GetPixel(0,0); BigBack.setSizeInPixels(UGAWidth, UGAHeight); BigBack.Fill(firstPixel); } } void XTheme::InitBar() { if (!TypeSVG) { ScrollbarBackgroundImage.LoadXImage(ThemeDir, "scrollbar\\bar_fill"); BarStartImage.LoadXImage(ThemeDir, "scrollbar\\bar_start"); BarEndImage.LoadXImage(ThemeDir, "scrollbar\\bar_end"); ScrollbarImage.LoadXImage(ThemeDir, "scrollbar\\scroll_fill"); ScrollStartImage.LoadXImage(ThemeDir, "scrollbar\\scroll_start"); ScrollEndImage.LoadXImage(ThemeDir, "scrollbar\\scroll_end"); UpButtonImage.LoadXImage(ThemeDir, "scrollbar\\up_button"); DownButtonImage.LoadXImage(ThemeDir, "scrollbar\\down_button"); } else { ScrollbarBackgroundImage = *GetIconP("scrollbar_background"_XS8)->GetBest(!Daylight); BarStartImage.setEmpty(); BarEndImage.setEmpty(); ScrollbarImage = *GetIconP("scrollbar_holder"_XS8)->GetBest(!Daylight); //"_night" is already accounting ScrollStartImage = *GetIconP("scrollbar_start"_XS8)->GetBest(!Daylight); ScrollEndImage = *GetIconP("scrollbar_end"_XS8)->GetBest(!Daylight); UpButtonImage = *GetIconP("scrollbar_up_button"_XS8)->GetBest(!Daylight); DownButtonImage = *GetIconP("scrollbar_down_button"_XS8)->GetBest(!Daylight); } //some help with embedded scroll if (!TypeSVG) { // fill these from embedded only for non-svg // Question: why we don't want these for svg? (upbutton, downbutton, scrollstart, scrollend - also have hardcoded 0 height in REFIT_MENU_SCREEN.cpp) if (BarStartImage.isEmpty()) { BarStartImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_bar_start), ACCESS_EMB_SIZE(emb_scroll_bar_start)); } if (BarEndImage.isEmpty()) { BarEndImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_bar_end), ACCESS_EMB_SIZE(emb_scroll_bar_end)); } if (ScrollStartImage.isEmpty()) { ScrollStartImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_scroll_start), ACCESS_EMB_SIZE(emb_scroll_scroll_start)); } if (ScrollEndImage.isEmpty()) { ScrollEndImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_scroll_end), ACCESS_EMB_SIZE(emb_scroll_scroll_end)); } if (UpButtonImage.isEmpty()) { UpButtonImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_up_button), ACCESS_EMB_SIZE(emb_scroll_up_button)); } if (DownButtonImage.isEmpty()) { DownButtonImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_down_button), ACCESS_EMB_SIZE(emb_scroll_down_button)); } } // fill these from embedded for both svg and non-svg if (ScrollbarBackgroundImage.isEmpty()) { ScrollbarBackgroundImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_bar_fill), ACCESS_EMB_SIZE(emb_scroll_bar_fill)); } if (ScrollbarImage.isEmpty()) { ScrollbarImage.FromPNG(ACCESS_EMB_DATA(emb_scroll_scroll_fill), ACCESS_EMB_SIZE(emb_scroll_scroll_fill)); } } //the purpose of the procedure is restore Background in rect //XAlign is always centre, Color is the Backgrounf fill //TODO replace by some existing procedure VOID XTheme::FillRectAreaOfScreen(IN INTN XPos, IN INTN YPos, IN INTN Width, IN INTN Height) { // TmpBuffer.CopyScaled(Background, 1.f); INTN X = XPos - (Width >> 1); //X_IS_CENTRE if (X < 0) { X = 0; } if (X + Width > UGAWidth) { Width = (X > UGAWidth) ? 0 : (UGAWidth - X); } if (YPos + Height > UGAHeight) { Height = (YPos > UGAHeight) ? 0 : (UGAHeight - YPos); } XImage TmpBuffer(Width, Height); TmpBuffer.CopyRect(Background, X, YPos); //a part of BackGround image TmpBuffer.DrawWithoutCompose(X, YPos); // TmpBuffer.Draw(X, YPos, 0, true); }