2020-02-26 20:44:16 +01:00
|
|
|
#include "XImage.h"
|
2020-02-28 21:28:33 +01:00
|
|
|
#include "lodepng.h"
|
|
|
|
#include "nanosvg.h"
|
2020-02-26 20:44:16 +01:00
|
|
|
|
2020-03-20 18:48:19 +01:00
|
|
|
|
2020-02-28 19:12:57 +01:00
|
|
|
#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
|
2020-02-28 15:08:40 +01:00
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
|
|
|
|
XImage::XImage()
|
|
|
|
{
|
|
|
|
Width = 0;
|
|
|
|
Height = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
XImage::XImage(UINTN W, UINTN H)
|
|
|
|
{
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = W;
|
|
|
|
// Height = H; //included below
|
2020-03-09 02:12:24 +01:00
|
|
|
setSizeInPixels(W, H);
|
2020-03-06 20:56:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-02 13:00:24 +01:00
|
|
|
XImage::XImage(EG_IMAGE* egImage)
|
|
|
|
{
|
2020-03-04 21:39:48 +01:00
|
|
|
if ( egImage) {
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = egImage->Width;
|
|
|
|
// Height = egImage->Height;
|
|
|
|
setSizeInPixels(egImage->Width, egImage->Height); // change the size, ie the number of element in the array. Reaalocate buffer if needed
|
2020-03-09 02:12:24 +01:00
|
|
|
CopyMem(&PixelData[0], egImage->PixelData, GetSizeInBytes());
|
2020-03-04 21:39:48 +01:00
|
|
|
}else{
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = 0;
|
|
|
|
// Height = 0;
|
|
|
|
setSizeInPixels(0, 0); // change the size, ie the number of element in the array. Reallocate buffer if needed
|
2020-03-04 21:39:48 +01:00
|
|
|
}
|
2020-03-02 13:00:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 15:51:20 +01:00
|
|
|
EFI_STATUS XImage::FromEGImage(const EG_IMAGE* egImage)
|
|
|
|
{
|
|
|
|
if ( egImage) {
|
|
|
|
setSizeInPixels(egImage->Width, egImage->Height);
|
|
|
|
CopyMem(&PixelData[0], egImage->PixelData, GetSizeInBytes());
|
|
|
|
} else {
|
|
|
|
setSizeInPixels(0, 0);
|
|
|
|
}
|
|
|
|
if (GetSizeInBytes() == 0) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-03-09 02:12:24 +01:00
|
|
|
XImage& XImage::operator= (const XImage& other)
|
|
|
|
{
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = other.GetWidth();
|
|
|
|
// Height = other.GetHeight();
|
|
|
|
setSizeInPixels(other.GetWidth(), other.GetHeight()); // change the size, ie the number of element in the array. Reaalocate buffer if needed
|
2020-03-09 02:12:24 +01:00
|
|
|
CopyMem(&PixelData[0], &other.PixelData[0], GetSizeInBytes());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-02-27 15:02:43 +01:00
|
|
|
UINT8 Smooth(const UINT8* p, int a01, int a10, int a21, int a12, int dx, int dy, float scale)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
2020-02-27 15:02:43 +01:00
|
|
|
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));
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
XImage::XImage(const XImage& Image, float scale)
|
|
|
|
{
|
2020-03-02 13:00:24 +01:00
|
|
|
UINTN SrcWidth = Image.GetWidth();
|
|
|
|
UINTN SrcHeight = Image.GetHeight();
|
2020-03-06 22:08:04 +01:00
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
if (scale < 1.e-4) {
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = SrcWidth;
|
|
|
|
// Height = SrcHeight;
|
|
|
|
setSizeInPixels(SrcWidth, SrcHeight);
|
2020-03-07 07:37:19 +01:00
|
|
|
for (UINTN y = 0; y < Height; ++y)
|
|
|
|
for (UINTN x = 0; x < Width; ++x)
|
|
|
|
PixelData[y * Width + x] = Image.GetPixel(x, y);
|
|
|
|
|
|
|
|
} else {
|
2020-03-19 07:56:31 +01:00
|
|
|
// Width = (UINTN)(SrcWidth * scale);
|
|
|
|
// Height = (UINTN)(SrcHeight * scale);
|
|
|
|
setSizeInPixels((UINTN)(SrcWidth * scale), (UINTN)(SrcHeight * scale));
|
2020-03-07 07:37:19 +01:00
|
|
|
CopyScaled(Image, scale);
|
|
|
|
}
|
2020-02-27 15:02:43 +01:00
|
|
|
}
|
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
#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);
|
|
|
|
|
2020-02-26 14:59:10 +01:00
|
|
|
#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)
|
2020-02-26 20:44:16 +01:00
|
|
|
|
2020-02-26 14:59:10 +01:00
|
|
|
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;
|
|
|
|
|
2020-02-25 14:55:39 +01:00
|
|
|
}
|
2020-02-26 14:59:10 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2020-02-25 21:19:39 +01:00
|
|
|
}
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
#endif
|
2020-02-27 15:02:43 +01:00
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
|
|
|
|
XImage::~XImage()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
const XArray<EFI_GRAPHICS_OUTPUT_BLT_PIXEL>& XImage::GetData() const
|
|
|
|
{
|
|
|
|
return PixelData;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* XImage::GetPixelPtr(UINTN x, UINTN y)
|
|
|
|
{
|
|
|
|
return &PixelData[x + y * Width];
|
|
|
|
}
|
|
|
|
|
|
|
|
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& XImage::GetPixel(UINTN x, UINTN y) const
|
|
|
|
{
|
|
|
|
return PixelData[x + y * Width];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UINTN XImage::GetWidth() const
|
|
|
|
{
|
|
|
|
return Width;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINTN XImage::GetHeight() const
|
|
|
|
{
|
|
|
|
return Height;
|
|
|
|
}
|
|
|
|
|
2020-03-09 02:12:24 +01:00
|
|
|
UINTN XImage::GetSizeInBytes() const
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
2020-03-09 02:12:24 +01:00
|
|
|
return PixelData.size() * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
}
|
|
|
|
|
2020-03-19 07:56:31 +01:00
|
|
|
void XImage::setSizeInPixels(UINTN W, UINTN H) //unused arguments?
|
2020-03-09 02:12:24 +01:00
|
|
|
{
|
2020-03-19 07:56:31 +01:00
|
|
|
Width = W;
|
|
|
|
Height = H;
|
2020-03-09 02:12:24 +01:00
|
|
|
PixelData.setSize(Width * Height);
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-03-23 20:10:16 +01:00
|
|
|
void XImage::Fill(const EG_PIXEL* Color)
|
|
|
|
{
|
|
|
|
Fill((const EFI_GRAPHICS_OUTPUT_BLT_PIXEL&)Color);
|
|
|
|
}
|
|
|
|
|
2020-03-24 16:17:12 +01:00
|
|
|
void XImage::FillArea(const EG_PIXEL* Color, EG_RECT& Rect)
|
|
|
|
{
|
|
|
|
FillArea((const EFI_GRAPHICS_OUTPUT_BLT_PIXEL&)*Color, Rect);
|
|
|
|
}
|
2020-03-20 18:48:19 +01:00
|
|
|
|
|
|
|
void XImage::FillArea(const EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color, EG_RECT& Rect)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
2020-03-20 18:48:19 +01:00
|
|
|
for (INTN y = Rect.YPos; y < (INTN)Height && (y - Rect.YPos) < Rect.Height; ++y) {
|
|
|
|
for (INTN x = Rect.XPos; x < (INTN)Width && (x - Rect.XPos) < Rect.Width; ++x)
|
2020-02-26 20:44:16 +01:00
|
|
|
PixelData[y * Width + x] = Color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 13:00:24 +01:00
|
|
|
void XImage::CopyScaled(const XImage& Image, float scale)
|
|
|
|
{
|
|
|
|
UINTN SrcWidth = Image.GetWidth();
|
|
|
|
|
|
|
|
int Pixel = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
int Row = (int)SrcWidth * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
2020-03-06 22:08:04 +01:00
|
|
|
|
2020-03-02 13:00:24 +01:00
|
|
|
const XArray<EFI_GRAPHICS_OUTPUT_BLT_PIXEL>& Source = Image.GetData();
|
|
|
|
|
|
|
|
for (UINTN y = 0; y < Height; y++)
|
|
|
|
{
|
|
|
|
int ly = (int)(y / scale);
|
|
|
|
int dy = (int)(y - ly * scale);
|
|
|
|
for (UINTN x = 0; x < Width; x++)
|
|
|
|
{
|
|
|
|
int lx = (int)(x / scale);
|
|
|
|
int dx = (int)(x - lx * scale);
|
|
|
|
int a01 = (x == 0) ? 0 : -Pixel;
|
|
|
|
int a10 = (y == 0) ? 0 : -Row;
|
|
|
|
int a21 = (x == Width - 1) ? 0 : Pixel;
|
|
|
|
int a12 = (y == Height - 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
/* Place Top image over this image at PosX,PosY
|
|
|
|
* Lowest means final image is opaque
|
|
|
|
* else transparency will be multiplied
|
|
|
|
*/
|
|
|
|
void XImage::Compose(INTN PosX, INTN PosY, const XImage& TopImage, bool Lowest)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
|
|
|
UINT32 TopAlpha;
|
|
|
|
UINT32 RevAlpha;
|
|
|
|
UINT32 FinalAlpha;
|
|
|
|
UINT32 Temp;
|
|
|
|
|
|
|
|
for (UINTN y = PosY; y < Height && (y - PosY) < TopImage.GetHeight(); ++y) {
|
2020-03-07 07:37:19 +01:00
|
|
|
// 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(PosX, y);
|
2020-02-26 20:44:16 +01:00
|
|
|
for (UINTN x = PosX; x < Width && (x - PosX) < TopImage.GetWidth(); ++x) {
|
|
|
|
TopAlpha = TopImage.GetPixel(x-PosX, y-PosY).Reserved;
|
|
|
|
RevAlpha = 255 - TopAlpha;
|
2020-03-07 07:37:19 +01:00
|
|
|
FinalAlpha = (255*255 - RevAlpha*(255 - CompPtr->Reserved)) / 255;
|
2020-02-26 20:44:16 +01:00
|
|
|
|
|
|
|
//final alpha =(1-(1-x)*(1-y)) =(255*255-(255-topA)*(255-compA))/255
|
2020-03-07 07:37:19 +01:00
|
|
|
Temp = (CompPtr->Blue * RevAlpha) + (TopImage.GetPixel(x-PosX, y-PosY).Blue * TopAlpha);
|
|
|
|
CompPtr->Blue = (UINT8)(Temp / 255);
|
2020-02-26 20:44:16 +01:00
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
Temp = (CompPtr->Green * RevAlpha) + (TopImage.GetPixel(x-PosX, y-PosY).Green * TopAlpha);
|
|
|
|
CompPtr->Green = (UINT8)(Temp / 255);
|
2020-02-26 20:44:16 +01:00
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
Temp = (CompPtr->Red * RevAlpha) + (TopImage.GetPixel(x-PosX, y-PosY).Red * TopAlpha);
|
|
|
|
CompPtr->Red = (UINT8)(Temp / 255);
|
2020-02-26 20:44:16 +01:00
|
|
|
|
|
|
|
if (Lowest) {
|
2020-03-07 07:37:19 +01:00
|
|
|
CompPtr->Reserved = 255;
|
2020-02-26 20:44:16 +01:00
|
|
|
} else {
|
2020-03-07 07:37:19 +01:00
|
|
|
CompPtr->Reserved = (UINT8)FinalAlpha;
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
2020-03-07 07:37:19 +01:00
|
|
|
CompPtr++; //faster way to move to next pixel
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-19 07:56:31 +01:00
|
|
|
/* 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, bool Lowest)
|
|
|
|
|
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
void XImage::FlipRB(bool WantAlpha)
|
|
|
|
{
|
|
|
|
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
|
2020-03-17 20:43:58 +01:00
|
|
|
* Error = 0 - Success
|
|
|
|
* Error = 28 - invalid signature
|
2020-02-26 20:44:16 +01:00
|
|
|
*/
|
2020-03-19 19:35:58 +01:00
|
|
|
EFI_STATUS XImage::FromPNG(const UINT8 * Data, UINTN Length)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
2020-03-19 19:35:58 +01:00
|
|
|
if (Data == NULL) return EFI_INVALID_PARAMETER;
|
2020-03-02 13:00:24 +01:00
|
|
|
UINT8 * PixelPtr = (UINT8 *)&PixelData[0];
|
2020-02-26 20:44:16 +01:00
|
|
|
unsigned Error = eglodepng_decode(&PixelPtr, &Width, &Height, Data, Length);
|
2020-03-19 19:35:58 +01:00
|
|
|
if (Error != 0 && Error != 28) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
2020-02-26 20:44:16 +01:00
|
|
|
FlipRB(true);
|
2020-03-19 19:35:58 +01:00
|
|
|
return EFI_SUCCESS;
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2020-03-19 19:35:58 +01:00
|
|
|
EFI_STATUS XImage::ToPNG(UINT8** Data, UINTN& OutSize)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
|
|
|
size_t FileDataLength = 0;
|
|
|
|
FlipRB(false);
|
2020-03-02 13:00:24 +01:00
|
|
|
UINT8 * PixelPtr = (UINT8 *)&PixelData[0];
|
2020-02-26 20:44:16 +01:00
|
|
|
unsigned Error = eglodepng_encode(Data, &FileDataLength, PixelPtr, Width, Height);
|
|
|
|
OutSize = FileDataLength;
|
2020-03-19 19:35:58 +01:00
|
|
|
if (Error) return EFI_UNSUPPORTED;
|
|
|
|
return EFI_SUCCESS;
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
|
2020-02-28 15:08:40 +01:00
|
|
|
/*
|
2020-03-19 19:35:58 +01:00
|
|
|
* fill XImage object by raster data described in SVG file
|
2020-02-28 15:08:40 +01:00
|
|
|
* caller should create the object with Width and Height and calculate scale
|
|
|
|
* scale = 1 correspond to fill the rect with the image
|
2020-03-19 19:35:58 +01:00
|
|
|
* 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
|
2020-02-28 15:08:40 +01:00
|
|
|
*/
|
2020-03-19 19:35:58 +01:00
|
|
|
EFI_STATUS XImage::FromSVG(const CHAR8 *SVGData, float scale)
|
2020-02-28 15:08:40 +01:00
|
|
|
{
|
|
|
|
NSVGimage *SVGimage;
|
|
|
|
NSVGparser* p;
|
|
|
|
|
|
|
|
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
2020-03-19 19:35:58 +01:00
|
|
|
if (!rast) return EFI_UNSUPPORTED;
|
2020-02-28 19:12:57 +01:00
|
|
|
char *input = (__typeof__(input))AllocateCopyPool(AsciiStrSize(SVGData), SVGData);
|
2020-03-19 19:35:58 +01:00
|
|
|
if (!input) return EFI_DEVICE_ERROR;
|
2020-02-28 15:08:40 +01:00
|
|
|
|
2020-02-28 19:12:57 +01:00
|
|
|
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);
|
|
|
|
}
|
2020-03-10 14:00:02 +01:00
|
|
|
// nsvg__deleteParser(p); //can't delete raster until we make imageChain
|
2020-02-28 15:08:40 +01:00
|
|
|
nsvgDeleteRasterizer(rast);
|
|
|
|
FreePool(input);
|
2020-03-19 19:35:58 +01:00
|
|
|
return EFI_SUCCESS;
|
2020-02-28 15:08:40 +01:00
|
|
|
}
|
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
// Screen operations
|
|
|
|
/*
|
|
|
|
* The function to get image from screen. Used in screenshot (full screen), Pointer (small area) and Draw (small area)
|
2020-03-07 07:37:19 +01:00
|
|
|
* XImage must be created with Width, Height of Rect
|
|
|
|
* the rect will be clipped if it intersects the screen edge
|
2020-02-26 20:44:16 +01:00
|
|
|
*
|
|
|
|
* be careful about alpha. This procedure can produce alpha = 0 which means full transparent
|
|
|
|
*/
|
2020-03-02 13:00:24 +01:00
|
|
|
void XImage::GetArea(const EG_RECT& Rect)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
2020-03-02 13:00:24 +01:00
|
|
|
GetArea(Rect.XPos, Rect.YPos, Rect.Width, Rect.Height);
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
|
2020-03-02 13:00:24 +01:00
|
|
|
void XImage::GetArea(INTN x, INTN y, UINTN W, UINTN H)
|
2020-02-26 20:44:16 +01:00
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_GUID UgaDrawProtocolGuid = EFI_UGA_DRAW_PROTOCOL_GUID;
|
|
|
|
EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
|
|
|
|
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
|
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
|
|
|
|
|
|
|
|
Status = EfiLibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
GraphicsOutput = NULL;
|
|
|
|
Status = EfiLibLocateProtocol(&UgaDrawProtocolGuid, (VOID **)&UgaDraw);
|
|
|
|
if (EFI_ERROR(Status))
|
|
|
|
UgaDraw = NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-03 15:17:39 +01:00
|
|
|
if (W == 0) W = Width;
|
|
|
|
if (H == 0) H = Height;
|
|
|
|
|
2020-03-07 07:37:19 +01:00
|
|
|
Width = (x + W > (UINTN)UGAWidth) ? (UGAWidth - x) : W;
|
|
|
|
Height = (y + H > (UINTN)UGAHeight) ? ((UINTN)UGAHeight - y) : H;
|
2020-03-07 07:43:50 +01:00
|
|
|
|
2020-03-09 02:12:24 +01:00
|
|
|
setSizeInPixels(Width, Height); // setSizeInPixels BEFORE, so &PixelData[0]
|
|
|
|
if ( Width == 0 || Height == 0 ) return; // nothing to get, area is zero. &PixelData[0] would crash
|
2020-03-19 07:56:31 +01:00
|
|
|
/*
|
|
|
|
* Blt(...Width, Height, Delta);
|
|
|
|
* if (Delta == 0) {
|
|
|
|
* Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
*/
|
2020-02-26 20:44:16 +01:00
|
|
|
if (GraphicsOutput != NULL) {
|
|
|
|
GraphicsOutput->Blt(GraphicsOutput,
|
2020-03-09 02:12:24 +01:00
|
|
|
&PixelData[0],
|
2020-02-26 20:44:16 +01:00
|
|
|
EfiBltVideoToBltBuffer,
|
2020-03-19 07:56:31 +01:00
|
|
|
x, y, 0, 0, Width, Height, 0); // Width*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
else if (UgaDraw != NULL) {
|
|
|
|
UgaDraw->Blt(UgaDraw,
|
2020-03-07 07:37:19 +01:00
|
|
|
(EFI_UGA_PIXEL *)GetPixelPtr(0,0),
|
2020-02-26 20:44:16 +01:00
|
|
|
EfiUgaVideoToBltBuffer,
|
2020-03-19 07:56:31 +01:00
|
|
|
x, y, 0, 0, Width, Height, 0); //Width*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-03-09 02:12:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:14:05 +01:00
|
|
|
void XImage::DrawWithoutCompose(INTN x, INTN y, UINTN width, UINTN height)
|
2020-03-09 02:12:24 +01:00
|
|
|
{
|
2020-03-20 18:48:19 +01:00
|
|
|
if (isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:14:05 +01:00
|
|
|
if ( width == 0 ) width = Width;
|
|
|
|
if ( height == 0 ) height = Height;
|
|
|
|
UINTN AreaWidth = (x + width > (UINTN)UGAWidth) ? (UGAWidth - x) : width;
|
|
|
|
UINTN AreaHeight = (y + height > (UINTN)UGAHeight) ? (UGAHeight - y) : height;
|
2020-03-09 02:12:24 +01:00
|
|
|
// DBG("area=%d,%d\n", AreaWidth, AreaHeight);
|
|
|
|
// prepare protocols
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_GUID UgaDrawProtocolGuid = EFI_UGA_DRAW_PROTOCOL_GUID;
|
|
|
|
EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
|
|
|
|
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
|
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
|
|
|
|
|
|
|
|
Status = EfiLibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
GraphicsOutput = NULL;
|
|
|
|
Status = EfiLibLocateProtocol(&UgaDrawProtocolGuid, (VOID **)&UgaDraw);
|
|
|
|
if (EFI_ERROR(Status))
|
|
|
|
UgaDraw = NULL;
|
|
|
|
}
|
|
|
|
//output combined image
|
|
|
|
if (GraphicsOutput != NULL) {
|
|
|
|
GraphicsOutput->Blt(GraphicsOutput, (*this).GetPixelPtr(0, 0),
|
|
|
|
EfiBltBufferToVideo,
|
2020-03-09 09:14:05 +01:00
|
|
|
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-03-09 02:12:24 +01:00
|
|
|
}
|
|
|
|
else if (UgaDraw != NULL) {
|
|
|
|
UgaDraw->Blt(UgaDraw, (EFI_UGA_PIXEL *)(*this).GetPixelPtr(0, 0), EfiUgaBltBufferToVideo,
|
2020-03-09 09:14:05 +01:00
|
|
|
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 16:17:12 +01:00
|
|
|
void XImage::Draw(INTN x, INTN y)
|
|
|
|
{
|
2020-03-25 18:49:01 +01:00
|
|
|
Draw(x, y, 0, true);
|
2020-03-24 16:17:12 +01:00
|
|
|
}
|
|
|
|
|
2020-03-02 14:46:30 +01:00
|
|
|
void XImage::Draw(INTN x, INTN y, float scale)
|
2020-03-25 18:49:01 +01:00
|
|
|
{
|
|
|
|
Draw(x, y, scale, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XImage::Draw(INTN x, INTN y, float scale, bool Opaque)
|
2020-02-26 07:05:14 +01:00
|
|
|
{
|
2020-02-26 14:59:10 +01:00
|
|
|
//prepare images
|
2020-03-20 18:48:19 +01:00
|
|
|
if (isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-24 16:17:12 +01:00
|
|
|
XImage Top(*this, scale); //can accept 0 as scale
|
2020-03-07 07:37:19 +01:00
|
|
|
XImage Background(Width, Height);
|
2020-03-06 22:08:04 +01:00
|
|
|
Background.GetArea(x, y, Width, Height);
|
2020-03-25 18:49:01 +01:00
|
|
|
Background.Compose(0, 0, Top, Opaque);
|
2020-03-06 20:56:32 +01:00
|
|
|
UINTN AreaWidth = (x + Width > (UINTN)UGAWidth) ? (UGAWidth - x) : Width;
|
|
|
|
UINTN AreaHeight = (y + Height > (UINTN)UGAHeight) ? (UGAHeight - y) : Height;
|
2020-03-17 20:43:58 +01:00
|
|
|
|
2020-02-26 20:44:16 +01:00
|
|
|
// prepare protocols
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_GUID UgaDrawProtocolGuid = EFI_UGA_DRAW_PROTOCOL_GUID;
|
|
|
|
EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
|
|
|
|
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
|
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
|
|
|
|
|
|
|
|
Status = EfiLibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
GraphicsOutput = NULL;
|
|
|
|
Status = EfiLibLocateProtocol(&UgaDrawProtocolGuid, (VOID **)&UgaDraw);
|
|
|
|
if (EFI_ERROR(Status))
|
|
|
|
UgaDraw = NULL;
|
|
|
|
}
|
|
|
|
//output combined image
|
|
|
|
if (GraphicsOutput != NULL) {
|
2020-03-09 02:12:24 +01:00
|
|
|
GraphicsOutput->Blt(GraphicsOutput, Background.GetPixelPtr(0, 0),
|
2020-02-26 20:44:16 +01:00
|
|
|
EfiBltBufferToVideo,
|
2020-03-09 02:12:24 +01:00
|
|
|
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
else if (UgaDraw != NULL) {
|
2020-03-06 22:08:04 +01:00
|
|
|
UgaDraw->Blt(UgaDraw, (EFI_UGA_PIXEL *)Background.GetPixelPtr(0, 0), EfiUgaBltBufferToVideo,
|
2020-03-19 07:56:31 +01:00
|
|
|
0, 0, x, y, AreaWidth, AreaHeight, GetWidth()*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
2020-02-26 20:44:16 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-17 20:43:58 +01:00
|
|
|
|
2020-03-19 19:35:58 +01:00
|
|
|
/*
|
|
|
|
* 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)
|
2020-03-17 20:43:58 +01:00
|
|
|
{
|
|
|
|
EFI_STATUS Status = EFI_NOT_FOUND;
|
|
|
|
UINT8 *FileData = NULL;
|
|
|
|
UINTN FileDataLength = 0;
|
|
|
|
|
2020-03-19 19:35:58 +01:00
|
|
|
// 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());
|
2020-03-18 07:39:11 +01:00
|
|
|
// return EFI_SUCCESS;
|
|
|
|
// }
|
2020-03-17 20:43:58 +01:00
|
|
|
|
2020-03-19 19:35:58 +01:00
|
|
|
if (BaseDir == NULL || IconName.isEmpty())
|
2020-03-17 20:43:58 +01:00
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
|
|
|
|
// load file
|
2020-03-19 19:35:58 +01:00
|
|
|
XStringW FileName = L"icons\\" + IconName + L".icns";
|
2020-03-17 20:43:58 +01:00
|
|
|
Status = egLoadFile(BaseDir, FileName.data(), &FileData, &FileDataLength);
|
2020-03-19 19:35:58 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 20:43:58 +01:00
|
|
|
// decode it
|
2020-03-19 19:35:58 +01:00
|
|
|
Status = FromPNG(FileData, FileDataLength);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
DBG("%s not decoded\n", IconName.data());
|
2020-03-17 20:43:58 +01:00
|
|
|
}
|
|
|
|
FreePool(FileData);
|
|
|
|
return Status;
|
|
|
|
}
|
2020-03-18 07:39:11 +01:00
|
|
|
|
2020-03-19 07:56:31 +01:00
|
|
|
//EnsureImageSize should create new object with new sizes
|
|
|
|
//while compose uses old object
|
2020-03-19 05:15:08 +01:00
|
|
|
void XImage::EnsureImageSize(IN UINTN NewWidth, IN UINTN NewHeight, IN CONST EFI_GRAPHICS_OUTPUT_BLT_PIXEL& Color)
|
2020-03-18 07:39:11 +01:00
|
|
|
{
|
2020-03-19 05:15:08 +01:00
|
|
|
if (NewWidth == Width && NewHeight == Height)
|
|
|
|
return;
|
2020-03-18 07:39:11 +01:00
|
|
|
|
2020-03-19 05:15:08 +01:00
|
|
|
XImage NewImage(NewWidth, NewHeight);
|
|
|
|
NewImage.Fill(Color);
|
2020-03-19 07:56:31 +01:00
|
|
|
NewImage.Compose(0, 0, (*this), false);
|
|
|
|
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
|
2020-03-18 07:39:11 +01:00
|
|
|
}
|
2020-03-23 15:51:20 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2020-03-24 16:17:12 +01:00
|
|
|
*Ptr++ = ~111; //opacity
|
2020-03-23 15:51:20 +01:00
|
|
|
}
|
|
|
|
YPtr += LineOffset;
|
|
|
|
}
|
|
|
|
}
|
2020-03-24 16:17:12 +01:00
|
|
|
|
|
|
|
void XImage::CopyRect(const XImage& Image, INTN XPos, INTN YPos)
|
|
|
|
{
|
|
|
|
for (UINTN y = 0; y < Height && (y + YPos) < Image.GetHeight(); ++y) {
|
|
|
|
for (UINTN x = 0; x < Width && (x + XPos) < Image.GetWidth(); ++x) {
|
|
|
|
PixelData[y * Width + x] = Image.GetPixel(x + XPos, y + YPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EG_IMAGE* XImage::ToEGImage()
|
|
|
|
{
|
|
|
|
EG_IMAGE* Tmp = egCreateImage(Width, Height, TRUE);
|
|
|
|
CopyMem(&Tmp->PixelData[0], &PixelData[0], GetSizeInBytes());
|
|
|
|
return Tmp;
|
|
|
|
}
|