mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-27 12:15:19 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
796 lines
24 KiB
C
796 lines
24 KiB
C
/*
|
|
* Additional procedures for vector theme support
|
|
*
|
|
* Slice, 2018
|
|
*
|
|
*/
|
|
|
|
|
|
#include "nanosvg.h"
|
|
#include "FloatLib.h"
|
|
|
|
#include "lodepng.h"
|
|
|
|
#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
|
|
|
|
#define TEST_MATH 0
|
|
#define TEST_SVG_IMAGE 1
|
|
#define TEST_SIZEOF 0
|
|
#define TEST_FONT 0
|
|
#define TEST_DITHER 0
|
|
|
|
#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))
|
|
|
|
extern VOID
|
|
WaitForKeyPress(CHAR16 *Message);
|
|
|
|
extern void DumpFloat2 (char* s, float* t, int N);
|
|
extern EG_IMAGE *BackgroundImage;
|
|
extern EG_IMAGE *Banner;
|
|
extern EG_IMAGE *BigBack;
|
|
extern VOID *fontsDB;
|
|
extern INTN BanHeight;
|
|
extern INTN row0TileSize;
|
|
extern INTN row1TileSize;
|
|
extern INTN FontWidth;
|
|
extern UINTN NumFrames;
|
|
extern UINTN FrameTime;
|
|
extern BOOLEAN DayLight;
|
|
|
|
textFaces textFace[4]; //0-help 1-message 2-menu 3-test
|
|
NSVGparser *mainParser = NULL; //it must be global variable
|
|
|
|
|
|
EFI_STATUS ParseSVGIcon(NSVGparser *p, INTN Id, CHAR8 *IconName, float Scale, EG_IMAGE **Image)
|
|
{
|
|
EFI_STATUS Status = EFI_NOT_FOUND;
|
|
NSVGimage *SVGimage;
|
|
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
|
SVGimage = p->image;
|
|
NSVGshape *shape;
|
|
NSVGgroup *group;
|
|
NSVGimage *IconImage; // = (NSVGimage*)AllocateZeroPool(sizeof(NSVGimage));
|
|
NSVGshape *shapeNext, *shapesTail=NULL, *shapePrev;
|
|
// INTN ClipCount = 0;
|
|
|
|
NSVGparser* p2 = nsvg__createParser();
|
|
IconImage = p2->image;
|
|
|
|
shape = SVGimage->shapes;
|
|
shapePrev = NULL;
|
|
while (shape) {
|
|
group = shape->group;
|
|
shapeNext = shape->next;
|
|
while (group) {
|
|
if (strcmp(group->id, IconName) == 0) {
|
|
break;
|
|
}
|
|
group = group->next;
|
|
}
|
|
|
|
if (group) { //the shape is in the group
|
|
// keep this sample for debug purpose
|
|
/* DBG("found shape %a", shape->id);
|
|
DBG(" from group %a\n", group->id);
|
|
if ((Id == BUILTIN_SELECTION_BIG) ||
|
|
(Id == BUILTIN_ICON_BACKGROUND) ||
|
|
(Id == BUILTIN_ICON_BANNER)) {
|
|
shape->debug = TRUE;
|
|
} */
|
|
if (GlobalConfig.BootCampStyle && (strstr(IconName, "selection_big") != NULL)) {
|
|
shape->opacity = 0.f;
|
|
}
|
|
if (strstr(shape->id, "BoundingRect") != NULL) {
|
|
//there is bounds after nsvgParse()
|
|
IconImage->width = shape->bounds[2] - shape->bounds[0];
|
|
IconImage->height = shape->bounds[3] - shape->bounds[1];
|
|
if (!IconImage->height) {
|
|
IconImage->height = 200;
|
|
}
|
|
// if (Id == BUILTIN_ICON_BACKGROUND || Id == BUILTIN_ICON_BANNER) {
|
|
// DBG("IconImage size [%d,%d]\n", (int)IconImage->width, (int)IconImage->height);
|
|
// DBG("IconImage left corner x=%s y=%s\n", PoolPrintFloat(IconImage->realBounds[0]), PoolPrintFloat(IconImage->realBounds[1]));
|
|
// DumpFloat2("IconImage real bounds", IconImage->realBounds, 4);
|
|
// }
|
|
if ((strstr(IconName, "selection_big") != NULL) && (!GlobalConfig.SelectionOnTop)) {
|
|
GlobalConfig.MainEntriesSize = (int)(IconImage->width * Scale); //xxx
|
|
row0TileSize = GlobalConfig.MainEntriesSize + (int)(16.f * Scale);
|
|
DBG("main entry size = %d\n", GlobalConfig.MainEntriesSize);
|
|
}
|
|
if ((strstr(IconName, "selection_small") != NULL) && (!GlobalConfig.SelectionOnTop)) {
|
|
row1TileSize = (int)(IconImage->width * Scale);
|
|
}
|
|
|
|
// not exclude BoundingRect from IconImage?
|
|
shape->flags = 0; //invisible
|
|
if (shapePrev) {
|
|
shapePrev->next = shapeNext;
|
|
} else {
|
|
SVGimage->shapes = shapeNext;
|
|
}
|
|
shape = shapeNext;
|
|
continue; //while(shape) it is BoundingRect shape
|
|
|
|
// shape->opacity = 0.3f;
|
|
}
|
|
shape->flags = NSVG_VIS_VISIBLE;
|
|
// Add to tail
|
|
// ClipCount += shape->clip.count;
|
|
if (IconImage->shapes == NULL)
|
|
IconImage->shapes = shape;
|
|
else
|
|
shapesTail->next = shape;
|
|
shapesTail = shape;
|
|
if (shapePrev) {
|
|
shapePrev->next = shapeNext;
|
|
} else {
|
|
SVGimage->shapes = shapeNext;
|
|
}
|
|
shapePrev->next = shapeNext;
|
|
} //the shape in the group
|
|
else {
|
|
shapePrev = shape;
|
|
}
|
|
shape = shapeNext;
|
|
} //while shape
|
|
shapesTail->next = NULL;
|
|
|
|
//add clipPaths //xxx
|
|
NSVGclipPath* clipPaths = SVGimage->clipPaths;
|
|
NSVGclipPath* clipNext = NULL;
|
|
while (clipPaths) {
|
|
// ClipCount += clipPaths->shapes->clip.count;
|
|
group = clipPaths->shapes->group;
|
|
clipNext = clipPaths->next;
|
|
while (group) {
|
|
if (strcmp(group->id, IconName) == 0) {
|
|
break;
|
|
}
|
|
group = group->parent;
|
|
}
|
|
if (group) {
|
|
DBG("found clipPaths for %a\n", IconName);
|
|
IconImage->clipPaths = SVGimage->clipPaths;
|
|
break;
|
|
}
|
|
clipPaths = clipNext;
|
|
}
|
|
// DBG("found %d clips for %a\n", ClipCount, IconName);
|
|
// if (ClipCount) { //Id == BUILTIN_ICON_BANNER) {
|
|
// IconImage->clipPaths = SVGimage->clipPaths;
|
|
// }
|
|
|
|
|
|
float bounds[4];
|
|
bounds[0] = FLT_MAX;
|
|
bounds[1] = FLT_MAX;
|
|
bounds[2] = -FLT_MAX;
|
|
bounds[3] = -FLT_MAX;
|
|
nsvg__imageBounds(p2, bounds);
|
|
CopyMem(IconImage->realBounds, bounds, 4*sizeof(float));
|
|
|
|
if ((Id == BUILTIN_ICON_BANNER) && (strstr(IconName, "Banner") != NULL)) {
|
|
GlobalConfig.BannerPosX = (int)(bounds[0] * Scale - GlobalConfig.CentreShift);
|
|
GlobalConfig.BannerPosY = (int)(bounds[1] * Scale);
|
|
DBG("Banner position at parse [%d,%d]\n", GlobalConfig.BannerPosX, GlobalConfig.BannerPosY);
|
|
}
|
|
|
|
float Height = IconImage->height * Scale;
|
|
float Width = IconImage->width * Scale;
|
|
// DBG("icon %a width=%s height=%s\n", IconName, PoolPrintFloat(Width), PoolPrintFloat(Height));
|
|
int iWidth = (int)(Width + 0.5f);
|
|
int iHeight = (int)(Height + 0.5f);
|
|
EG_IMAGE *NewImage = egCreateFilledImage(iWidth, iHeight, TRUE, &MenuBackgroundPixel);
|
|
|
|
if (IconImage->shapes == NULL) {
|
|
*Image = NewImage;
|
|
return Status;
|
|
}
|
|
|
|
// DBG("begin rasterize %a\n", IconName);
|
|
float tx = 0.f, ty = 0.f;
|
|
if ((Id != BUILTIN_ICON_BACKGROUND) &&
|
|
(Id != BUILTIN_ICON_ANIME) &&
|
|
(strstr(IconName, "Banner") == NULL)) {
|
|
float realWidth = (bounds[2] - bounds[0]) * Scale;
|
|
float realHeight = (bounds[3] - bounds[1]) * Scale;
|
|
tx = (Width - realWidth) * 0.5f;
|
|
ty = (Height - realHeight) * 0.5f;
|
|
}
|
|
|
|
nsvgRasterize(rast, IconImage, tx,ty,Scale,Scale, (UINT8*)NewImage->PixelData, iWidth, iHeight, iWidth*4);
|
|
// DBG("%a rastered, blt\n", IconImage);
|
|
#if 0
|
|
BltImageAlpha(NewImage,
|
|
(int)(UGAWidth - NewImage->Width) / 2,
|
|
(int)(UGAHeight * 0.05f),
|
|
&MenuBackgroundPixel,
|
|
16);
|
|
// WaitForKeyPress(L"waiting for key press...\n");
|
|
#endif
|
|
nsvgDeleteRasterizer(rast);
|
|
// nsvg__deleteParser(p2);
|
|
// nsvgDelete(p2->image);
|
|
*Image = NewImage;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS ParseSVGTheme(CONST CHAR8* buffer, TagPtr * dict, UINT32 bufSize)
|
|
{
|
|
EFI_STATUS Status;
|
|
NSVGimage *SVGimage;
|
|
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
|
|
|
// --- Parse theme.svg --- low case
|
|
mainParser = nsvgParse((CHAR8*)buffer, 72, 1.f);
|
|
SVGimage = mainParser->image;
|
|
if (!SVGimage) {
|
|
DBG("Theme not parsed!\n");
|
|
return EFI_NOT_STARTED;
|
|
}
|
|
|
|
// --- Get scale as theme design height vs screen height
|
|
float Scale;
|
|
// must be svg view-box
|
|
float vbx = mainParser->viewWidth;
|
|
float vby = mainParser->viewHeight;
|
|
DBG("Theme view-bounds: w=%d h=%d units=%a\n", (int)vbx, (int)vby, "px");
|
|
if (vby > 1.0f) {
|
|
SVGimage->height = vby;
|
|
} else {
|
|
SVGimage->height = 768.f; //default height
|
|
}
|
|
Scale = UGAHeight / SVGimage->height;
|
|
DBG("using scale %s\n", PoolPrintFloat(Scale));
|
|
GlobalConfig.Scale = Scale;
|
|
GlobalConfig.CentreShift = (vbx * Scale - (float)UGAWidth) * 0.5f;
|
|
|
|
if (mainParser->font) {
|
|
DBG("theme contains font-family=%a\n", mainParser->font->fontFamily);
|
|
}
|
|
|
|
#if 0
|
|
// --- Create rastered font
|
|
if (fontSVG) {
|
|
if (p->font) {
|
|
FontHeight = (int)(textFace[2].size * Scale); //as in MenuRows
|
|
DBG("Menu font scaled height=%d color=%x\n", FontHeight, textFace[2].color);
|
|
}
|
|
if (!FontHeight) FontHeight = 16; //xxx
|
|
if (fontSVG->fontFamily[0] < 0x30) {
|
|
AsciiStrCpyS(fontSVG->fontFamily, 64, fontSVG->id);
|
|
}
|
|
RenderSVGfont(fontSVG, p->fontColor);
|
|
DBG("font %a parsed\n", fontSVG->fontFamily);
|
|
}
|
|
#endif
|
|
|
|
// --- Make background
|
|
BackgroundImage = egCreateFilledImage(UGAWidth, UGAHeight, TRUE, &BlackPixel);
|
|
if (BigBack) {
|
|
egFreeImage(BigBack);
|
|
BigBack = NULL;
|
|
}
|
|
Status = EFI_NOT_FOUND;
|
|
if (!DayLight) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_BACKGROUND, "Background_night", Scale, &BigBack);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_BACKGROUND, "Background", Scale, &BigBack);
|
|
}
|
|
DBG("background parsed\n");
|
|
|
|
// --- Make Banner
|
|
if (Banner) {
|
|
egFreeImage(Banner);
|
|
Banner = NULL;
|
|
}
|
|
Status = EFI_NOT_FOUND;
|
|
if (!DayLight) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_BANNER, "Banner_night", Scale, &Banner);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_BANNER, "Banner", Scale, &Banner);
|
|
}
|
|
DBG("Banner parsed\n");
|
|
|
|
BuiltinIconTable[BUILTIN_ICON_BANNER].Image = Banner;
|
|
BanHeight = (int)(Banner->Height * Scale + 1.f);
|
|
DBG("parsed banner->width=%d\n", Banner->Width);
|
|
|
|
// --- Make other icons
|
|
INTN i = BUILTIN_ICON_FUNC_ABOUT;
|
|
CHAR8 *IconName;
|
|
while (BuiltinIconTable[i].Path) {
|
|
if (i == BUILTIN_ICON_BANNER) {
|
|
i++;
|
|
continue;
|
|
}
|
|
CHAR16 *IconPath = BuiltinIconTable[i].Path;
|
|
// DBG("next table icon=%s\n", IconPath);
|
|
CHAR16 *ptr = StrStr(IconPath, L"\\");
|
|
if (!ptr) {
|
|
ptr = IconPath;
|
|
} else {
|
|
ptr++;
|
|
}
|
|
// DBG("next icon=%s Len=%d\n", ptr, StrLen(ptr));
|
|
UINTN Size = StrLen(ptr)+1;
|
|
IconName = AllocateZeroPool(Size);
|
|
UnicodeStrToAsciiStrS(ptr, IconName, Size);
|
|
// DBG("search for icon name %a\n", IconName);
|
|
CHAR8 IconNight[64];
|
|
AsciiStrCpyS(IconNight, 64, IconName);
|
|
AsciiStrCatS(IconNight, 64, "_night");
|
|
Status = EFI_NOT_FOUND;
|
|
if (!DayLight) {
|
|
Status = ParseSVGIcon(mainParser, i, IconNight, Scale, &BuiltinIconTable[i].Image);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
Status = ParseSVGIcon(mainParser, i, IconName, Scale, &BuiltinIconTable[i].Image);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
DBG(" icon %d not parsed take common %s\n", i, BuiltinIconTable[i].Path);
|
|
if ((i >= BUILTIN_ICON_VOL_EXTERNAL) && (i <= BUILTIN_ICON_VOL_INTERNAL_REC)) {
|
|
if (BuiltinIconTable[BUILTIN_ICON_VOL_INTERNAL].Image) {
|
|
BuiltinIconTable[i].Image = egCopyImage(BuiltinIconTable[BUILTIN_ICON_VOL_INTERNAL].Image);
|
|
}
|
|
}
|
|
}
|
|
if (i == BUILTIN_SELECTION_BIG) {
|
|
DBG("icon main size=[%d,%d]\n", BuiltinIconTable[i].Image->Width,
|
|
BuiltinIconTable[i].Image->Height);
|
|
}
|
|
i++;
|
|
FreePool(IconName);
|
|
}
|
|
|
|
// OS icons and buttons
|
|
i = 0;
|
|
while (OSIconsTable[i].name) {
|
|
CHAR8 IconNight[64];
|
|
AsciiStrCpyS(IconNight, 64, OSIconsTable[i].name);
|
|
AsciiStrCatS(IconNight, 64, "_night");
|
|
OSIconsTable[i].image = NULL;
|
|
// DBG("search for %a\n", OSIconsTable[i].name);
|
|
Status = EFI_NOT_FOUND;
|
|
if (!DayLight) {
|
|
Status = ParseSVGIcon(mainParser, i, IconNight, Scale, &OSIconsTable[i].image);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
Status = ParseSVGIcon(mainParser, i, OSIconsTable[i].name, Scale, &OSIconsTable[i].image);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
DBG("OSicon %a not parsed\n", OSIconsTable[i].name);
|
|
if ((i > 0) && (i < 13)) {
|
|
if (OSIconsTable[0].image) {
|
|
OSIconsTable[i].image = egCopyImage(OSIconsTable[0].image);
|
|
}
|
|
} else if (i < 18) {
|
|
if (OSIconsTable[13].image) {
|
|
OSIconsTable[i].image = egCopyImage(OSIconsTable[13].image);
|
|
}
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
//selection for bootcamp style
|
|
Status = EFI_NOT_FOUND;
|
|
if (!DayLight) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_SELECTION, "selection_indicator_night", Scale, &SelectionImages[4]);
|
|
}
|
|
if (EFI_ERROR(Status)) {
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_SELECTION, "selection_indicator", Scale, &SelectionImages[4]);
|
|
}
|
|
|
|
//banner animation
|
|
GUI_ANIME *Anime = AllocateZeroPool (sizeof(GUI_ANIME));
|
|
Anime->ID = 1; //main screen
|
|
//there is no Anime->Path in vectors
|
|
Anime->Frames = NumFrames;
|
|
Anime->FrameTime = FrameTime;
|
|
Anime->Next = GuiAnime;
|
|
Anime->FilmX = INITVALUE;
|
|
Anime->FilmY = INITVALUE;
|
|
Anime->NudgeX = INITVALUE;
|
|
Anime->NudgeY = INITVALUE;
|
|
GuiAnime = Anime;
|
|
|
|
nsvgDeleteRasterizer(rast);
|
|
|
|
*dict = AllocateZeroPool(sizeof(TagStruct));
|
|
(*dict)->type = kTagTypeNone;
|
|
GlobalConfig.TypeSVG = TRUE;
|
|
GlobalConfig.ThemeDesignHeight = (int)SVGimage->height;
|
|
GlobalConfig.ThemeDesignWidth = (int)SVGimage->width;
|
|
if (GlobalConfig.SelectionOnTop) {
|
|
row0TileSize = (INTN)(144.f * Scale);
|
|
row1TileSize = (INTN)(64.f * Scale);
|
|
GlobalConfig.MainEntriesSize = (INTN)(128.f * Scale);
|
|
}
|
|
DBG("parsing theme finish\n");
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EG_IMAGE * LoadSvgFrame(INTN i)
|
|
{
|
|
EG_IMAGE *Frame = NULL;
|
|
EFI_STATUS Status;
|
|
CHAR8 FrameName[64];
|
|
AsciiSPrint(FrameName, 63, "frame_%d", i+1);
|
|
Status = ParseSVGIcon(mainParser, BUILTIN_ICON_ANIME, FrameName, GlobalConfig.Scale, &Frame);
|
|
if (EFI_ERROR(Status)) {
|
|
DBG("icon '%a' not loaded, status=%r\n", FrameName, Status);
|
|
}
|
|
return Frame;
|
|
}
|
|
|
|
#if 0
|
|
VOID RenderSVGfont(NSVGfont *fontSVG, UINT32 color)
|
|
{
|
|
// EFI_STATUS Status;
|
|
float FontScale;
|
|
NSVGparser *p;
|
|
NSVGrasterizer *rast;
|
|
INTN i;
|
|
if (!fontSVG) {
|
|
return;
|
|
}
|
|
//free old font
|
|
if (FontImage != NULL) {
|
|
egFreeImage (FontImage);
|
|
FontImage = NULL;
|
|
}
|
|
INTN Height = FontHeight + 4;
|
|
// DBG("load font %a\n", fontSVG->fontFamily);
|
|
if (fontSVG->unitsPerEm < 1.f) {
|
|
fontSVG->unitsPerEm = 1000.f;
|
|
}
|
|
float fH = fontSVG->bbox[3] - fontSVG->bbox[1];
|
|
if (fH == 0.f) {
|
|
fH = fontSVG->unitsPerEm;
|
|
}
|
|
FontScale = (float)FontHeight / fH;
|
|
DBG("font scale %s\n", PoolPrintFloat(FontScale));
|
|
FontWidth = (int)(fontSVG->horizAdvX * FontScale);
|
|
INTN Width = FontWidth * (AsciiPageSize + GlobalConfig.CodepageSize);
|
|
FontImage = egCreateImage(Width, Height, TRUE);
|
|
|
|
p = nsvg__createParser();
|
|
if (!p) {
|
|
return;
|
|
}
|
|
// p->font = fontSVG;
|
|
p->image->height = (float)Height;
|
|
p->image->width = (float)Width;
|
|
|
|
NSVGtext* text = (NSVGtext*)AllocateZeroPool(sizeof(NSVGtext));
|
|
if (!text) {
|
|
return;
|
|
}
|
|
text->fontSize = (float)FontHeight;
|
|
text->font = fontSVG;
|
|
text->fontColor = color;
|
|
|
|
// DBG("RenderSVGfont: fontID=%a\n", text->font->id);
|
|
// DBG("RenderSVGfont: family=%a\n", text->font->fontFamily);
|
|
//add to head
|
|
text->next = p->text;
|
|
p->text = text;
|
|
//for each letter rasterize glyph into FontImage
|
|
//0..0xC0 == AsciiPageSize
|
|
// cyrillic 0x410..0x450 at 0xC0
|
|
float x = 0.f;
|
|
float y = fontSVG->bbox[1] * FontScale;; //(float)Height;
|
|
p->isText = TRUE;
|
|
for (i = 0; i < AsciiPageSize; i++) {
|
|
addLetter(p, i, x, y, FontScale, color);
|
|
x += (float)FontWidth;
|
|
}
|
|
x = AsciiPageSize * FontWidth;
|
|
for (i = GlobalConfig.Codepage; i < GlobalConfig.Codepage+GlobalConfig.CodepageSize; i++) {
|
|
addLetter(p, i, x, y, FontScale, color);
|
|
x += (float)FontWidth;
|
|
}
|
|
p->image->realBounds[0] = fontSVG->bbox[0] * FontScale;
|
|
p->image->realBounds[1] = fontSVG->bbox[1] * FontScale;
|
|
p->image->realBounds[2] = fontSVG->bbox[2] * FontScale + x; //last bound
|
|
p->image->realBounds[3] = fontSVG->bbox[3] * FontScale;
|
|
|
|
//We made an image, then rasterize it
|
|
rast = nsvgCreateRasterizer();
|
|
nsvgRasterize(rast, p->image, 0, 0, 1.0f, 1.0f, (UINT8*)FontImage->PixelData, (int)Width, (int)Height, (int)(Width*4));
|
|
|
|
#if 0 //DEBUG_FONT
|
|
//save font as png yyyyy
|
|
UINT8 *FileData = NULL;
|
|
UINTN FileDataLength = 0U;
|
|
|
|
EFI_UGA_PIXEL *ImagePNG = (EFI_UGA_PIXEL *)FontImage->PixelData;
|
|
|
|
unsigned lode_return =
|
|
eglodepng_encode(&FileData, &FileDataLength, (CONST UINT8*)ImagePNG, (UINTN)FontImage->Width, (UINTN)FontImage->Height);
|
|
|
|
if (!lode_return) {
|
|
egSaveFile(SelfRootDir, L"\\FontSVG.png", FileData, FileDataLength);
|
|
}
|
|
#endif
|
|
nsvgDeleteRasterizer(rast);
|
|
// nsvg__deleteParser(p);
|
|
return;
|
|
}
|
|
#endif
|
|
//textType = 0-help 1-message 2-menu 3-test
|
|
//return text width in pixels
|
|
INTN drawSVGtext(EG_IMAGE* TextBufferXY, INTN posX, INTN posY, INTN textType, CONST CHAR16* string, UINTN Cursor)
|
|
{
|
|
INTN Width;
|
|
UINTN i;
|
|
UINTN len;
|
|
NSVGparser* p;
|
|
NSVGrasterizer* rast;
|
|
if (!textFace[textType].valid) {
|
|
for (i=0; i<4; i++) {
|
|
if (textFace[i].valid) {
|
|
textType = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!textFace[textType].valid) {
|
|
DBG("valid fontface not found!\n");
|
|
return 0;
|
|
}
|
|
NSVGfont* fontSVG = textFace[textType].font;
|
|
UINT32 color = textFace[textType].color;
|
|
INTN Height = (INTN)(textFace[textType].size * GlobalConfig.Scale);
|
|
float Scale, sy;
|
|
float x, y;
|
|
if (!fontSVG) {
|
|
DBG("no font for drawSVGtext\n");
|
|
return 0;
|
|
}
|
|
if (!TextBufferXY) {
|
|
DBG("no buffer\n");
|
|
return 0;
|
|
}
|
|
p = nsvg__createParser();
|
|
if (!p) {
|
|
return 0;
|
|
}
|
|
NSVGtext* text = (NSVGtext*)AllocateZeroPool(sizeof(NSVGtext));
|
|
if (!text) {
|
|
return 0;
|
|
}
|
|
text->font = fontSVG;
|
|
text->fontColor = color;
|
|
text->fontSize = (float)Height;
|
|
nsvg__xformIdentity(text->xform);
|
|
p->text = text;
|
|
|
|
len = StrLen(string);
|
|
Width = TextBufferXY->Width;
|
|
// Height = TextBufferXY->Height;
|
|
// DBG("Text Height=%d Buffer Height=%d\n", Height, TextBufferXY->Height);
|
|
|
|
// Height = 180; //for test
|
|
// DBG("textBuffer: [%d,%d], fontUnits=%d\n", Width, TextBufferXY->Height, (int)fontSVG->unitsPerEm);
|
|
if (!fontSVG->unitsPerEm) {
|
|
fontSVG->unitsPerEm = 1000.f;
|
|
}
|
|
float fH = fontSVG->bbox[3] - fontSVG->bbox[1]; //1250
|
|
if (fH == 0.f) {
|
|
DBG("wrong font: %s\n", PoolPrintFloat(fontSVG->unitsPerEm));
|
|
DumpFloat2("Font bbox", fontSVG->bbox, 4);
|
|
fH = fontSVG->unitsPerEm?fontSVG->unitsPerEm:1000.0f; //1000
|
|
}
|
|
sy = (float)Height / fH; //(float)fontSVG->unitsPerEm; // 260./1250.
|
|
//in font units
|
|
// float fW = fontSVG->bbox[2] - fontSVG->bbox[0];
|
|
// sx = (float)Width / (fW * len);
|
|
// Scale = (sx > sy)?sy:sx;
|
|
Scale = sy;
|
|
x = (float)posX; //0.f;
|
|
y = (float)posY + fontSVG->bbox[1] * Scale;
|
|
p->isText = TRUE;
|
|
#ifdef _MSC_VER
|
|
CHAR8 *Str8 = (CHAR8*)string;
|
|
#endif
|
|
|
|
for (i=0; i < len; i++) {
|
|
CHAR16 letter = 0;
|
|
#ifdef _MSC_VER
|
|
Str8 = GetUnicodeChar(Str8, &letter);
|
|
Str8++;
|
|
#else
|
|
letter = string[i]; //already UTF16 in clang
|
|
#endif
|
|
if (!letter) {
|
|
break;
|
|
}
|
|
// DBG("add letter 0x%x\n", letter);
|
|
if (i == Cursor) {
|
|
addLetter(p, 0x5F, x, y, sy, color);
|
|
}
|
|
x = addLetter(p, letter, x, y, sy, color);
|
|
// DBG("next x=%s\n", PoolPrintFloat(x));
|
|
} //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;
|
|
// DBG("internal Scale=%s\n", PoolPrintFloat(Scale));
|
|
// DumpFloat2("text bounds", p->image->realBounds, 4);
|
|
//We made an image, then rasterize it
|
|
rast = nsvgCreateRasterizer();
|
|
// DBG("begin raster text, scale=%s\n", PoolPrintFloat(Scale));
|
|
nsvgRasterize(rast, p->image, 0, 0, 1.f, 1.f, (UINT8*)TextBufferXY->PixelData,
|
|
(int)TextBufferXY->Width, (int)TextBufferXY->Height, (int)(Width*4));
|
|
float RealWidth = p->image->realBounds[2] - p->image->realBounds[0];
|
|
// DBG("end raster text\n");
|
|
nsvgDeleteRasterizer(rast);
|
|
// nsvg__deleteParser(p);
|
|
nsvgDelete(p->image);
|
|
return (INTN)RealWidth; //x;
|
|
}
|
|
|
|
VOID testSVG()
|
|
{
|
|
do {
|
|
|
|
EFI_STATUS Status;
|
|
UINT8 *FileData = NULL;
|
|
UINTN FileDataLength = 0;
|
|
|
|
INTN Width = 400, Height = 400;
|
|
|
|
#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
|
|
{
|
|
EG_IMAGE *RndImage = egCreateImage(256, 256, FALSE);
|
|
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) {
|
|
// DBG("r=%x g=%x\n", pixel.r, pixel.g);
|
|
// }
|
|
RndImage->PixelData[i * 256 + j] = pixel;
|
|
}
|
|
}
|
|
|
|
BltImageAlpha(RndImage,
|
|
20,
|
|
20,
|
|
&MenuBackgroundPixel,
|
|
16);
|
|
}
|
|
#endif
|
|
#if TEST_SVG_IMAGE
|
|
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
|
EG_IMAGE *NewImage;
|
|
NSVGimage *SVGimage;
|
|
float Scale, ScaleX, ScaleY;
|
|
|
|
// load file
|
|
Status = egLoadFile(SelfRootDir, L"Sample.svg", &FileData, &FileDataLength);
|
|
if (!EFI_ERROR(Status)) {
|
|
//Parse XML to vector data
|
|
|
|
p = nsvgParse((CHAR8*)FileData, 72, 1.f);
|
|
SVGimage = p->image;
|
|
DBG("Test image width=%d heigth=%d\n", (int)(SVGimage->width), (int)(SVGimage->height));
|
|
// FreePool(FileData);
|
|
/*
|
|
if (p->patterns && p->patterns->image) {
|
|
BltImageAlpha((EG_IMAGE*)(p->patterns->image),
|
|
40,
|
|
40,
|
|
&MenuBackgroundPixel,
|
|
16);
|
|
}
|
|
*/
|
|
// Rasterize
|
|
NewImage = egCreateFilledImage(Width, Height, TRUE, &MenuBackgroundPixel);
|
|
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;
|
|
DBG("timing rasterize start tx=%s ty=%s\n", PoolPrintFloat(tx), PoolPrintFloat(ty));
|
|
nsvgRasterize(rast, SVGimage, tx,ty,Scale,Scale, (UINT8*)NewImage->PixelData, (int)Width, (int)Height, (int)Width*4);
|
|
DBG("timing rasterize end\n");
|
|
//now show it!
|
|
BltImageAlpha(NewImage,
|
|
(UGAWidth - Width) / 2,
|
|
(UGAHeight - Height) / 2,
|
|
&MenuBackgroundPixel,
|
|
16);
|
|
FreePool(FileData);
|
|
FileData = NULL;
|
|
egFreeImage(NewImage);
|
|
nsvg__deleteParser(p);
|
|
nsvgDeleteRasterizer(rast);
|
|
}
|
|
#endif
|
|
//Test text
|
|
Height = 80;
|
|
Width = UGAWidth-200;
|
|
DBG("create test textbuffer\n");
|
|
EG_IMAGE* TextBufferXY = egCreateFilledImage(Width, Height, TRUE, &MenuBackgroundPixel);
|
|
Status = egLoadFile(SelfRootDir, L"Font.svg", &FileData, &FileDataLength);
|
|
DBG("test Font.svg loaded status=%r\n", Status);
|
|
if (!EFI_ERROR(Status)) {
|
|
p = nsvgParse((CHAR8*)FileData, 72, 1.f);
|
|
if (!p) {
|
|
DBG("font not parsed\n");
|
|
break;
|
|
}
|
|
// NSVGfont* fontSVG = p->font;
|
|
textFace[3].font = p->font;
|
|
textFace[3].color = NSVG_RGBA(0x80, 0xFF, 0, 255);
|
|
textFace[3].size = Height;
|
|
// DBG("font parsed family=%a\n", p->font->fontFamily);
|
|
FreePool(FileData);
|
|
// Scale = Height / fontSVG->unitsPerEm;
|
|
drawSVGtext(TextBufferXY, 0, 0, 3, L"Clover Кловер", 1);
|
|
// DBG("text ready to blit\n");
|
|
BltImageAlpha(TextBufferXY,
|
|
(UGAWidth - Width) / 2,
|
|
(UGAHeight - Height) / 2,
|
|
&MenuBackgroundPixel,
|
|
16);
|
|
egFreeImage(TextBufferXY);
|
|
// nsvg__deleteParser(p);
|
|
// DBG("draw finished\n");
|
|
}
|
|
} while (0);
|
|
|
|
}
|