2019-09-03 11:58:42 +02:00
/*
* Additional procedures for vector theme support
*
* Slice , 2018
*
*/
2020-03-13 14:38:52 +01:00
2020-03-13 14:34:36 +01:00
# define TEST_MATH 0
# define TEST_SVG_IMAGE 1
# define TEST_SIZEOF 0
# define TEST_FONT 0
# define TEST_DITHER 0
2020-03-16 12:15:25 +01:00
2020-03-31 17:59:35 +02:00
# include "VectorGraphics.h"
2019-09-03 11:58:42 +02:00
2020-08-17 21:40:52 +02:00
# include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
2020-03-13 14:38:52 +01:00
2019-09-03 11:58:42 +02:00
# include "nanosvg.h"
# include "FloatLib.h"
# include "lodepng.h"
2020-02-29 08:30:21 +01:00
# include "../refit/screen.h"
2020-03-12 18:45:28 +01:00
# include "../cpp_foundation/XString.h"
2020-08-17 21:40:52 +02:00
# include "../refit/lib.h"
2021-04-28 20:30:34 +02:00
# include "../Settings/Self.h"
2019-09-03 11:58:42 +02:00
# ifndef DEBUG_ALL
# define DEBUG_VEC 1
# else
# define DEBUG_VEC DEBUG_ALL
# endif
# if DEBUG_VEC == 0
# define DBG(...)
# else
# define DBG(...) DebugLog(DEBUG_VEC, __VA_ARGS__)
# endif
2020-03-13 14:34:36 +01:00
# include "XTheme.h"
2021-02-06 18:16:46 +01:00
2024-01-08 11:27:38 +01:00
//extern const LString8 IconsNames[]; -> Include XIcon.h instead if duplicating declaration.
//extern const INTN IconsNamesSize;
2020-03-13 14:34:36 +01:00
2019-09-03 11:58:42 +02:00
# define NSVG_RGB(r, g, b) (((unsigned int)b) | ((unsigned int)g << 8) | ((unsigned int)r << 16))
//#define NSVG_RGBA(r, g, b, a) (((unsigned int)b) | ((unsigned int)g << 8) | ((unsigned int)r << 16) | ((unsigned int)a << 24))
2020-10-03 19:02:31 +02:00
extern void
2019-09-03 11:58:42 +02:00
WaitForKeyPress ( CHAR16 * Message ) ;
2023-11-08 14:35:22 +01:00
EFI_STATUS XTheme : : ParseSVGXIcon ( NSVGparser * SVGParser , INTN Id , const XString8 & IconNameX , OUT XImage * Image )
2020-03-13 14:34:36 +01:00
{
2020-03-16 12:15:25 +01:00
EFI_STATUS Status = EFI_NOT_FOUND ;
2023-11-08 14:35:22 +01:00
NSVGimage * SVGimage = SVGParser - > image ; // full theme SVG image
2020-03-16 12:15:25 +01:00
NSVGshape * shape ;
2023-11-08 14:35:22 +01:00
NSVGshape * shapeNext /*, *shapesTail = NULL, *shapePrev*/ ;
2020-03-16 12:15:25 +01:00
2023-07-20 19:25:33 +02:00
2023-11-08 14:35:22 +01:00
float IconImageWidth = 0 ; // Width of the image.
float IconImageHeight = 0 ; // Height of the image.
shape = SVGimage - > shapes ;
2020-03-16 12:15:25 +01:00
while ( shape ) {
shapeNext = shape - > next ;
2023-11-11 06:50:58 +01:00
if ( nsvg__isShapeInGroup ( shape , IconNameX . c_str ( ) ) )
2023-11-08 14:35:22 +01:00
{
2020-04-23 22:43:35 +02:00
if ( BootCampStyle & & IconNameX . contains ( " selection_big " ) ) {
2020-03-16 12:15:25 +01:00
shape - > opacity = 0.f ;
}
2020-04-30 08:03:56 +02:00
if ( XString8 ( ) . takeValueFrom ( shape - > id ) . contains ( " BoundingRect " ) ) {
2020-03-16 12:15:25 +01:00
//there is bounds after nsvgParse()
2023-11-08 14:35:22 +01:00
IconImageWidth = shape - > bounds [ 2 ] - shape - > bounds [ 0 ] ;
IconImageHeight = shape - > bounds [ 3 ] - shape - > bounds [ 1 ] ;
// DBG("parsed bounds: %f, %f\n", IconImage.width, IconImage.height);
if ( IconImageHeight < 1.f ) {
IconImageHeight = 200.f ;
2020-03-16 12:15:25 +01:00
}
2020-04-23 22:43:35 +02:00
if ( IconNameX . contains ( " selection_big " ) & & ( ! SelectionOnTop ) ) {
2023-11-08 14:35:22 +01:00
MainEntriesSize = ( int ) ( IconImageWidth * Scale ) ; //xxx
2020-04-10 05:11:00 +02:00
row0TileSize = MainEntriesSize + ( int ) ( 16.f * Scale ) ;
2020-05-16 22:07:27 +02:00
// DBG("main entry size = %lld\n", MainEntriesSize);
2020-03-16 12:15:25 +01:00
}
2020-04-23 22:43:35 +02:00
if ( IconNameX . contains ( " selection_small " ) & & ( ! SelectionOnTop ) ) {
2023-11-08 14:35:22 +01:00
row1TileSize = ( int ) ( IconImageWidth * Scale ) ;
2020-03-16 12:15:25 +01:00
}
// not exclude BoundingRect from IconImage?
shape - > flags = 0 ; //invisible
shape = shapeNext ;
continue ; //while(shape) it is BoundingRect shape
}
shape - > flags = NSVG_VIS_VISIBLE ;
} //the shape in the group
2023-11-08 14:35:22 +01:00
shape = shapeNext ;
2020-03-16 12:15:25 +01:00
} //while shape
2023-07-20 19:25:33 +02:00
2023-11-08 14:35:22 +01:00
if ( IconImageWidth = = 0 | | IconImageHeight = = 0 ) {
return Status ;
}
2020-03-16 12:15:25 +01:00
float bounds [ 4 ] ;
2023-11-08 14:35:22 +01:00
nsvg__imageBounds ( SVGimage , bounds , IconNameX . c_str ( ) ) ;
2020-04-23 22:43:35 +02:00
if ( ( Id = = BUILTIN_ICON_BANNER ) & & IconNameX . contains ( " Banner " ) ) {
2020-04-10 05:11:00 +02:00
BannerPosX = ( int ) ( bounds [ 0 ] * Scale - CentreShift ) ;
if ( BannerPosX < 0 ) {
BannerPosX = 1 ; //one pixel
}
BannerPosY = ( int ) ( bounds [ 1 ] * Scale ) ;
2023-07-19 22:39:43 +02:00
// DBG("Banner position at parse [%lld,%lld]\n", BannerPosX, BannerPosY);
2020-03-22 11:48:13 +01:00
}
2023-11-08 14:35:22 +01:00
float Height = IconImageHeight * Scale ;
float Width = IconImageWidth * Scale ;
2023-07-19 22:39:43 +02:00
if ( Height < 0 | | Width < 0 ) {
return EFI_NOT_FOUND ;
}
// DBG("icon %s width=%f height=%f\n", IconNameX.c_str(), Width, Height);
int iWidth = ( ( int ) ( Width + 0.5f ) + 7 ) & ~ 0x07u ;
int iHeight = ( ( int ) ( Height + 0.5f ) + 7 ) & ~ 0x07u ;
2020-03-22 11:48:13 +01:00
XImage NewImage ( iWidth , iHeight ) ; //empty
2023-11-08 14:35:22 +01:00
2020-03-22 11:48:13 +01:00
float tx = 0.f , ty = 0.f ;
if ( ( Id ! = BUILTIN_ICON_BACKGROUND ) & &
2020-04-19 17:40:08 +02:00
( Id ! = BUILTIN_ICON_ANIME ) & &
2020-04-23 22:43:35 +02:00
! IconNameX . contains ( " Banner " ) ) {
2020-04-10 05:11:00 +02:00
float realWidth = ( bounds [ 2 ] - bounds [ 0 ] ) * Scale ;
float realHeight = ( bounds [ 3 ] - bounds [ 1 ] ) * Scale ;
2020-04-19 13:28:14 +02:00
// DBG("icon=%s width=%f realwidth=%f\n", IconNameX.c_str(), Width, realWidth);
2020-03-22 11:48:13 +01:00
tx = ( Width - realWidth ) * 0.5f ;
ty = ( Height - realHeight ) * 0.5f ;
}
2023-11-11 06:50:58 +01:00
NSVGrasterizer * rast = nsvg__createRasterizer ( ) ;
2023-11-08 14:35:22 +01:00
nsvgRasterize ( rast , SVGimage , bounds , IconNameX . c_str ( ) , tx , ty , Scale , Scale , ( UINT8 * ) NewImage . GetPixelPtr ( 0 , 0 ) , iWidth , iHeight , iWidth * 4 ) ;
2023-11-11 06:50:58 +01:00
nsvg__deleteRasterizer ( rast ) ;
2020-05-18 21:40:47 +02:00
* Image = NewImage ; //copy array
2020-05-14 09:03:06 +02:00
2020-03-13 14:34:36 +01:00
return EFI_SUCCESS ;
}
2019-09-03 11:58:42 +02:00
2023-11-06 22:46:13 +01:00
EFI_STATUS XTheme : : ParseSVGXTheme ( UINT8 * buffer , UINTN Size )
2019-09-03 11:58:42 +02:00
{
2020-03-29 18:17:27 +02:00
EFI_STATUS Status ;
2020-04-03 22:00:42 +02:00
2020-08-12 17:15:47 +02:00
Icons . setEmpty ( ) ;
2023-11-26 15:10:53 +01:00
# ifdef JIEF_DEBUG
2023-11-08 14:35:22 +01:00
displayFreeMemory ( " XTheme::ParseSVGXTheme begin " _XS8 ) ;
2023-11-26 15:10:53 +01:00
# endif
2023-11-08 14:35:22 +01:00
# if defined(JIEF_DEBUG) && defined(NANOSVG_MEMORY_ALLOCATION_TRACE)
2023-11-06 22:46:13 +01:00
if ( nsvg__nbDanglingPtr ( ) > 0 ) {
DBG ( " There is already dangling ptr. nano svg memory leak test not done \n " ) ;
} else {
2023-11-18 11:04:54 +01:00
apd < char * > buffer2 = ( char * ) malloc ( Size ) ;
2023-11-06 22:46:13 +01:00
memcpy ( buffer2 , buffer , Size ) ;
nvsg__memoryallocation_verbose = false ;
2023-11-11 06:50:58 +01:00
NSVGparser * p = nsvg__parse ( buffer2 , 72 , 1.f ) ; //the buffer will be modified, it is how nanosvg works
2023-11-08 14:35:22 +01:00
nsvg__deleteParser ( p ) ;
2023-11-06 22:46:13 +01:00
if ( nsvg__nbDanglingPtr ( ) > 0 ) {
2023-11-08 14:35:22 +01:00
nsvg__outputDanglingPtr ( ) ;
nvsg__memoryallocation_verbose = true ;
# if 1
// Do it a second time, to display all allocations and to be able to step in with debugger
memcpy ( buffer2 , buffer , Size ) ;
2023-11-11 06:50:58 +01:00
p = nsvg__parse ( buffer2 , 72 , 1.f ) ; //the buffer will be modified, it is how nanosvg works
2023-11-08 14:35:22 +01:00
nsvg__deleteParser ( p ) ;
nsvg__outputDanglingPtr ( ) ;
# endif
2023-11-06 22:46:13 +01:00
} else {
nvsg__memoryallocation_verbose = false ; // be sure that nvsg__memoryallocation_verbose is false, as it seems there is no memory leaks
}
}
# else
( void ) Size ;
# endif
2020-03-27 20:47:06 +01:00
// --- Parse theme.svg --- low case
2023-11-11 06:50:58 +01:00
NSVGparser * SVGParser = nsvg__parse ( ( CHAR8 * ) buffer , 72 , 1.f ) ; //the buffer will be modified, it is how nanosvg works// Jief : NEVER cast const to not const. Just change the parameter to not const !!! Nothing better to deceive.
2023-11-08 14:35:22 +01:00
NSVGimage * SVGimage = SVGParser - > image ;
2019-09-03 11:58:42 +02:00
if ( ! SVGimage ) {
2020-05-16 22:07:27 +02:00
// DBG("Theme not parsed!\n");
2019-09-03 11:58:42 +02:00
return EFI_NOT_STARTED ;
}
2020-03-27 20:47:06 +01:00
// --- Get scale as theme design height vs screen height
2020-04-18 08:39:47 +02:00
// must be svg view-box. This is Design Width and Heigth
2023-11-08 14:35:22 +01:00
float vbx = SVGParser - > viewWidth ;
float vby = SVGParser - > viewHeight ;
// DBG("Theme view-bounds: w=%f h=%f units=px\n", vbx, vby); //Theme view-bounds: w=1600.000000 h=900.000000 units=px
2020-03-27 20:47:06 +01:00
if ( vby > 1.0f ) {
SVGimage - > height = vby ;
2020-03-29 18:17:27 +02:00
} else {
2020-03-27 20:47:06 +01:00
SVGimage - > height = 768.f ; //default height
}
2020-03-29 18:17:27 +02:00
float ScaleF = UGAHeight / SVGimage - > height ;
2023-11-08 14:35:22 +01:00
// DBG("using scale %f\n", ScaleF); // using scale 0.666667
2020-03-31 16:25:07 +02:00
Scale = ScaleF ;
2020-03-29 18:17:27 +02:00
CentreShift = ( vbx * Scale - ( float ) UGAWidth ) * 0.5f ;
2020-03-27 20:47:06 +01:00
2020-03-29 18:17:27 +02:00
Background = XImage ( UGAWidth , UGAHeight ) ;
if ( ! BigBack . isEmpty ( ) ) {
BigBack . setEmpty ( ) ;
2020-03-27 20:47:06 +01:00
}
Status = EFI_NOT_FOUND ;
2023-11-08 14:35:22 +01:00
if ( ! Daylight ) {
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_BACKGROUND , " Background_night " _XS8 , & BigBack ) ;
2020-03-27 20:47:06 +01:00
}
if ( EFI_ERROR ( Status ) ) {
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_BACKGROUND , " Background " _XS8 , & BigBack ) ;
2020-03-27 20:47:06 +01:00
}
2023-11-08 14:35:22 +01:00
// DBG(" Background parsed [%lld, %lld]\n", BigBack.GetWidth(), BigBack.GetHeight()); //Background parsed [1067, 133]
2020-03-27 20:47:06 +01:00
// --- Make Banner
2020-03-29 18:17:27 +02:00
Banner . setEmpty ( ) ; //for the case of theme switch
2020-03-27 20:47:06 +01:00
Status = EFI_NOT_FOUND ;
2023-11-08 14:35:22 +01:00
if ( ! Daylight ) {
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_BANNER , " Banner_night " _XS8 , & Banner ) ;
2020-03-27 20:47:06 +01:00
}
if ( EFI_ERROR ( Status ) ) {
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_BANNER , " Banner " _XS8 , & Banner ) ;
2020-03-27 20:47:06 +01:00
}
2020-04-18 08:39:47 +02:00
// DBG("Banner parsed\n");
2020-03-29 18:17:27 +02:00
BanHeight = ( int ) ( Banner . GetHeight ( ) * Scale + 1.f ) ;
2023-11-08 14:35:22 +01:00
// DBG(" parsed banner->width=%lld height=%lld\n", Banner.GetWidth(), BanHeight); //parsed banner->width=467 height=89
2020-03-27 20:47:06 +01:00
// --- Make other icons
2020-04-03 22:00:42 +02:00
for ( INTN i = BUILTIN_ICON_FUNC_ABOUT ; i < = BUILTIN_CHECKBOX_CHECKED ; + + i ) {
2020-04-18 08:39:47 +02:00
if ( i = = BUILTIN_ICON_BANNER ) { //exclude "logo" as it done as Banner
2020-03-27 20:47:06 +01:00
continue ;
}
2020-05-16 21:30:29 +02:00
XIcon * NewIcon = new XIcon ( i , false ) ; //initialize without embedded
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , i , NewIcon - > Name , & NewIcon - > Image ) ;
2020-08-25 17:35:19 +02:00
// DBG("parse %s status %s\n", NewIcon->Name.c_str(), efiStrError(Status));
2020-04-18 08:39:47 +02:00
NewIcon - > Native = ! EFI_ERROR ( Status ) ;
2020-04-21 20:41:35 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2023-11-08 14:35:22 +01:00
ParseSVGXIcon ( SVGParser , i , NewIcon - > Name + " _night " _XS8 , & NewIcon - > ImageNight ) ;
2020-04-06 11:15:36 +02:00
}
2020-08-25 17:35:19 +02:00
// DBG("parse night %s status %s\n", NewIcon->Name.c_str(), efiStrError(Status));
2020-04-02 21:27:10 +02:00
Icons . AddReference ( NewIcon , true ) ;
2020-05-16 21:30:29 +02:00
if ( EFI_ERROR ( Status ) ) {
if ( i > = BUILTIN_ICON_VOL_INTERNAL_HFS & & i < = BUILTIN_ICON_VOL_INTERNAL_REC ) {
// call to GetIconAlt will get alternate/embedded into Icon if missing
GetIconAlt ( i , BUILTIN_ICON_VOL_INTERNAL ) ;
} else if ( i = = BUILTIN_SELECTION_BIG ) {
GetIconAlt ( i , BUILTIN_SELECTION_SMALL ) ;
}
2020-04-21 20:41:35 +02:00
}
}
2020-04-23 13:36:35 +02:00
// --- Make other OSes
for ( INTN i = ICON_OTHER_OS ; i < IconsNamesSize ; + + i ) {
2024-01-08 11:27:38 +01:00
if ( IconsNames [ i ] . isEmpty ( ) ) break ;
2020-05-16 21:30:29 +02:00
XIcon * NewIcon = new XIcon ( i , false ) ; //initialize without embedded
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , i , NewIcon - > Name , & NewIcon - > Image ) ;
2020-08-25 17:35:19 +02:00
// DBG("parse %s i=%lld status %s\n", NewIcon->Name.c_str(), i, efiStrError(Status));
2020-04-23 13:36:35 +02:00
NewIcon - > Native = ! EFI_ERROR ( Status ) ;
if ( ! EFI_ERROR ( Status ) ) {
2023-11-08 14:35:22 +01:00
ParseSVGXIcon ( SVGParser , i , NewIcon - > Name + " _night " _XS8 , & NewIcon - > ImageNight ) ;
2020-04-23 13:36:35 +02:00
}
Icons . AddReference ( NewIcon , true ) ;
}
2020-04-21 20:41:35 +02:00
//selection for bootcampstyle
2020-05-16 21:30:29 +02:00
XIcon * NewIcon = new XIcon ( BUILTIN_ICON_SELECTION ) ;
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_SELECTION , " selection_indicator " _XS8 , & NewIcon - > Image ) ;
2020-04-21 20:41:35 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_SELECTION , " selection_indicator_night " _XS8 , & NewIcon - > ImageNight ) ;
2020-03-28 19:34:23 +01:00
}
2020-04-21 20:41:35 +02:00
Icons . AddReference ( NewIcon , true ) ;
2020-03-28 19:34:23 +01:00
2020-04-02 16:03:58 +02:00
//selections
2020-04-06 11:15:36 +02:00
SelectionBackgroundPixel . Red = ( SelectionColor > > 24 ) & 0xFF ;
SelectionBackgroundPixel . Green = ( SelectionColor > > 16 ) & 0xFF ;
SelectionBackgroundPixel . Blue = ( SelectionColor > > 8 ) & 0xFF ;
SelectionBackgroundPixel . Reserved = ( SelectionColor > > 0 ) & 0xFF ;
2020-05-16 21:30:29 +02:00
//TODO make SelectionImages to be XIcon
2023-11-07 17:40:28 +01:00
SelectionImages [ 0 ] = GetIcon ( BUILTIN_SELECTION_BIG ) . GetBest ( ! Daylight ) ;
SelectionImages [ 2 ] = GetIcon ( BUILTIN_SELECTION_SMALL ) . GetBest ( ! Daylight ) ;
SelectionImages [ 4 ] = GetIcon ( BUILTIN_ICON_SELECTION ) . GetBest ( ! Daylight ) ;
2020-04-06 11:15:36 +02:00
2020-04-04 05:46:41 +02:00
//buttons
for ( INTN i = BUILTIN_RADIO_BUTTON ; i < = BUILTIN_CHECKBOX_CHECKED ; + + i ) {
2023-11-07 17:40:28 +01:00
Buttons [ i - BUILTIN_RADIO_BUTTON ] = GetIcon ( i ) . GetBest ( ! Daylight ) ;
2020-04-04 05:46:41 +02:00
}
2020-04-16 06:45:53 +02:00
2021-09-28 10:28:45 +02:00
TypeSVG = true ;
2020-03-29 18:17:27 +02:00
ThemeDesignHeight = ( int ) SVGimage - > height ;
ThemeDesignWidth = ( int ) SVGimage - > width ;
if ( SelectionOnTop ) {
row0TileSize = ( INTN ) ( 144.f * Scale ) ;
row1TileSize = ( INTN ) ( 64.f * Scale ) ;
MainEntriesSize = ( INTN ) ( 128.f * Scale ) ;
2020-03-28 19:34:23 +01:00
}
2023-11-08 14:35:22 +01:00
// DBG("parsing svg theme finished\n");
// It looks like the fonts are self-contained. So we can just keep fontsDB pointer and copy textfaces and delete the parser.
// I'm not sure if font are self contained with all theme. To avoid deleting, just comment out the next line.
// SVGParser will still be deleted at XTheme dtor. So it's not a memory leak.
fontsDB = SVGParser - > fontsDB ;
for ( size_t i = 0 ; i < sizeof ( textFace ) / sizeof ( textFace [ 0 ] ) ; i + + ) {
textFace [ i ] = SVGParser - > textFace [ i ] ;
}
SVGParser - > fontsDB = NULL ; // To avoid nsvg__deleteParser to delete it;
nsvg__deleteParser ( SVGParser ) ; // comment out this line and the next to keep the parser memory, in case of doubt that font are dependent.
SVGParser = NULL ;
2023-11-26 15:10:53 +01:00
# ifdef JIEF_DEBUG
2023-11-08 14:35:22 +01:00
displayFreeMemory ( " XTheme::ParseSVGXTheme end " _XS8 ) ;
2023-11-26 15:10:53 +01:00
# endif
2020-05-20 19:56:01 +02:00
return EFI_SUCCESS ;
2020-03-27 20:47:06 +01:00
}
2019-09-03 11:58:42 +02:00
2023-11-08 14:35:22 +01:00
// 2023-11 This is currently never called.
EFI_STATUS XTheme : : LoadSvgFrame ( NSVGparser * SVGParser , INTN i , OUT XImage * XFrame )
2020-04-14 18:52:13 +02:00
{
EFI_STATUS Status = EFI_NOT_FOUND ;
2020-08-11 08:00:19 +02:00
XString8 XFrameName = S8Printf ( " frame_%04lld " , i + 1 ) ;
2023-11-08 14:35:22 +01:00
Status = ParseSVGXIcon ( SVGParser , BUILTIN_ICON_ANIME , XFrameName , XFrame ) ; //svg anime will be full redesigned
2020-04-14 18:52:13 +02:00
if ( EFI_ERROR ( Status ) ) {
2020-08-25 17:35:19 +02:00
DBG ( " frame '%s' not loaded, status=%s \n " , XFrameName . c_str ( ) , efiStrError ( Status ) ) ;
2020-04-14 18:52:13 +02:00
}
return Status ;
}
2020-03-21 08:34:28 +01:00
// it is not draw, it is render and mainly used in egRenderText
2020-03-29 18:17:27 +02:00
// which is used in icns.cpp as an icon replacement if no image found, looks like not used
2020-03-21 08:34:28 +01:00
// in menu.cpp 3 places
2019-09-03 11:58:42 +02:00
//textType = 0-help 1-message 2-menu 3-test
//return text width in pixels
2020-04-10 05:11:00 +02:00
//it is not theme member!
2023-11-08 14:35:22 +01:00
INTN renderSVGtext ( XImage * TextBufferXY_ptr , INTN posX , INTN posY , const textFaces & textFace , const XStringW & string , UINTN Cursor )
2020-03-20 21:10:08 +01:00
{
2020-03-31 17:59:35 +02:00
XImage & TextBufferXY = * TextBufferXY_ptr ;
2020-03-20 21:10:08 +01:00
INTN Width ;
2023-07-20 19:25:33 +02:00
2020-03-20 21:10:08 +01:00
NSVGparser * p ;
NSVGrasterizer * rast ;
2023-11-08 14:35:22 +01:00
if ( ! textFace . valid ) {
DBG ( " invalid fontface! \n " ) ;
2020-03-20 21:10:08 +01:00
return 0 ;
}
2023-11-08 14:35:22 +01:00
NSVGfont * fontSVG = textFace . font ;
UINT32 color = textFace . color ;
INTN Height = ( INTN ) ( textFace . size * ThemeX - > Scale ) ;
2020-03-20 21:10:08 +01:00
float Scale , sy ;
float x , y ;
if ( ! fontSVG ) {
2020-03-21 08:34:28 +01:00
DBG ( " no font for renderSVGtext \n " ) ;
2020-03-20 21:10:08 +01:00
return 0 ;
}
p = nsvg__createParser ( ) ;
if ( ! p ) {
return 0 ;
}
2023-11-06 22:46:13 +01:00
NSVGtext * text = ( NSVGtext * ) nsvg__alloczero ( sizeof ( NSVGtext ) , " renderSVGtext " _XS8 ) ; // use nsvg__alloczero method so it won't panic when it's freed.
2020-03-20 21:10:08 +01:00
if ( ! text ) {
return 0 ;
}
text - > font = fontSVG ;
text - > fontColor = color ;
text - > fontSize = ( float ) Height ;
nsvg__xformIdentity ( text - > xform ) ;
p - > text = text ;
Width = TextBufferXY . GetWidth ( ) ;
2020-04-10 05:11:00 +02:00
if ( fontSVG - > unitsPerEm < 1.f ) {
2020-03-20 21:10:08 +01:00
fontSVG - > unitsPerEm = 1000.f ;
}
float fH = fontSVG - > bbox [ 3 ] - fontSVG - > bbox [ 1 ] ; //1250
if ( fH = = 0.f ) {
2020-03-26 18:00:53 +01:00
DBG ( " wrong font: %f \n " , fontSVG - > unitsPerEm ) ;
2023-11-11 06:50:58 +01:00
nsvg__dumpFloat ( " Font bbox " , fontSVG - > bbox , 4 ) ;
2020-04-10 05:11:00 +02:00
fH = ( fontSVG - > unitsPerEm > 1.f ) ? fontSVG - > unitsPerEm : 1000.0f ; //1000
2020-03-20 21:10:08 +01:00
}
sy = ( float ) Height / fH ; //(float)fontSVG->unitsPerEm; // 260./1250.
Scale = sy ;
x = ( float ) posX ; //0.f;
y = ( float ) posY + fontSVG - > bbox [ 1 ] * Scale ;
2021-09-28 10:28:45 +02:00
p - > isText = true ;
2020-04-23 15:20:48 +02:00
size_t len = string . length ( ) ;
for ( size_t i = 0 ; i < len ; i + + ) {
CHAR16 letter = string . char16At ( i ) ;
2020-03-20 21:10:08 +01:00
if ( ! letter ) {
break ;
}
2020-04-10 05:11:00 +02:00
// DBG("add letter 0x%X\n", letter);
2020-03-20 21:10:08 +01:00
if ( i = = Cursor ) {
2023-11-11 06:50:58 +01:00
nsvg__addLetter ( p , 0x5F , x , y , sy , color ) ;
2020-03-20 21:10:08 +01:00
}
2023-11-11 06:50:58 +01:00
x = nsvg__addLetter ( p , letter , x , y , sy , color ) ;
2020-03-20 21:10:08 +01:00
} //end of string
p - > image - > realBounds [ 0 ] = fontSVG - > bbox [ 0 ] * Scale ;
p - > image - > realBounds [ 1 ] = fontSVG - > bbox [ 1 ] * Scale ;
p - > image - > realBounds [ 2 ] = fontSVG - > bbox [ 2 ] * Scale + x ; //last bound
p - > image - > realBounds [ 3 ] = fontSVG - > bbox [ 3 ] * Scale ;
2023-11-11 06:50:58 +01:00
rast = nsvg__createRasterizer ( ) ;
2020-03-20 21:10:08 +01:00
nsvgRasterize ( rast , p - > image , 0 , 0 , 1.f , 1.f , ( UINT8 * ) TextBufferXY . GetPixelPtr ( 0 , 0 ) ,
2020-03-22 11:48:13 +01:00
( int ) TextBufferXY . GetWidth ( ) , ( int ) TextBufferXY . GetHeight ( ) , ( int ) ( Width * 4 ) ) ;
2020-03-20 21:10:08 +01:00
float RealWidth = p - > image - > realBounds [ 2 ] - p - > image - > realBounds [ 0 ] ;
2023-11-11 06:50:58 +01:00
nsvg__deleteRasterizer ( rast ) ;
2023-11-08 14:35:22 +01:00
nsvg__deleteParser ( p ) ; // this deletes p->text;
// nsvgDelete(p->image);
// TODO delete parser p and p->text?
2020-03-20 21:10:08 +01:00
return ( INTN ) RealWidth ; //x;
}
2020-05-16 22:07:27 +02:00
2023-11-08 14:35:22 +01:00
INTN renderSVGtext ( XImage * TextBufferXY_ptr , INTN posX , INTN posY , INTN textType , const XStringW & string , UINTN Cursor )
{
if ( ! ThemeX - > getTextFace ( textType ) . valid ) {
for ( decltype ( textType ) i = 0 ; i < 4 ; i + + ) {
if ( ThemeX - > getTextFace ( i ) . valid ) {
textType = i ;
break ;
}
}
}
if ( ! ThemeX - > getTextFace ( textType ) . valid ) {
DBG ( " valid fontface not found! \n " ) ;
return 0 ;
}
return renderSVGtext ( TextBufferXY_ptr , posX , posY , ThemeX - > getTextFace ( textType ) , string , Cursor ) ;
}
2020-10-03 19:02:31 +02:00
void testSVG ( )
2019-09-03 11:58:42 +02:00
{
do {
EFI_STATUS Status ;
UINT8 * FileData = NULL ;
UINTN FileDataLength = 0 ;
2023-07-19 22:39:43 +02:00
INTN Width = 192 , Height = 192 ;
2019-09-03 11:58:42 +02:00
# if TEST_MATH
//Test mathematique
//#define fabsf(x) ((x >= 0.0f)?x:(-x))
# define pr(x) (int)fabsf(x), (int)fabsf((x - (int)x) * 1000000.0f)
int i ;
float x , y1 , y2 ;
// CHAR8 Str[128];
DBG ( " Test float: -%d.%06d \n " , pr ( - 0.7612f ) ) ;
for ( i = 0 ; i < 15 ; i + + ) {
x = ( PI ) / 30.0f * i ;
y1 = SinF ( x ) ;
y2 = CosF ( x ) ;
DBG ( " x=%d: %d.%06d " , i * 6 , pr ( x ) ) ;
DBG ( " sinx=%c%d.%06d " , ( y1 < 0 ) ? ' - ' : ' ' , pr ( y1 ) ) ;
DBG ( " cosx=%c%d.%06d \n " , ( y2 < 0 ) ? ' - ' : ' ' , pr ( y2 ) ) ;
y1 = Atan2F ( y1 , y2 ) ;
DBG ( " atan2x=%c%d.%06d " , ( y1 < 0 ) ? ' - ' : ' ' , pr ( y1 ) ) ;
y1 = AcosF ( y2 ) ;
DBG ( " acos=%c%d.%06d " , ( y1 < 0 ) ? ' - ' : ' ' , pr ( y1 ) ) ;
y1 = SqrtF ( x ) ;
DBG ( " sqrt=%d.%06d " , pr ( y1 ) ) ;
y1 = CeilF ( x ) ;
DBG ( " ceil=%c%d.%06d \n " , ( y1 < 0 ) ? ' - ' : ' ' , pr ( y1 ) ) ;
}
# undef pr
# endif
NSVGparser * p ;
# if TEST_DITHER
{
2021-09-28 10:28:45 +02:00
EG_IMAGE * RndImage = egCreateImage ( 256 , 256 , false ) ;
2019-09-03 11:58:42 +02:00
INTN i , j ;
EG_PIXEL pixel = WhitePixel ;
for ( i = 0 ; i < 256 ; i + + ) {
for ( j = 0 ; j < 256 ; j + + ) {
pixel . b = 0x40 + ( dither ( ( float ) j / 32.0f , 1 ) * 8 ) ;
pixel . r = 0x0 ;
pixel . g = 0x0 ;
// if (i==1) {
2020-03-25 19:32:44 +01:00
// DBG("r=%X g=%X\n", pixel.r, pixel.g);
2019-09-03 11:58:42 +02:00
// }
RndImage - > PixelData [ i * 256 + j ] = pixel ;
}
}
BltImageAlpha ( RndImage ,
20 ,
20 ,
& MenuBackgroundPixel ,
16 ) ;
}
# endif
# if TEST_SVG_IMAGE
2023-06-28 19:05:58 +02:00
2023-11-11 06:50:58 +01:00
NSVGrasterizer * rast = nsvg__createRasterizer ( ) ;
2020-04-01 14:57:32 +02:00
// EG_IMAGE *NewImage;
2019-09-03 11:58:42 +02:00
NSVGimage * SVGimage ;
float Scale , ScaleX , ScaleY ;
// load file
2020-10-03 19:02:31 +02:00
Status = egLoadFile ( & self . getSelfVolumeRootDir ( ) , L " Sample.svg " , & FileData , & FileDataLength ) ;
2019-09-03 11:58:42 +02:00
if ( ! EFI_ERROR ( Status ) ) {
//Parse XML to vector data
2023-11-11 06:50:58 +01:00
p = nsvg__parse ( ( CHAR8 * ) FileData , 72 , 1.f ) ;
2019-09-03 11:58:42 +02:00
SVGimage = p - > image ;
DBG ( " Test image width=%d heigth=%d \n " , ( int ) ( SVGimage - > width ) , ( int ) ( SVGimage - > height ) ) ;
2023-07-20 19:25:33 +02:00
2019-09-03 11:58:42 +02:00
// Rasterize
2020-04-01 14:57:32 +02:00
XImage NewImage ( Width , Height ) ;
2019-09-03 11:58:42 +02:00
if ( SVGimage - > width < = 0 ) SVGimage - > width = ( float ) Width ;
if ( SVGimage - > height < = 0 ) SVGimage - > height = ( float ) Height ;
ScaleX = Width / SVGimage - > width ;
ScaleY = Height / SVGimage - > height ;
Scale = ( ScaleX > ScaleY ) ? ScaleY : ScaleX ;
float tx = 0 ; //-SVGimage->realBounds[0] * Scale;
float ty = 0 ; //-SVGimage->realBounds[1] * Scale;
2020-04-19 13:23:23 +02:00
DBG ( " timing rasterize start tx=%f ty=%f \n " , tx , ty ) ; //the aim is measure duration
2020-04-01 14:57:32 +02:00
nsvgRasterize ( rast , SVGimage , tx , ty , Scale , Scale , ( UINT8 * ) NewImage . GetPixelPtr ( 0 , 0 ) , ( int ) Width , ( int ) Height , ( int ) Width * 4 ) ;
DBG ( " timing rasterize end \n " ) ;
NewImage . Draw ( ( UGAWidth - Width ) / 2 ,
( UGAHeight - Height ) / 2 ) ;
2019-09-03 11:58:42 +02:00
FreePool ( FileData ) ;
FileData = NULL ;
2020-04-01 14:57:32 +02:00
//
2020-03-10 14:00:02 +01:00
// nsvg__deleteParser(p);
2023-11-11 06:50:58 +01:00
nsvg__deleteRasterizer ( rast ) ;
2020-03-06 15:02:06 +01:00
2019-09-03 11:58:42 +02:00
}
2020-03-06 15:02:06 +01:00
2019-09-03 11:58:42 +02:00
# endif
//Test text
Height = 80 ;
Width = UGAWidth - 200 ;
2020-04-04 07:50:30 +02:00
// DBG("create test textbuffer\n");
2020-03-21 08:34:28 +01:00
XImage TextBufferXY ( Width , Height ) ;
2020-10-03 19:02:31 +02:00
Status = egLoadFile ( & self . getSelfVolumeRootDir ( ) , L " Font.svg " , & FileData , & FileDataLength ) ;
2020-08-25 17:35:19 +02:00
DBG ( " test Font.svg loaded status=%s \n " , efiStrError ( Status ) ) ;
2019-09-03 11:58:42 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2023-11-11 06:50:58 +01:00
p = nsvg__parse ( ( CHAR8 * ) FileData , 72 , 1.f ) ;
2019-09-03 11:58:42 +02:00
if ( ! p ) {
DBG ( " font not parsed \n " ) ;
break ;
}
2023-07-20 19:25:33 +02:00
2023-11-08 14:35:22 +01:00
textFaces textFace ;
textFace . font = p - > currentFont ;
textFace . color = NSVG_RGBA ( 0x80 , 0xFF , 0 , 255 ) ;
textFace . size = Height ;
textFace . valid = true ;
2020-03-25 19:32:44 +01:00
// DBG("font parsed family=%s\n", p->font->fontFamily);
2019-09-03 11:58:42 +02:00
FreePool ( FileData ) ;
// Scale = Height / fontSVG->unitsPerEm;
2020-04-10 12:04:21 +02:00
2020-03-31 17:59:35 +02:00
renderSVGtext ( & TextBufferXY , 0 , 0 , 3 , XStringW ( ) . takeValueFrom ( " Clover Кловер " ) , 1 ) ;
2019-09-03 11:58:42 +02:00
// DBG("text ready to blit\n");
2020-03-21 08:34:28 +01:00
TextBufferXY . Draw ( ( UGAWidth - Width ) / 2 ,
2020-03-25 18:49:01 +01:00
( UGAHeight - Height ) / 2 ) ;
2019-09-03 11:58:42 +02:00
// nsvg__deleteParser(p);
// DBG("draw finished\n");
}
} while ( 0 ) ;
}