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-04-10 12:04:21 +02:00
# include "libegint.h" //for egDecodeIcns
2020-08-17 21:40:52 +02:00
# include "../refit/lib.h"
# include "../Platform/Settings.h"
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-04-01 14:57:32 +02:00
EFI_GRAPHICS_OUTPUT_BLT_PIXEL NullColor = { 0 , 0 , 0 , 0 } ;
2020-02-26 20:44:16 +01:00
2020-08-11 14:43:53 +02:00
XImage : : XImage ( UINTN W , UINTN H ) : Width ( 0 ) , Height ( 0 ) , PixelData ( ) // initialisation of Width and Height and , PixelData() to avoid warning with -Weffc++
2020-02-26 20:44:16 +01:00
{
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-23 15:51:20 +01:00
2020-03-09 02:12:24 +01:00
XImage & XImage : : operator = ( const XImage & other )
{
2020-03-30 23:42:58 +02:00
setSizeInPixels ( other . GetWidth ( ) , other . GetHeight ( ) ) ; // change the size, ie the number of element in the array. Reaalocate buffer if needed
PixelData = other . PixelData ;
2020-03-09 02:12:24 +01:00
return * this ;
}
2020-08-11 14:43:53 +02:00
XImage : : XImage ( const XImage & Image , float scale ) : Width ( 0 ) , Height ( 0 ) , PixelData ( ) // initialisation of Width and Height and , PixelData() to avoid warning with -Weffc++
2020-02-26 20:44:16 +01:00
{
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 ;
}
2020-04-04 04:59:17 +02:00
EFI_GRAPHICS_OUTPUT_BLT_PIXEL * XImage : : GetPixelPtr ( INTN x , INTN y )
2020-02-26 20:44:16 +01:00
{
return & PixelData [ x + y * Width ] ;
}
2020-04-04 04:59:17 +02:00
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL * XImage : : GetPixelPtr ( INTN x , INTN y ) const
2020-03-31 17:59:35 +02:00
{
return & PixelData [ x + y * Width ] ;
}
2020-04-04 04:59:17 +02:00
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL & XImage : : GetPixel ( INTN x , INTN y ) const
2020-02-26 20:44:16 +01:00
{
return PixelData [ x + y * Width ] ;
}
2020-04-04 04:59:17 +02:00
/*
2020-02-26 20:44:16 +01:00
UINTN XImage : : GetWidth ( ) const
{
return Width ;
}
UINTN XImage : : GetHeight ( ) const
{
return Height ;
}
2020-04-04 04:59:17 +02:00
*/
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-04-04 04:59:17 +02:00
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 ) {
2020-02-26 20:44:16 +01:00
PixelData [ y * Width + x ] = Color ;
2020-04-04 04:59:17 +02:00
}
2020-02-26 20:44:16 +01:00
}
}
2020-04-12 20:53:27 +02:00
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 ) ) ;
}
2020-04-03 22:00:42 +02:00
//sizes remain as were assumed input image is large enough?
2020-03-02 13:00:24 +01:00
void XImage : : CopyScaled ( const XImage & Image , float scale )
{
2020-04-04 04:59:17 +02:00
int SrcWidth = ( int ) Image . GetWidth ( ) ; //because Source[] requires int argument, why not long long int?
2020-03-02 13:00:24 +01:00
int Pixel = sizeof ( EFI_GRAPHICS_OUTPUT_BLT_PIXEL ) ;
2020-04-04 04:59:17 +02:00
int Row = SrcWidth * sizeof ( EFI_GRAPHICS_OUTPUT_BLT_PIXEL ) ;
int W = ( int ) GetWidth ( ) ;
int H = ( int ) GetHeight ( ) ;
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 ( ) ;
2020-04-12 20:53:27 +02:00
for ( INTN y = 0 ; y < H ; y + + ) //destination coordinates
2020-03-02 13:00:24 +01:00
{
2020-04-12 20:53:27 +02:00
int ly = ( int ) ( y / scale ) ; //integer part of src coord
float dy = y - ly * scale ; //fractional part
2020-04-04 04:59:17 +02:00
for ( INTN x = 0 ; x < W ; x + + )
2020-03-02 13:00:24 +01:00
{
int lx = ( int ) ( x / scale ) ;
2020-04-12 20:53:27 +02:00
float dx = x - lx * scale ;
2020-04-20 01:12:43 +02:00
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 ;
2020-03-02 13:00:24 +01:00
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
*/
2020-04-19 13:23:23 +02:00
void XImage : : Compose ( INTN PosX , INTN PosY , const XImage & TopImage , bool Lowest , float topScale )
2020-04-07 21:48:36 +02:00
{
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 ( ) ;
2020-04-19 13:23:23 +02:00
Compose ( OutPlace , Area , TopImage , Lowest , topScale ) ;
2020-04-07 21:48:36 +02:00
}
2020-04-09 15:04:12 +02:00
// 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 , bool Lowest , float TopScale )
2020-02-26 20:44:16 +01:00
{
2020-04-08 09:32:26 +02:00
INTN PosX = InPlace . XPos ;
INTN PosY = InPlace . YPos ;
2020-04-09 15:04:12 +02:00
INTN WArea = InPlace . Width ;
INTN HArea = InPlace . Height ;
2020-04-21 17:57:47 +02:00
bool gray = false ;
if ( TopScale < 0 ) {
gray = true ;
TopScale = - TopScale ;
}
2020-04-09 15:04:12 +02:00
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
2020-04-07 21:48:36 +02:00
//assumed Area.Width == OutPlace.Width
// if not choose min
2020-04-09 15:04:12 +02:00
WArea = MIN ( WArea , OutPlace . Width ) ;
2020-04-08 09:32:26 +02:00
if ( OutPlace . XPos + WArea > GetWidth ( ) ) { //coordinate in this image - OutPlace
WArea = GetWidth ( ) - OutPlace . XPos ;
2020-04-07 21:48:36 +02:00
}
2020-04-09 15:04:12 +02:00
HArea = MIN ( HArea , OutPlace . Height ) ;
2020-04-08 09:32:26 +02:00
if ( OutPlace . YPos + HArea > GetHeight ( ) ) {
HArea = GetHeight ( ) - OutPlace . YPos ;
2020-04-07 21:48:36 +02:00
}
2020-04-02 08:50:44 +02:00
//change only affected pixels
2020-04-09 15:04:12 +02:00
for ( INTN y = 0 ; y < HArea & & ( y + PosY ) < Top . 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.
2020-04-07 21:48:36 +02:00
EFI_GRAPHICS_OUTPUT_BLT_PIXEL * CompPtr = GetPixelPtr ( OutPlace . XPos , OutPlace . YPos + y ) ;
2020-04-09 15:04:12 +02:00
for ( INTN x = 0 ; x < WArea & & ( x + PosX ) < Top . GetWidth ( ) ; + + x ) {
2020-04-01 16:59:58 +02:00
//------
// test compAlpha = 255; TopAlpha = 0 -> only Comp, TopAplha = 255 -> only Top
2020-04-09 15:04:12 +02:00
UINT32 TopAlpha = Top . GetPixel ( x + PosX , y + PosY ) . Reserved & 0xFF ; //0, 255
2020-04-08 09:32:26 +02:00
UINT32 CompAlpha = CompPtr - > Reserved & 0xFF ; //255
UINT32 RevAlpha = 255 - TopAlpha ; //2<<8; 255, 0
UINT32 TempAlpha = CompAlpha * RevAlpha ; //2<<16; 255*255, 0
2020-04-01 16:59:58 +02:00
TopAlpha * = 255 ; //2<<16; 0, 255*255
2020-04-08 09:32:26 +02:00
UINT32 FinalAlpha = TopAlpha + TempAlpha ; //2<<16; 255*255, 255*255
2020-04-01 16:59:58 +02:00
//final alpha =(1-(1-x)*(1-y)) =(255*255-(255-topA)*(255-compA))/255 = topA+compA*(1-topA)
2020-04-04 04:59:17 +02:00
2020-04-02 08:50:44 +02:00
if ( FinalAlpha ! = 0 ) {
2020-04-09 15:04:12 +02:00
UINT32 Temp = ( CompPtr - > Blue * TempAlpha ) + ( Top . GetPixel ( x + PosX , y + PosY ) . Blue * TopAlpha ) ;
2020-04-02 08:50:44 +02:00
CompPtr - > Blue = ( UINT8 ) ( Temp / FinalAlpha ) ;
2020-04-01 16:59:58 +02:00
2020-04-09 15:04:12 +02:00
Temp = ( CompPtr - > Green * TempAlpha ) + ( Top . GetPixel ( x + PosX , y + PosY ) . Green * TopAlpha ) ;
2020-04-02 08:50:44 +02:00
CompPtr - > Green = ( UINT8 ) ( Temp / FinalAlpha ) ;
2020-04-01 16:59:58 +02:00
2020-04-09 15:04:12 +02:00
Temp = ( CompPtr - > Red * TempAlpha ) + ( Top . GetPixel ( x + PosX , y + PosY ) . Red * TopAlpha ) ;
2020-04-02 08:50:44 +02:00
CompPtr - > Red = ( UINT8 ) ( Temp / FinalAlpha ) ;
2020-04-21 17:57:47 +02:00
2020-04-21 18:27:30 +02:00
if ( gray & & ( TopAlpha ! = 0 ) ) {
2020-04-21 17:57:47 +02:00
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 ;
}
2020-04-02 08:50:44 +02:00
}
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-04-01 16:59:58 +02:00
CompPtr - > Reserved = ( UINT8 ) ( FinalAlpha / 255 ) ;
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-04-02 08:50:44 +02:00
void XImage : : FlipRB ( )
2020-02-26 20:44:16 +01:00
{
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 ;
2020-04-02 08:50:44 +02:00
// if (!WantAlpha) Pixel->Reserved = 0xFF;
2020-02-26 20:44:16 +01:00
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-30 22:31:34 +02:00
// DBG("XImage len=%llu\n", Length);
2020-03-19 19:35:58 +01:00
if ( Data = = NULL ) return EFI_INVALID_PARAMETER ;
2020-03-30 22:31:34 +02: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-04-05 08:44:47 +02:00
if ( ! PixelPtr ) return EFI_UNSUPPORTED ; // It's possible to get error 28 and PixelPtr == NULL
2020-03-30 22:31:34 +02:00
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
2020-04-02 08:50:44 +02:00
FlipRB ( ) ;
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 ;
2020-04-02 08:50:44 +02:00
FlipRB ( ) ; //commomly we want alpha for PNG, but not for screenshot, fix alpha there
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-04-01 14:57:32 +02:00
//we have to copy input data because nanosvg wants to change it
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-04-01 14:57:32 +02:00
* No ! Anuway alpha should be corrected to 0xFF
2020-02-26 20:44:16 +01:00
*/
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-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 ;
2020-10-03 19:02:31 +02:00
Status = EfiLibLocateProtocol ( & GraphicsOutputProtocolGuid , ( void * * ) & GraphicsOutput ) ;
2020-02-26 20:44:16 +01:00
if ( EFI_ERROR ( Status ) ) {
GraphicsOutput = NULL ;
2020-10-03 19:02:31 +02:00
Status = EfiLibLocateProtocol ( & UgaDrawProtocolGuid , ( void * * ) & UgaDraw ) ;
2020-02-26 20:44:16 +01:00
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-04-18 11:46:52 +02:00
Width = ( x + W > ( UINTN ) UGAWidth ) ? ( x > UGAWidth ? 0 : UGAWidth - x ) : W ;
Height = ( y + H > ( UINTN ) UGAHeight ) ? ( y > UGAHeight ? 0 : 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-04-01 14:57:32 +02:00
//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 ;
}
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 ;
2020-04-18 11:46:52 +02:00
UINTN AreaWidth = ( x + width > ( UINTN ) UGAWidth ) ? ( x > UGAWidth ? 0 : UGAWidth - x ) : width ;
UINTN AreaHeight = ( y + height > ( UINTN ) UGAHeight ) ? ( y > UGAHeight ? 0 : 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 ;
2020-10-03 19:02:31 +02:00
Status = EfiLibLocateProtocol ( & GraphicsOutputProtocolGuid , ( void * * ) & GraphicsOutput ) ;
2020-03-09 02:12:24 +01:00
if ( EFI_ERROR ( Status ) ) {
GraphicsOutput = NULL ;
2020-10-03 19:02:31 +02:00
Status = EfiLibLocateProtocol ( & UgaDrawProtocolGuid , ( void * * ) & UgaDraw ) ;
2020-03-09 02:12:24 +01:00
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-04-09 20:04:35 +02:00
if ( x < 0 ) {
x = 0 ;
}
if ( y < 0 ) {
y = 0 ;
}
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-04-18 11:46:52 +02:00
UINTN AreaWidth = ( x + Width > ( UINTN ) UGAWidth ) ? ( x > UGAWidth ? 0 : UGAWidth - x ) : Width ;
UINTN AreaHeight = ( y + Height > ( UINTN ) UGAHeight ) ? ( y > UGAHeight ? 0 : UGAHeight - y ) : Height ;
2020-03-31 14:35:04 +02:00
Background . GetArea ( x , y , AreaWidth , AreaHeight ) ; //it will resize the Background image
Background . Compose ( 0 , 0 , Top , Opaque ) ;
2020-04-04 19:02:30 +02:00
Background . DrawWithoutCompose ( x , y ) ;
2020-02-26 20:44:16 +01:00
2020-04-04 19:02:30 +02:00
}
void XImage : : DrawOnBack ( INTN XPos , INTN YPos , const XImage & Plate )
{
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 ) ;
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
*/
2020-10-03 19:02:31 +02:00
EFI_STATUS XImage : : LoadXImage ( const EFI_FILE * BaseDir , const char * IconName )
2020-03-19 19:35:58 +01:00
{
2020-03-31 17:59:35 +02:00
return LoadXImage ( BaseDir , XStringW ( ) . takeValueFrom ( IconName ) ) ;
2020-03-19 19:35:58 +01:00
}
2020-03-30 17:31:52 +02:00
2020-10-03 19:02:31 +02:00
EFI_STATUS XImage : : LoadXImage ( const EFI_FILE * BaseDir , const wchar_t * LIconName )
2020-03-30 17:31:52 +02:00
{
2020-03-31 17:59:35 +02:00
return LoadXImage ( BaseDir , XStringW ( ) . takeValueFrom ( LIconName ) ) ;
2020-03-30 17:31:52 +02:00
}
2020-03-19 19:35:58 +01:00
//dont call this procedure for SVG theme BaseDir == NULL?
2020-04-07 21:48:36 +02:00
//it can be used for other files
2020-10-03 19:02:31 +02:00
EFI_STATUS XImage : : LoadXImage ( const 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-08-15 15:47:56 +02:00
Status = egLoadFile ( BaseDir , FileName . wc_str ( ) , & FileData , & FileDataLength ) ;
2020-03-19 19:35:58 +01:00
if ( EFI_ERROR ( Status ) ) {
FileName = L " icons \\ " + IconName + L " .png " ;
2020-08-15 15:47:56 +02:00
Status = egLoadFile ( BaseDir , FileName . wc_str ( ) , & FileData , & FileDataLength ) ;
2020-03-19 19:35:58 +01:00
if ( EFI_ERROR ( Status ) ) {
FileName = IconName + L " .png " ;
2020-08-15 15:47:56 +02:00
Status = egLoadFile ( BaseDir , FileName . wc_str ( ) , & FileData , & FileDataLength ) ;
2020-03-19 19:35:58 +01:00
if ( EFI_ERROR ( Status ) ) {
FileName = IconName ; //may be it already contain extension, for example Logo.png
2020-08-15 15:47:56 +02:00
Status = egLoadFile ( BaseDir , FileName . wc_str ( ) , & FileData , & FileDataLength ) ;
2020-03-19 19:35:58 +01:00
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 ) ) {
2020-08-25 17:35:19 +02:00
DBG ( " %ls not decoded. Status=%s \n " , IconName . wc_str ( ) , efiStrError ( Status ) ) ;
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-04-01 14:57:32 +02:00
void XImage : : EnsureImageSize ( IN UINTN NewWidth , IN UINTN NewHeight )
{
EnsureImageSize ( NewWidth , NewHeight , NullColor ) ;
}
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-04-03 22:00:42 +02:00
NewImage . Compose ( 0 , 0 , ( * this ) , false ) ; //should keep existing opacity
2020-03-19 07:56:31 +01:00
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
2020-04-06 11:15:36 +02:00
void XImage : : Copy ( XImage * Image )
{
CopyRect ( * Image , 0 , 0 ) ;
}
2020-03-24 16:17:12 +01:00
void XImage : : CopyRect ( const XImage & Image , INTN XPos , INTN YPos )
{
2020-04-04 04:59:17 +02:00
for ( INTN y = 0 ; y < GetHeight ( ) & & ( y + YPos ) < Image . GetHeight ( ) ; + + y ) {
for ( INTN x = 0 ; x < GetWidth ( ) & & ( x + XPos ) < Image . GetWidth ( ) ; + + x ) {
2020-03-24 16:17:12 +01:00
PixelData [ y * Width + x ] = Image . GetPixel ( x + XPos , y + YPos ) ;
}
}
}
2020-04-04 04:59:17 +02:00
/*
* 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
*/
2020-04-03 22:00:42 +02:00
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 ;
2020-04-04 04:59:17 +02:00
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 ) {
2020-04-03 22:00:42 +02:00
PixelData [ y * Width + x ] = Image . GetPixel ( x - Dx , y - Dy ) ;
}
}
}
2020-04-10 12:04:21 +02:00
//
// Load an image from a .icns file
//
2020-10-03 19:02:31 +02:00
EFI_STATUS XImage : : LoadIcns ( const EFI_FILE * BaseDir , IN CONST CHAR16 * FileName , IN UINTN PixelSize )
2020-04-10 12:04:21 +02:00
{
if ( GlobalConfig . 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
2020-04-16 13:42:14 +02:00
// EG_IMAGE *NewImage;
2020-04-10 12:04:21 +02:00
// load file
Status = egLoadFile ( BaseDir , FileName , & FileData , & FileDataLength ) ;
if ( EFI_ERROR ( Status ) ) {
return Status ;
}
// decode it
2020-04-16 13:42:14 +02:00
// NewImage = egDecodeICNS(FileData, FileDataLength, PixelSize, TRUE);
// Status = FromEGImage(NewImage);
Status = FromICNS ( FileData , FileDataLength , PixelSize ) ;
2020-04-10 12:04:21 +02:00
FreePool ( FileData ) ;
return Status ;
}
return EFI_NOT_FOUND ;
}