implemented LoadXImage

Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
This commit is contained in:
Sergey Isakov 2020-03-19 21:35:58 +03:00
parent c64788a19e
commit 054898eef3
4 changed files with 210 additions and 72 deletions

View File

@ -294,13 +294,16 @@ void XImage::FlipRB(bool WantAlpha)
* Error = 0 - Success
* Error = 28 - invalid signature
*/
unsigned XImage::FromPNG(const UINT8 * Data, UINTN Length)
EFI_STATUS XImage::FromPNG(const UINT8 * Data, UINTN Length)
{
if (Data == NULL) return EFI_INVALID_PARAMETER;
UINT8 * PixelPtr = (UINT8 *)&PixelData[0];
unsigned Error = eglodepng_decode(&PixelPtr, &Width, &Height, Data, Length);
if (Error != 0 && Error != 28) {
return EFI_NOT_FOUND;
}
FlipRB(true);
return Error;
return EFI_SUCCESS;
}
/*
@ -309,31 +312,33 @@ unsigned XImage::FromPNG(const UINT8 * Data, UINTN Length)
* The caller is responsible to free the array.
*/
unsigned XImage::ToPNG(UINT8** Data, UINTN& OutSize)
EFI_STATUS XImage::ToPNG(UINT8** Data, UINTN& OutSize)
{
size_t FileDataLength = 0;
FlipRB(false);
UINT8 * PixelPtr = (UINT8 *)&PixelData[0];
unsigned Error = eglodepng_encode(Data, &FileDataLength, PixelPtr, Width, Height);
OutSize = FileDataLength;
return Error;
if (Error) return EFI_UNSUPPORTED;
return EFI_SUCCESS;
}
/*
* fill XImage object by raster data described in SVG
* fill XImage object by raster data described in SVG file
* caller should create the object with Width and Height and calculate scale
* scale = 1 correspond to fill the rect with the image
* scale = 0.5 will reduce image
* scale = 0.5 will reduce image
* but this procedure is mostly for testing purpose. Real SVG theme can't be divided to separate SVG files
*/
unsigned XImage::FromSVG(const CHAR8 *SVGData, float scale)
EFI_STATUS XImage::FromSVG(const CHAR8 *SVGData, float scale)
{
NSVGimage *SVGimage;
NSVGparser* p;
NSVGrasterizer* rast = nsvgCreateRasterizer();
if (!rast) return 1;
if (!rast) return EFI_UNSUPPORTED;
char *input = (__typeof__(input))AllocateCopyPool(AsciiStrSize(SVGData), SVGData);
if (!input) return 2;
if (!input) return EFI_DEVICE_ERROR;
p = nsvgParse(input, 72, 1.f); //the parse will change input contents
SVGimage = p->image;
@ -350,7 +355,7 @@ unsigned XImage::FromSVG(const CHAR8 *SVGData, float scale)
// nsvg__deleteParser(p); //can't delete raster until we make imageChain
nsvgDeleteRasterizer(rast);
FreePool(input);
return 0;
return EFI_SUCCESS;
}
// Screen operations
@ -491,30 +496,59 @@ void XImage::Draw(INTN x, INTN y, float scale)
}
}
EFI_STATUS XImage::LoadXImage(EFI_FILE *BaseDir, const XStringW& FileName)
/*
* IconName is just func_about for example
* will search files
* icons/iconname.icns - existing themes
* icons/iconname.png - it will be more correct
* iconname.png - for example checkbox.png
* if not found use embedded. It should be decoded again after theme change
* SVG themes filled separately after ThemeName defined so the procedure just return EFI_SUCCESS
* The function always create new image and will not be used to get a link to existing image
*/
EFI_STATUS XImage::LoadXImage(EFI_FILE *BaseDir, const char* IconName)
{
return LoadXImage(BaseDir, XStringWP(IconName));
}
//dont call this procedure for SVG theme BaseDir == NULL?
EFI_STATUS XImage::LoadXImage(EFI_FILE *BaseDir, const XStringW& IconName)
{
EFI_STATUS Status = EFI_NOT_FOUND;
UINT8 *FileData = NULL;
UINTN FileDataLength = 0;
// if (TypeSVG) {
// if (TypeSVG) { //make a copy of SVG image
// XImage NewImage = Theme.GetIcon(IconName);
// setSizeInPixels(NewImage.GetWidth(), NewImage.GetHeight());
// CopyMem(&PixelData[0], &NewImage.PixelData[0], GetSizeInBytes());
// return EFI_SUCCESS;
// }
if (BaseDir == NULL || FileName.isEmpty())
if (BaseDir == NULL || IconName.isEmpty())
return EFI_NOT_FOUND;
// load file
XStringW FileName = L"icons\\" + IconName + L".icns";
Status = egLoadFile(BaseDir, FileName.data(), &FileData, &FileDataLength);
if (EFI_ERROR(Status))
return Status;
if (EFI_ERROR(Status)) {
FileName = L"icons\\" + IconName + L".png";
Status = egLoadFile(BaseDir, FileName.data(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
FileName = IconName + L".png";
if (EFI_ERROR(Status)) {
FileName = IconName; //may be it already contain extension, for example Logo.png
Status = egLoadFile(BaseDir, FileName.data(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
return Status;
}
}
}
}
// decode it
unsigned ret = FromPNG(FileData, FileDataLength);
if (ret) {
DBG("%s not decoded\n", FileName.data());
Status = EFI_UNSUPPORTED;
Status = FromPNG(FileData, FileDataLength);
if (EFI_ERROR(Status)) {
DBG("%s not decoded\n", IconName.data());
}
FreePool(FileData);
return Status;

View File

@ -84,9 +84,9 @@ public:
void CopyScaled(const XImage& Image, float scale);
void Compose(INTN PosX, INTN PosY, const XImage& TopImage, bool Lowest); //instead of compose we often can Back.Draw(...) + Top.Draw(...)
void FlipRB(bool WantAlpha);
unsigned FromPNG(const UINT8 * Data, UINTN Lenght);
unsigned ToPNG(UINT8** Data, UINTN& OutSize);
unsigned FromSVG(const CHAR8 *SVGData, float scale);
EFI_STATUS FromPNG(const UINT8 * Data, UINTN Lenght);
EFI_STATUS ToPNG(UINT8** Data, UINTN& OutSize);
EFI_STATUS FromSVG(const CHAR8 *SVGData, float scale);
void GetArea(const EG_RECT& Rect);
void GetArea(INTN x, INTN y, UINTN W, UINTN H);
void Draw(INTN x, INTN y, float scale);
@ -94,6 +94,7 @@ public:
//I changed the name because LoadImage is too widely used
// will be used instead of old egLoadImage
EFI_STATUS LoadXImage(EFI_FILE *Dir, const XStringW& FileName); //for example LoadImage(ThemeDir, L"icons\\" + Name);
EFI_STATUS LoadXImage(EFI_FILE *BaseDir, const char* IconName);
void EnsureImageSize(IN UINTN Width, IN UINTN Height, IN CONST EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color);
};

View File

@ -59,9 +59,9 @@ CONST CHAR8* IconsNames[] = {
"os_freedos",
"os_win",
"os_vista",
"radio_button",
"radio_button", //20
"radio_button_selected",
"checkbox",
"checkbox", //22
"checkbox_checked",
"scrollbar_background", //24
"scrollbar_holder"
@ -184,44 +184,106 @@ void XTheme::AddIcon(Icon& NewIcon)
//if ImageNight is not set then Image should be used
#define DEC_BUILTIN_ICON(id, ico) { \
Icon NewIcon(id); \
NewIcon.Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico)); \
Icons.AddCopy(NewIcon); \
Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico)); \
}
#define DEC_BUILTIN_ICON2(id, ico, dark) { \
Icon NewIcon(id); \
NewIcon.Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico)); \
NewIcon.ImageNight.FromPNG(ACCESS_EMB_DATA(dark), ACCESS_EMB_SIZE(dark)); \
Icons.AddCopy(NewIcon); \
Image.FromPNG(ACCESS_EMB_DATA(ico), ACCESS_EMB_SIZE(ico)); \
ImageNight.FromPNG(ACCESS_EMB_DATA(dark), ACCESS_EMB_SIZE(dark)); \
}
Icon::Icon(INTN Index, BOOLEAN Embedded) : Image(0), ImageNight(0)
{
Id = Index;
if (Index < BUILTIN_ICON_FUNC_ABOUT || Index >=BUILTIN_ICON_COUNT || !Embedded) {
Name.setEmpty();
return;
}
Name.takeValueFrom(IconsNames[Index]);
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;
default:
Name.setEmpty();
break;
}
//something to do else?
}
void XTheme::FillByEmbedded()
{
DEC_BUILTIN_ICON2(0, emb_func_about, emb_dark_func_about)
DEC_BUILTIN_ICON2(1, emb_func_options, emb_dark_func_options)
DEC_BUILTIN_ICON2(2, emb_func_clover, emb_dark_func_clover)
DEC_BUILTIN_ICON2(3, emb_func_secureboot, emb_dark_func_secureboot)
DEC_BUILTIN_ICON2(4, emb_func_secureboot_config, emb_dark_func_secureboot_config)
DEC_BUILTIN_ICON2(5, emb_func_reset, emb_dark_func_reset)
DEC_BUILTIN_ICON2(6, emb_func_exit, emb_dark_func_exit)
DEC_BUILTIN_ICON2(7, emb_func_help, emb_dark_func_help)
DEC_BUILTIN_ICON2(8, emb_func_shell, emb_dark_func_shell)
DEC_BUILTIN_ICON(11, emb_pointer)
DEC_BUILTIN_ICON(12, emb_vol_internal)
DEC_BUILTIN_ICON(13, emb_vol_external)
DEC_BUILTIN_ICON(14, emb_vol_optical)
DEC_BUILTIN_ICON(16, emb_vol_internal_booter)
DEC_BUILTIN_ICON(17, emb_vol_internal_hfs)
DEC_BUILTIN_ICON(18, emb_vol_internal_apfs)
DEC_BUILTIN_ICON(19, emb_vol_internal_ntfs)
DEC_BUILTIN_ICON(20, emb_vol_internal_ext)
DEC_BUILTIN_ICON(21, emb_vol_internal_recovery)
DEC_BUILTIN_ICON2(22, emb_logo, emb_dark_logo)
DEC_BUILTIN_ICON2(23, emb_selection_small, emb_dark_selection_small)
DEC_BUILTIN_ICON2(24, emb_selection_big, emb_dark_selection_big)
for (INTN i = 0; i < BUILTIN_ICON_COUNT; ++i) {
Icon NewIcon(i, true);
Icons.AddCopy(NewIcon);
}
}
void XTheme::ClearScreen() //and restore background and banner
@ -333,20 +395,21 @@ void XTheme::ClearScreen() //and restore background and banner
void XTheme::InitSelection()
{
EFI_STATUS Status;
if (!AllowGraphicsMode)
return;
//used to fill TextBuffer if selected
SelectionBackgroundPixel.r = (SelectionColor >> 24) & 0xFF;
SelectionBackgroundPixel.g = (SelectionColor >> 16) & 0xFF;
SelectionBackgroundPixel.b = (SelectionColor >> 8) & 0xFF;
SelectionBackgroundPixel.a = (SelectionColor >> 0) & 0xFF;
if (!SelectionImages[0].isEmpty()) {
if (!SelectionImages[0].isEmpty()) { //already presents
return;
}
// load small selection image
if (SelectionSmallFileName.isEmpty()){
SelectionImages[2].LoadImage(ThemeDir, SelectionSmallFileName);
SelectionImages[2].LoadXImage(ThemeDir, SelectionSmallFileName);
}
if (SelectionImages[2].isEmpty()){
// SelectionImages[2] = BuiltinIcon(BUILTIN_SELECTION_SMALL);
@ -367,7 +430,7 @@ void XTheme::InitSelection()
// load big selection image
if (!TypeSVG && !SelectionBigFileName.isEmpty()) {
SelectionImages[0].LoadImage(ThemeDir, SelectionBigFileName);
SelectionImages[0].LoadXImage(ThemeDir, SelectionBigFileName);
// SelectionImages[0].EnsureImageSize(row0TileSize, row0TileSize, &MenuBackgroundPixel);
}
if (SelectionImages[0].isEmpty()) {
@ -394,7 +457,7 @@ void XTheme::InitSelection()
if (BootCampStyle) {
// load indicator selection image
if (!SelectionIndicatorName.isEmpty()) {
SelectionImages[4].LoadImage(ThemeDir, SelectionIndicatorName);
SelectionImages[4].LoadXImage(ThemeDir, SelectionIndicatorName);
}
if (!SelectionImages[4].isEmpty()) {
SelectionImages[4].FromPNG(ACCESS_EMB_DATA(emb_selection_indicator), ACCESS_EMB_SIZE(emb_selection_indicator));
@ -427,11 +490,30 @@ void XTheme::InitSelection()
//it was a nonsense egLoadImage is just inluded into egLoadIcon.
// will be corrected with XTheme support
//the procedure loadIcon should also check embedded icons
//DECLARE_EMB_EXTERN_WITH_SIZE(emb_radio_button_selected)
//DECLARE_EMB_EXTERN_WITH_SIZE(emb_radio_button)
//DECLARE_EMB_EXTERN_WITH_SIZE(emb_checkbox)
//DECLARE_EMB_EXTERN_WITH_SIZE(emb_checkbox_checked)
Button[0] = GetIcon("radio_button");
Button[1] = GetIcon("radio_button_selected"));
Button[2] = GetIcon("checkbox");
Button[3] = GetIcon("checkbox_checked");
DECLARE_EMB_EXTERN_WITH_SIZE(emb_dark_font_data)
Status = Button[0].LoadXImage(ThemeDir, "radio_button");
if (EFI_ERROR(Status)) {
Button[0].FromPNG(ACCESS_EMB_DATA(emb_radio_button), ACCESS_EMB_SIZE(emb_radio_button));
}
Status = Button[1].LoadXImage(ThemeDir, "radio_button_selected"));
if (EFI_ERROR(Status)) {
Button[0].FromPNG(ACCESS_EMB_DATA(emb_radio_button_selected), ACCESS_EMB_SIZE(emb_radio_button_selected));
}
Status = Button[2].LoadXImage(ThemeDir, "checkbox");
if (EFI_ERROR(Status)) {
Button[0].FromPNG(ACCESS_EMB_DATA(emb_checkbox), ACCESS_EMB_SIZE(emb_checkbox));
}
Status = Button[3].LoadXImage(ThemeDir, "checkbox_checked");
if (EFI_ERROR(Status)) {
Button[0].FromPNG(ACCESS_EMB_DATA(emb_checkbox_checked), ACCESS_EMB_SIZE(emb_checkbox_checked));
}
// non-selected background images
@ -450,13 +532,25 @@ void XTheme::InitSelection()
}
//use this only for PNG theme
void XTheme::FillByDir() //assume ThemeDir is defined by InitTheme() procedure
{
for (INTN i=0; i<BUILTIN_ICON_COUNT; ++i) {
Icon NewIcon(i);
NewIcon.Image.LoadImage(ThemeDir, XStringWP(IconsNames[i]));
NewIcon.ImageNight.LoadImage(ThemeDir, XStringWP(IconsNames[i]) + "_night");
for (INTN i = 0; i < BUILTIN_ICON_COUNT; ++i) {
Icon NewIcon(i, true); //initialize with embedded but further replace by loaded
NewIcon.Image.LoadXImage(ThemeDir, IconsNames[i]));
NewIcon.ImageNight.LoadXImage(ThemeDir, XStringWP(IconsNames[i]) + "_night");
Icons.AddCopy(NewIcon);
}
//to be continued
for (INTN i = BUILTIN_ICON_COUNT; i < 45; ++i) {
Icon NewIcon(i); //there is no embedded
NewIcon.Image.LoadXImage(ThemeDir, IconsNames[i])); //all os_***
Icons.AddCopy(NewIcon);
}
InitSelection(); //initialize selections, buttons
//load banner
Banner.LoadXImage(ThemeDir, BannerFileName);
}

View File

@ -637,7 +637,16 @@ EFI_STATUS egScreenShot(VOID)
//convert to PNG
UINT8 *FileData = NULL;
UINTN FileDataLength = 0U;
Screen.ToPNG(&FileData, FileDataLength);
Status = Screen.ToPNG(&FileData, FileDataLength);
if (EFI_ERROR(Status)) {
if (FileData != NULL) {
FreePool(FileData);
}
return Status;
}
if (!FileData) {
return EFI_NOT_READY;
}
//save file with a first unoccupied name
XStringWP CommonName(L"EFI\\CLOVER\\misc\\screenshot");
for (UINTN Index = 0; Index < 60; Index++) {