CloverBootloader/rEFIt_UEFI/libeg/XImage.cpp
jief 42cece9885 Fix nanosvg leaks.
Move global variable textfaces in XTheme.
Move global variable fontsDB in XTheme.
Remove XTheme member SVGParser. SVGParser is deleted just after use.
Remove XTheme members ImageSVG and ImageSVGnight. All images are
rasterized at load, so no need to keep that.
Remove XIcon setFilled because XIcon knows if it's filled or not by
checking Image & ImageNight
2023-11-08 14:35:22 +01:00

758 lines
25 KiB
C++

#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
#include "XImage.h"
#include "lodepng.h"
#include "nanosvg.h"
#include "libegint.h" //for egDecodeIcns
#include "../refit/lib.h"
#include "../Platform/Settings.h"
#ifndef DEBUG_ALL
#define DEBUG_XIMAGE 1
#else
#define DEBUG_XIMAGE DEBUG_ALL
#endif
#if DEBUG_XIMAGE == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_XIMAGE, __VA_ARGS__)
#endif
EFI_GRAPHICS_OUTPUT_BLT_PIXEL NullColor = {0,0,0,0};
XImage NullXImage;
XImage::XImage(UINTN W, UINTN H) : Width(0), Height(0), PixelData() // initialisation of Width and Height and , PixelData() to avoid warning with -Weffc++
{
// Width = W;
// Height = H; //included below
setSizeInPixels(W, H);
}
XImage& XImage::operator= (const XImage& other)
{
setSizeInPixels(other.GetWidth(), other.GetHeight()); // change the size, ie the number of element in the array. Reaalocate buffer if needed
PixelData = other.PixelData;
return *this;
}
XImage::XImage(const XImage& Image, float scale) : Width(0), Height(0), PixelData() // initialisation of Width and Height and , PixelData() to avoid warning with -Weffc++
{
UINTN SrcWidth = Image.GetWidth();
UINTN SrcHeight = Image.GetHeight();
if (scale < 1.e-4) {
// Width = SrcWidth;
// Height = SrcHeight;
setSizeInPixels(SrcWidth, SrcHeight);
for (UINTN y = 0; y < Height; ++y)
for (UINTN x = 0; x < Width; ++x)
PixelData[y * Width + x] = Image.GetPixel(x, y);
} else {
// Width = (UINTN)(SrcWidth * scale);
// Height = (UINTN)(SrcHeight * scale);
setSizeInPixels((UINTN)(SrcWidth * scale), (UINTN)(SrcHeight * scale));
CopyScaled(Image, scale);
}
}
#if 0
UINTN Offset = OFFSET_OF(EFI_GRAPHICS_OUTPUT_BLT_PIXEL, Blue);
dst.Blue = Smooth(&src.Blue, a01, a10, a11, a21, a12, dx, dy, scale);
#define SMOOTH(P) \
do { \
((PIXEL*)dst_ptr)->P = (BYTE)((a01.P * (cx - dx) * 3 + a10.P * (cy - dy) * 3 + \
a21.P * dx * 3 + a12.P * dy * 3 + a11.P * (cx + cy)) / ((cx + cy) * 4)); \
} while(0)
UINT x, y, z;
PIXEL a10, a11, a12, a01, a21;
int fx, cx, lx, dx, fy, cy, ly, dy;
fx = (dst_size->width << PRECISION) / src_size->width;
fy = (dst_size->height << PRECISION) / src_size->height;
if (!fx || !fy) {
return;
}
cx = ((fx - 1) >> PRECISION) + 1;
cy = ((fy - 1) >> PRECISION) + 1;
for (z = 0; z < dst_size->depth; z++)
{
BYTE * dst_slice_ptr = dst + z * dst_slice_pitch;
const BYTE *src_slice_ptr = src + src_slice_pitch * (z * src_size->depth / dst_size->depth);
for (y = 0; y < dst_size->height; y++)
{
BYTE * dst_ptr = dst_slice_ptr + y * dst_row_pitch;
const BYTE *src_row_ptr = src_slice_ptr + src_row_pitch * (y * src_size->height / dst_size->height);
ly = (y << PRECISION) / fy;
dy = y - ((ly * fy) >> PRECISION);
for (x = 0; x < dst_size->width; x++)
{
const BYTE *src_ptr = src_row_ptr + (x * src_size->width / dst_size->width) * src_format->bytes_per_pixel;
lx = (x << PRECISION) / fx;
dx = x - ((lx * fx) >> PRECISION);
a11 = *(PIXEL*)src_ptr;
a10 = (y == 0) ? a11 : (*(PIXEL*)(src_ptr - src_row_pitch));
a01 = (x == 0) ? a11 : (*(PIXEL*)(src_ptr - src_format->bytes_per_pixel));
a21 = (x == dst_size->width) ? a11 : (*(PIXEL*)(src_ptr + src_format->bytes_per_pixel));
a12 = (y == dst_size->height) ? a11 : (*(PIXEL*)(src_ptr + src_row_pitch));
SMOOTH(r);
SMOOTH(g);
SMOOTH(b);
SMOOTH(a);
dst_ptr += dst_format->bytes_per_pixel;
}
}
}
#endif
XImage::~XImage()
{
}
const XArray<EFI_GRAPHICS_OUTPUT_BLT_PIXEL>& XImage::GetData() const
{
return PixelData;
}
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* XImage::GetPixelPtr(INTN x, INTN y)
{
return &PixelData[x + y * Width];
}
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL* XImage::GetPixelPtr(INTN x, INTN y) const
{
return &PixelData[x + y * Width];
}
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& XImage::GetPixel(INTN x, INTN y) const
{
return PixelData[x + y * Width];
}
/*
UINTN XImage::GetWidth() const
{
return Width;
}
UINTN XImage::GetHeight() const
{
return Height;
}
*/
UINTN XImage::GetSizeInBytes() const
{
return PixelData.size() * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
}
void XImage::setSizeInPixels(UINTN W, UINTN H) //unused arguments?
{
Width = W;
Height = H;
PixelData.setSize(Width * Height);
}
void XImage::Fill(const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color)
{
for (UINTN y = 0; y < Height; ++y)
for (UINTN x = 0; x < Width; ++x)
PixelData[y * Width + x] = Color;
}
void XImage::Fill(const EG_PIXEL* Color)
{
Fill( *(const EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)Color );
}
void XImage::FillArea(const EG_PIXEL* Color, EG_RECT& Rect)
{
FillArea((const EFI_GRAPHICS_OUTPUT_BLT_PIXEL&)*Color, Rect);
}
void XImage::FillArea(const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color, EG_RECT& Rect)
{
for (INTN y = Rect.YPos; y < GetHeight() && (y - Rect.YPos) < Rect.Height; ++y) {
for (INTN x = Rect.XPos; x < GetWidth() && (x - Rect.XPos) < Rect.Width; ++x) {
PixelData[y * Width + x] = Color;
}
}
}
UINT8 XImage::Smooth(const UINT8* p, int a01, int a10, int a21, int a12, float dx, float dy, float scale)
{
return (UINT8)((*(p + a01) * (scale - dx) * 3.f + *(p + a10) * (scale - dy) * 3.f + *(p + a21) * dx * 3.f +
*(p + a12) * dy * 3.f + *(p) * 2.f *scale) / (scale * 8.f));
}
/* Place Top image over this image at PosX,PosY
* Lowest means final image is opaque
* else transparency will be multiplied
*/
void XImage::Copy(XImage* Image)
{
CopyRect(*Image, 0, 0);
}
//sizes remain as were assumed input image is large enough?
void XImage::CopyScaled(const XImage& Image, float scale)
{
int SrcWidth = (int)Image.GetWidth(); //because Source[] requires int argument, why not long long int?
int Pixel = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
int Row = SrcWidth * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
int W = (int)GetWidth();
int H = (int)GetHeight();
const XArray<EFI_GRAPHICS_OUTPUT_BLT_PIXEL>& Source = Image.GetData();
for (INTN y = 0; y < H; y++) //destination coordinates
{
int ly = (int)(y / scale); //integer part of src coord
float dy = y - ly * scale; //fractional part
for (INTN x = 0; x < W; x++)
{
int lx = (int)(x / scale);
float dx = x - lx * scale;
int a01 = (lx == 0) ? 0 : -Pixel;
int a10 = (ly == 0) ? 0 : -Row;
int a21 = (lx == W - 1) ? 0 : Pixel;
int a12 = (ly == H - 1) ? 0 : Row;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL& dst = *GetPixelPtr(x, y);
dst.Blue = Smooth(&Source[lx + ly * SrcWidth].Blue, a01, a10, a21, a12, dx, dy, scale);
dst.Green = Smooth(&Source[lx + ly * SrcWidth].Green, a01, a10, a21, a12, dx, dy, scale);
dst.Red = Smooth(&Source[lx + ly * SrcWidth].Red, a01, a10, a21, a12, dx, dy, scale);
dst.Reserved = Source[lx + ly * SrcWidth].Reserved;
}
}
}
void XImage::CopyRect(const XImage& Image, INTN XPos, INTN YPos)
{
for (INTN y = 0; y < GetHeight() && (y + YPos) < Image.GetHeight(); ++y) {
for (INTN x = 0; x < GetWidth() && (x + XPos) < Image.GetWidth(); ++x) {
PixelData[y * Width + x] = Image.GetPixel(x + XPos, y + YPos);
}
}
}
/*
* copy rect InputRect from the input Image and place to OwnRect in this image
* width and height will be the smaller of the two rect
* taking into account boundary intersect
*/
void XImage::CopyRect(const XImage& Image, const EG_RECT& OwnPlace, const EG_RECT& InputRect)
{
INTN Dx = OwnPlace.XPos - InputRect.XPos;
INTN Dy = OwnPlace.YPos - InputRect.YPos;
INTN W = MIN(OwnPlace.Width, InputRect.Width);
INTN H = MIN(OwnPlace.Height, InputRect.Height);
for (INTN y = OwnPlace.YPos; y - OwnPlace.YPos < H && y < GetHeight() && (y - Dy) < Image.GetHeight(); ++y) {
for (INTN x = OwnPlace.XPos; x - OwnPlace.XPos < W && x < GetWidth() && (x - Dx) < Image.GetWidth(); ++x) {
PixelData[y * Width + x] = Image.GetPixel(x - Dx, y - Dy);
}
}
}
void XImage::Compose(INTN PosX, INTN PosY, const XImage& TopImage, XBool Lowest, float topScale)
{
EG_RECT OutPlace;
OutPlace.XPos = PosX;
OutPlace.YPos = PosY;
OutPlace.Width = GetWidth();
OutPlace.Height = GetHeight();
EG_RECT Area;
Area.XPos = 0;
Area.YPos = 0;
Area.Width = TopImage.GetWidth();
Area.Height = TopImage.GetHeight();
Compose(OutPlace, Area, TopImage, Lowest, topScale);
}
// TopScale is for scaling TopImage. = 0.f means no scale or = 1.f
// InPlace is a place in TopImage before scaling
void XImage::Compose(const EG_RECT& OutPlace, const EG_RECT& InPlace, const XImage& TopImage, XBool Lowest, float TopScale)
{
INTN PosX = InPlace.XPos;
INTN PosY = InPlace.YPos;
INTN WArea = InPlace.Width;
INTN HArea = InPlace.Height;
XBool gray = false;
if (TopScale < 0) {
gray = true;
TopScale = -TopScale;
}
XImage Top2;
if (TopScale != 0.f && TopScale != 1.f) {
Top2.setSizeInPixels((UINTN)(TopImage.GetWidth() * TopScale), (UINTN)(TopImage.GetHeight() * TopScale));
Top2.CopyScaled(TopImage, TopScale);
PosX = (int)(PosX * TopScale);
PosY = (int)(PosY * TopScale);
WArea = (int)(WArea * TopScale);
HArea = (int)(HArea * TopScale);
}
const XImage& Top = (TopScale != 0.f && TopScale != 1.f) ? Top2 : TopImage; //this is a link, not copy
//assumed Area.Width == OutPlace.Width
// if not choose min
WArea = MIN(WArea, OutPlace.Width);
if (OutPlace.XPos + WArea > GetWidth()) { //coordinate in this image - OutPlace
WArea = GetWidth() - OutPlace.XPos;
}
HArea = MIN(HArea, OutPlace.Height);
if (OutPlace.YPos + HArea > GetHeight()) {
HArea = GetHeight() - OutPlace.YPos;
}
//change only affected pixels
for (INTN y = 0; y < HArea && (y + PosY) < Top.GetHeight(); ++y) {
// EFI_GRAPHICS_OUTPUT_BLT_PIXEL& CompPtr = *GetPixelPtr(PosX, y); // I assign a ref to avoid the operator ->. Compiler will produce the same anyway.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* CompPtr = GetPixelPtr(OutPlace.XPos, OutPlace.YPos + y);
for (INTN x = 0; x < WArea && (x + PosX) < Top.GetWidth(); ++x) {
//------
// test compAlpha = 255; TopAlpha = 0 -> only Comp, TopAplha = 255 -> only Top
UINT32 TopAlpha = Top.GetPixel(x + PosX, y + PosY).Reserved & 0xFF; //0, 255
UINT32 CompAlpha = CompPtr->Reserved & 0xFF; //255
UINT32 RevAlpha = 255 - TopAlpha; //2<<8; 255, 0
UINT32 TempAlpha = CompAlpha * RevAlpha; //2<<16; 255*255, 0
TopAlpha *= 255; //2<<16; 0, 255*255
UINT32 FinalAlpha = TopAlpha + TempAlpha; //2<<16; 255*255, 255*255
//final alpha =(1-(1-x)*(1-y)) =(255*255-(255-topA)*(255-compA))/255 = topA+compA*(1-topA)
if (FinalAlpha != 0) {
UINT32 Temp = (CompPtr->Blue * TempAlpha) + (Top.GetPixel(x + PosX, y + PosY).Blue * TopAlpha);
CompPtr->Blue = (UINT8)(Temp / FinalAlpha);
Temp = (CompPtr->Green * TempAlpha) + (Top.GetPixel(x + PosX, y + PosY).Green * TopAlpha);
CompPtr->Green = (UINT8)(Temp / FinalAlpha);
Temp = (CompPtr->Red * TempAlpha) + (Top.GetPixel(x + PosX, y + PosY).Red * TopAlpha);
CompPtr->Red = (UINT8)(Temp / FinalAlpha);
if (gray && (TopAlpha != 0)) {
Temp = ((UINT32)CompPtr->Blue + 2 * (UINT32)CompPtr->Red + 4 * (UINT32)CompPtr->Green) / 7;
CompPtr->Blue = (UINT8)Temp;
CompPtr->Red = (UINT8)Temp;
CompPtr->Green = (UINT8)Temp;
}
}
if (Lowest) {
CompPtr->Reserved = 255;
} else {
CompPtr->Reserved = (UINT8)(FinalAlpha / 255);
}
CompPtr++; //faster way to move to next pixel
}
}
}
/* Place this image over Back image at PosX,PosY
* and result will be in this image
* But pixels will be moved anyway so it's impossible without double copy
*
*/
//void XImage::ComposeOnBack(INTN PosX, INTN PosY, const XImage& BackImage, XBool Lowest)
void XImage::FlipRB()
{
UINTN ImageSize = (Width * Height);
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Pixel = GetPixelPtr(0,0);
for (UINTN i = 0; i < ImageSize; ++i) {
UINT8 Temp = Pixel->Blue;
Pixel->Blue = Pixel->Red;
Pixel->Red = Temp;
// if (!WantAlpha) Pixel->Reserved = 0xFF;
Pixel++;
}
}
/*
* The function converted plain array into XImage object
* Error = 0 - Success
* Error = 28 - invalid signature
* Image is emptied if there is an error.
*/
EFI_STATUS XImage::FromPNG(const UINT8 * Data, UINTN Length)
{
// DBG("XImage len=%llu\n", Length);
if (Data == NULL) {
setEmpty(); // to be 100% sure
return EFI_INVALID_PARAMETER;
}
UINT8 * PixelPtr; // = (UINT8 *)&PixelData[0];
unsigned Error = eglodepng_decode(&PixelPtr, &Width, &Height, Data, Length);
if (Error != 0 && Error != 28) {
setEmpty(); // to be 100% sure
return EFI_NOT_FOUND;
}
if ( !PixelPtr ) {
setEmpty(); // to be 100% sure
return EFI_UNSUPPORTED; // It's possible to get error 28 and PixelPtr == NULL
}
setSizeInPixels(Width, Height);
//now we have a new pointer and want to move data
INTN NewLength = Width * Height * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
CopyMem(GetPixelPtr(0,0), PixelPtr, NewLength);
FreePool(PixelPtr); //allocated by lodepng
FlipRB();
return EFI_SUCCESS;
}
/*
* The function creates new array Data and inform about it size to be saved
* as a file.
* The caller is responsible to free the array.
*/
EFI_STATUS XImage::ToPNG(UINT8** Data, UINTN& OutSize)
{
size_t FileDataLength = 0;
FlipRB(); //commomly we want alpha for PNG, but not for screenshot, fix alpha there
UINT8 * PixelPtr = (UINT8 *)&PixelData[0];
unsigned Error = eglodepng_encode(Data, &FileDataLength, PixelPtr, Width, Height);
OutSize = FileDataLength;
if (Error) return EFI_UNSUPPORTED;
return EFI_SUCCESS;
}
///*
// * 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
// * but this procedure is mostly for testing purpose. Real SVG theme can't be divided to separate SVG files
// */
//EFI_STATUS XImage::FromSVG(const CHAR8 *SVGData, float scale)
//{
// NSVGimage *SVGimage;
// NSVGparser* p;
//
// NSVGrasterizer* rast = nsvgCreateRasterizer();
// if (!rast) return EFI_UNSUPPORTED;
// //we have to copy input data because nanosvg wants to change it
// char *input = (__typeof__(input))AllocateCopyPool(AsciiStrSize(SVGData), SVGData);
// if (!input) return EFI_DEVICE_ERROR;
//
// p = nsvgParse(input, 72, 1.f); //the parse will change input contents
// SVGimage = p->image;
// if (SVGimage) {
// float ScaleX = Width / SVGimage->width;
// float ScaleY = Height / SVGimage->height;
// float Scale = (ScaleX > ScaleY) ? ScaleY : ScaleX;
// Scale *= scale;
//
// DBG("Test image width=%d heigth=%d\n", (int)(SVGimage->width), (int)(SVGimage->height));
// nsvgRasterize(rast, SVGimage, 0.f, 0.f, Scale, Scale, (UINT8*)&PixelData[0], (int)Width, (int)Height, (int)Width * sizeof(PixelData[0]));
// FreePool(SVGimage);
// }
//// nsvg__deleteParser(p); //can't delete raster until we make imageChain // not sure why, but this is npt used so far. FIX if back in use.
// nsvgDeleteRasterizer(rast);
// FreePool(input);
// return EFI_SUCCESS;
//}
// Screen operations
/*
* The function to get image from screen. Used in screenshot (full screen), Pointer (small area) and Draw (small area)
* XImage must be created with Width, Height of Rect
* the rect will be clipped if it intersects the screen edge
*
* be careful about alpha. This procedure can produce alpha = 0 which means full transparent
* No! Anuway alpha should be corrected to 0xFF
*/
void XImage::GetArea(const EG_RECT& Rect)
{
GetArea(Rect.XPos, Rect.YPos, Rect.Width, Rect.Height);
}
void XImage::GetArea(INTN x, INTN y, UINTN W, UINTN H)
{
EFI_STATUS Status;
EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
Status = EfiLibLocateProtocol(EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&GraphicsOutput);
if (EFI_ERROR(Status)) {
GraphicsOutput = NULL;
Status = EfiLibLocateProtocol(EFI_UGA_DRAW_PROTOCOL_GUID, (void **)&UgaDraw);
if (EFI_ERROR(Status))
UgaDraw = NULL;
}
if (W == 0) W = Width;
if (H == 0) H = Height;
Width = (x + W > (UINTN)UGAWidth) ? (x > UGAWidth ? 0 : UGAWidth - x) : W;
Height = (y + H > (UINTN)UGAHeight) ? (y > UGAHeight ? 0 : UGAHeight - y) : H;
setSizeInPixels(Width, Height); // setSizeInPixels BEFORE, so &PixelData[0]
if ( Width == 0 || Height == 0 ) return; // nothing to get, area is zero. &PixelData[0] would crash
/*
* Blt(...Width, Height, Delta);
* if (Delta == 0) {
* Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
* }
*
*/
if (GraphicsOutput != NULL) {
GraphicsOutput->Blt(GraphicsOutput,
&PixelData[0],
EfiBltVideoToBltBuffer,
x, y, 0, 0, Width, Height, 0); // Width*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
}
else if (UgaDraw != NULL) {
UgaDraw->Blt(UgaDraw,
(EFI_UGA_PIXEL *)GetPixelPtr(0,0),
EfiUgaVideoToBltBuffer,
x, y, 0, 0, Width, Height, 0); //Width*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
}
//fix alpha
UINTN ImageSize = (Width * Height);
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Pixel = GetPixelPtr(0,0);
for (UINTN i = 0; i < ImageSize; ++i) {
(Pixel++)->Reserved = 0xFF;
}
}
void XImage::DrawWithoutCompose(INTN x, INTN y, UINTN width, UINTN height) const
{
if (isEmpty()) {
return;
}
if ( width == 0 ) width = Width;
if ( height == 0 ) height = Height;
UINTN AreaWidth = (x + width > (UINTN)UGAWidth) ? (x > UGAWidth ? 0 : UGAWidth - x) : width;
UINTN AreaHeight = (y + height > (UINTN)UGAHeight) ? (y > UGAHeight ? 0 : UGAHeight - y) : height;
// DBG("area=%d,%d\n", AreaWidth, AreaHeight);
// prepare protocols
EFI_STATUS Status;
EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
Status = EfiLibLocateProtocol(EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&GraphicsOutput);
if (EFI_ERROR(Status)) {
GraphicsOutput = NULL;
Status = EfiLibLocateProtocol(EFI_UGA_DRAW_PROTOCOL_GUID, (void **)&UgaDraw);
if (EFI_ERROR(Status))
UgaDraw = NULL;
}
//output combined image
if (GraphicsOutput != NULL) {
GraphicsOutput->Blt(GraphicsOutput, (*this).GetPixelPtr(0, 0),
EfiBltBufferToVideo,
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
}
else if (UgaDraw != NULL) {
UgaDraw->Blt(UgaDraw, (EFI_UGA_PIXEL *)(*this).GetPixelPtr(0, 0), EfiUgaBltBufferToVideo,
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
}
}
void XImage::Draw(INTN x, INTN y) const
{
Draw(x, y, 0, true);
}
void XImage::Draw(INTN x, INTN y, float scale) const
{
Draw(x, y, scale, true);
}
void XImage::Draw(INTN x, INTN y, float scale, XBool Opaque) const
{
//prepare images
if (isEmpty()) {
return;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
XImage Top(*this, scale); //can accept 0 as scale
XImage Background(Width, Height);
UINTN AreaWidth = (x + Width > (UINTN)UGAWidth) ? (x > UGAWidth ? 0 : UGAWidth - x) : Width;
UINTN AreaHeight = (y + Height > (UINTN)UGAHeight) ? (y > UGAHeight ? 0 : UGAHeight - y) : Height;
Background.GetArea(x, y, AreaWidth, AreaHeight); //it will resize the Background image
Background.Compose(0, 0, Top, Opaque);
Background.DrawWithoutCompose(x, y);
}
void XImage::DrawOnBack(INTN XPos, INTN YPos, const XImage& Plate) const
{
XImage BackLayer(Width, Height);
BackLayer.CopyRect(Plate, XPos, YPos); //assume Plate is big enough [XPos+Width, YPos+Height]
BackLayer.Compose(0, 0, *this, true);
BackLayer.DrawWithoutCompose(XPos, YPos);
}
/*
* 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(const EFI_FILE *BaseDir, const char* IconName)
{
return LoadXImage(BaseDir, XStringW().takeValueFrom(IconName));
}
EFI_STATUS XImage::LoadXImage(const 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 XImage::LoadXImage(const EFI_FILE *BaseDir, const XStringW& IconName)
{
EFI_STATUS Status = EFI_NOT_FOUND;
UINT8 *FileData = NULL;
UINTN FileDataLength = 0;
// 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 || IconName.isEmpty())
return EFI_NOT_FOUND;
// load file
XStringW FileName = L"icons\\" + IconName + L".icns";
Status = egLoadFile(BaseDir, FileName.wc_str(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
FileName = L"icons\\" + IconName + L".png";
Status = egLoadFile(BaseDir, FileName.wc_str(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
FileName = IconName + L".png";
Status = egLoadFile(BaseDir, FileName.wc_str(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
FileName = IconName; //may be it already contain extension, for example Logo.png
Status = egLoadFile(BaseDir, FileName.wc_str(), &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
return Status;
}
}
}
}
// decode it
Status = FromPNG(FileData, FileDataLength);
if (EFI_ERROR(Status)) {
DBG("%ls not decoded. Status=%s\n", IconName.wc_str(), efiStrError(Status));
}
FreePool(FileData);
return Status;
}
//EnsureImageSize should create new object with new sizes
//while compose uses old object
void XImage::EnsureImageSize(IN UINTN NewWidth, IN UINTN NewHeight)
{
EnsureImageSize(NewWidth, NewHeight, NullColor);
}
void XImage::EnsureImageSize(IN UINTN NewWidth, IN UINTN NewHeight, IN CONST EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color)
{
if (NewWidth == Width && NewHeight == Height)
return;
XImage NewImage(NewWidth, NewHeight);
NewImage.Fill(Color);
NewImage.Compose(0, 0, (*this), false); //should keep existing opacity
setSizeInPixels(NewWidth, NewHeight); //include reallocate but loose data
CopyMem(&PixelData[0], &NewImage.PixelData[0], GetSizeInBytes());
//we have to copy pixels twice? because we can't return newimage instead of this
}
void XImage::DummyImage(IN UINTN PixelSize)
{
UINTN LineOffset;
CHAR8 *Ptr, *YPtr;
setSizeInPixels(PixelSize, PixelSize);
LineOffset = PixelSize * 4;
YPtr = (CHAR8 *)GetPixelPtr(0,0) + ((PixelSize - 32) >> 1) * (LineOffset + 4);
for (UINTN y = 0; y < 32; y++) {
Ptr = YPtr;
for (UINTN x = 0; x < 32; x++) {
if (((x + y) % 12) < 6) {
*Ptr++ = 0;
*Ptr++ = 0;
*Ptr++ = 0;
} else {
*Ptr++ = 0;
*Ptr++ = ~0; //yellow
*Ptr++ = ~0;
}
*Ptr++ = ~111; //opacity
}
YPtr += LineOffset;
}
}
//
// Load an image from a .icns file
//
EFI_STATUS XImage::LoadIcns(const EFI_FILE* BaseDir, IN CONST CHAR16 *FileName, IN UINTN PixelSize)
{
if (gSettings.GUI.TextOnly) // skip loading if it's not used anyway
return EFI_SUCCESS;
if (BaseDir) {
EFI_STATUS Status = EFI_NOT_FOUND;
UINT8 *FileData = NULL;
UINTN FileDataLength = 0;
//TODO - make XImage
// EG_IMAGE *NewImage;
// load file
Status = egLoadFile(BaseDir, FileName, &FileData, &FileDataLength);
if (EFI_ERROR(Status)) {
return Status;
}
// decode it
// NewImage = egDecodeICNS(FileData, FileDataLength, PixelSize, true);
// Status = FromEGImage(NewImage);
Status = FromICNS(FileData, FileDataLength, PixelSize);
FreePool(FileData);
return Status;
}
return EFI_NOT_FOUND;
}