Added PBF font file loading

This commit is contained in:
Gordon Williams 2023-05-26 12:14:13 +01:00
parent 5fe2e6ae95
commit 566344f4e8
6 changed files with 280 additions and 33 deletions

View File

@ -411,6 +411,7 @@ SOURCES += \
libs/graphics/bitmap_font_4x6.c \
libs/graphics/bitmap_font_6x8.c \
libs/graphics/vector_font.c \
libs/graphics/pbf_font.c \
libs/graphics/graphics.c \
libs/graphics/lcd_arraybuffer.c \
libs/graphics/lcd_js.c

View File

@ -88,6 +88,7 @@ typedef enum {
JSGRAPHICS_FONTSIZE_CUSTOM_1BPP = 4 << 13,// a custom bitmap font made from fields in the graphics object (See below)
JSGRAPHICS_FONTSIZE_CUSTOM_2BPP = 5 << 13,// a custom bitmap font made from fields in the graphics object (See below)
JSGRAPHICS_FONTSIZE_CUSTOM_4BPP = 6 << 13,// a custom bitmap font made from fields in the graphics object (See below)
JSGRAPHICS_FONTSIZE_CUSTOM_PBF = 7 << 13,// a custom bitmap font using PBF format (in JSGRAPHICS_CUSTOMFONT_BMP)
JSGRAPHICS_FONTSIZE_CUSTOM_BIT = 4 << 13 // all custom fonts have this bit set
#endif
} JsGraphicsFontSize;

View File

@ -36,6 +36,7 @@
#include "bitmap_font_4x6.h"
#include "bitmap_font_6x8.h"
#include "vector_font.h"
#include "pbf_font.h"
#ifdef GRAPHICS_PALETTED_IMAGES
#if defined(ESPR_GRAPHICS_12BIT)
@ -797,7 +798,7 @@ JsVar *jswrap_graphics_createImage(JsVar *data) {
}
}
if (x) height++;
if (height && ch=="\n") height--; // if the last char was a newline, ignore it
if (height && ch=='\n') height--; // if the last char was a newline, ignore it
jsvStringIteratorFree(&it);
// Sorted - now create the object, set it up and create the buffer
JsVar *img = jsvNewObject();
@ -1716,6 +1717,36 @@ JsVar *jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar
return jsvLockAgain(parent);
}
#endif
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "setFontPBF",
"ifndef" : "SAVE_ON_FLASH",
"generate" : "jswrap_graphics_setFontPBF",
"params" : [
["file","JsVar","The font as a PBF file"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
*/
#ifndef SAVE_ON_FLASH
JsVar *jswrap_graphics_setFontPBF(JsVar *parent, JsVar *file) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
if (!jsvIsString(file)) {
jsExceptionHere(JSET_ERROR, "Font must be a String");
return 0;
}
int scale = 1;
jsvObjectSetChild(parent, JSGRAPHICS_CUSTOMFONT_BMP, file);
gfx.data.fontSize = (unsigned short)(scale | JSGRAPHICS_FONTSIZE_CUSTOM_PBF);
graphicsSetVar(&gfx);
return jsvLockAgain(parent);
}
#endif
/*JSON{
"type" : "method",
"class" : "Graphics",
@ -1990,6 +2021,9 @@ typedef struct {
unsigned short scale;
unsigned short scalex, scaley;
unsigned char customFirstChar;
JsVar *widths; // Array of widths (for old-style fonts) or just an int for fixed width
JsVar *bitmap; // Bitmap/font data
PbfFontLoaderInfo pbfInfo;
} JsGraphicsFontInfo;
static void _jswrap_graphics_getFontInfo(JsGraphics *gfx, JsGraphicsFontInfo *info) {
@ -2004,34 +2038,53 @@ static void _jswrap_graphics_getFontInfo(JsGraphics *gfx, JsGraphicsFontInfo *in
#ifndef SAVE_ON_FLASH
if (info->font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
info->customFirstChar = (int)jsvGetIntegerAndUnLock(jsvObjectGetChildIfExists(gfx->graphicsVar, JSGRAPHICS_CUSTOMFONT_FIRSTCHAR));
info->widths = jsvObjectGetChildIfExists(gfx->graphicsVar, JSGRAPHICS_CUSTOMFONT_WIDTH);
info->bitmap = jsvObjectGetChildIfExists(gfx->graphicsVar, JSGRAPHICS_CUSTOMFONT_BMP);
if ((info->font & JSGRAPHICS_FONTSIZE_FONT_MASK) == JSGRAPHICS_FONTSIZE_CUSTOM_PBF)
jspbfFontNew(&info->pbfInfo, info->bitmap);
} else
#endif
info->customFirstChar = 0;
}
static int _jswrap_graphics_getCharWidth(JsGraphics *gfx, JsGraphicsFontInfo *info, char ch) {
if (info->font == JSGRAPHICS_FONTSIZE_VECTOR) {
static void _jswrap_graphics_freeFontInfo(JsGraphicsFontInfo *info) {
if (info->font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
jsvUnLock(info->widths);
jsvUnLock(info->bitmap);
if ((info->font & JSGRAPHICS_FONTSIZE_FONT_MASK) == JSGRAPHICS_FONTSIZE_CUSTOM_PBF)
jspbfFontFree(&info->pbfInfo);
}
}
static int _jswrap_graphics_getCharWidth(JsGraphics *gfx, JsGraphicsFontInfo *info, int ch) {
if ((info->font == JSGRAPHICS_FONTSIZE_VECTOR) && (ch<256)) {
#ifndef NO_VECTOR_FONT
return (int)graphicsVectorCharWidth(info->scalex, ch);
#endif
} else if (info->font == JSGRAPHICS_FONTSIZE_4X6) {
} else if ((info->font == JSGRAPHICS_FONTSIZE_4X6) && (ch<256)) {
return 4*info->scalex;
#ifdef USE_FONT_6X8
} else if (info->font == JSGRAPHICS_FONTSIZE_6X8) {
} else if ((info->font == JSGRAPHICS_FONTSIZE_6X8) && (ch<256)) {
return 6*info->scalex;
#endif
#ifndef SAVE_ON_FLASH
} else if (info->font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
if ((info->font & JSGRAPHICS_FONTSIZE_FONT_MASK)==JSGRAPHICS_FONTSIZE_CUSTOM_PBF) {
PbfFontLoaderGlyph result;
if (jspbfFontFindGlyph(&info->pbfInfo, ch, &result))
return result.advance;
else
return 0;
}
int w = 0;
// FIXME: is getCharWidth is called a lot (eg for metrics), we do getChild for each
// character - maybe we should store this in JsGraphicsFontInfo (but then we have to unlock it)
JsVar *customWidth = jsvObjectGetChildIfExists(gfx->graphicsVar, JSGRAPHICS_CUSTOMFONT_WIDTH);
if (jsvIsString(customWidth)) {
if (ch>=info->customFirstChar)
w = info->scalex*(unsigned char)jsvGetCharInString(customWidth, (size_t)(ch-info->customFirstChar));
} else
w = info->scalex*(int)jsvGetInteger(customWidth);
jsvUnLock(customWidth);
if (ch<256) {
if (jsvIsString(info->widths)) {
if (ch>=info->customFirstChar)
w = info->scalex*(unsigned char)jsvGetCharInString(info->widths, (size_t)(ch-info->customFirstChar));
} else
w = info->scalex*(int)jsvGetInteger(info->widths);
}
return w;
#endif
}
@ -2059,6 +2112,8 @@ static int _jswrap_graphics_getFontHeightInternal(JsGraphics *gfx, JsGraphicsFon
#endif
#ifndef SAVE_ON_FLASH
} else if (info->font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
if ((info->font & JSGRAPHICS_FONTSIZE_FONT_MASK)==JSGRAPHICS_FONTSIZE_CUSTOM_PBF)
return info->pbfInfo.lineHeight;
return info->scaley*(int)jsvGetIntegerAndUnLock(jsvObjectGetChildIfExists(gfx->graphicsVar, JSGRAPHICS_CUSTOMFONT_HEIGHT));
#endif
}
@ -2069,7 +2124,9 @@ int jswrap_graphics_getFontHeight(JsVar *parent) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
JsGraphicsFontInfo info;
_jswrap_graphics_getFontInfo(&gfx, &info);
return _jswrap_graphics_getFontHeightInternal(&gfx, &info);
int h = _jswrap_graphics_getFontHeightInternal(&gfx, &info);
_jswrap_graphics_freeFontInfo(&info);
return h;
#else
return 0;
#endif
@ -2117,6 +2174,7 @@ void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIn
jsvUnLock(str);
if (stringWidth) *stringWidth = width>maxWidth ? width : maxWidth;
if (stringHeight) *stringHeight = height;
_jswrap_graphics_freeFontInfo(&info);
}
JsVarInt _jswrap_graphics_stringWidth(JsGraphics *gfx, JsVar *var, int lineStartIndex) {
int w,h;
@ -2271,6 +2329,7 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) {
if (jsvGetStringLength(currentLine))
jsvArrayPush(lines, currentLine);
jsvUnLock2(str,currentLine);
_jswrap_graphics_freeFontInfo(&info);
return lines;
}
@ -2317,14 +2376,11 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
int fontHeight = _jswrap_graphics_getFontHeightInternal(&gfx, &info);
#ifndef SAVE_ON_FLASH
JsVar *customBitmap = 0, *customWidth = 0;
int customBPP = 1;
if (info.font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
if (info.font==JSGRAPHICS_FONTSIZE_CUSTOM_2BPP) customBPP = 2;
if (info.font==JSGRAPHICS_FONTSIZE_CUSTOM_4BPP) customBPP = 4;
customBitmap = jsvObjectGetChildIfExists(parent, JSGRAPHICS_CUSTOMFONT_BMP);
customWidth = jsvObjectGetChildIfExists(parent, JSGRAPHICS_CUSTOMFONT_WIDTH);
if ((info.font&JSGRAPHICS_FONTSIZE_FONT_MASK)==JSGRAPHICS_FONTSIZE_CUSTOM_2BPP) customBPP = 2;
if ((info.font&JSGRAPHICS_FONTSIZE_FONT_MASK)==JSGRAPHICS_FONTSIZE_CUSTOM_4BPP) customBPP = 4;
}
#endif
#ifndef SAVE_ON_FLASH
@ -2402,36 +2458,44 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
continue;
}
#endif
if (info.font == JSGRAPHICS_FONTSIZE_VECTOR) {
if ((info.font == JSGRAPHICS_FONTSIZE_VECTOR) && (ch<256)) {
#ifndef NO_VECTOR_FONT
int w = (int)graphicsVectorCharWidth(info.scalex, ch);
// TODO: potentially we could do this in x16 accuracy so vector chars rendered together better
if (x>minX-w && x<maxX && y>minY-fontHeight && y<=maxY) {
if (solidBackground)
graphicsFillRect(&gfx,x,y,x+w-1,y+fontHeight-1, gfx.data.bgColor);
graphicsGetVectorChar((graphicsPolyCallback)graphicsFillPoly, &gfx, x, y, info.scalex, info.scaley, ch);
graphicsGetVectorChar((graphicsPolyCallback)graphicsFillPoly, &gfx, x, y, info.scalex, info.scaley, (char)ch);
}
x+=w;
#endif
} else if (info.font == JSGRAPHICS_FONTSIZE_4X6) {
} else if ((info.font == JSGRAPHICS_FONTSIZE_4X6) && (ch<256)) {
if (x>minX-4*info.scalex && x<maxX && y>minY-fontHeight && y<=maxY)
graphicsDrawChar4x6(&gfx, x, y, ch, info.scalex, info.scaley, solidBackground);
graphicsDrawChar4x6(&gfx, x, y, (char)ch, info.scalex, info.scaley, solidBackground);
x+=4*info.scalex;
#ifdef USE_FONT_6X8
} else if (info.font == JSGRAPHICS_FONTSIZE_6X8) {
if (x>minX-6*info.scalex && x<maxX && y>minY-fontHeight && y<=maxY)
graphicsDrawChar6x8(&gfx, x, y, ch, info.scalex, info.scaley, solidBackground);
graphicsDrawChar6x8(&gfx, x, y, (char)ch, info.scalex, info.scaley, solidBackground);
x+=6*info.scalex;
#endif
#ifndef SAVE_ON_FLASH
} else if (info.font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) {
} else if ((info.font & JSGRAPHICS_FONTSIZE_FONT_MASK)==JSGRAPHICS_FONTSIZE_CUSTOM_PBF) {
PbfFontLoaderGlyph glyph;
if (jspbfFontFindGlyph(&info.pbfInfo, ch, &glyph)) {
jspbfFontRenderGlyph(&info.pbfInfo, &glyph, &gfx,
x+glyph.x*info.scalex, y+glyph.y*info.scaley,
solidBackground, info.scalex, info.scaley);
x+=glyph.advance*info.scalex;
}
} else if ((info.font & JSGRAPHICS_FONTSIZE_CUSTOM_BIT) && (ch<256)) {
int customBPPRange = (1<<customBPP)-1;
// get char width and offset in string
int width = 0, bmpOffset = 0;
if (jsvIsString(customWidth)) {
if (jsvIsString(info.widths)) {
if (ch>=info.customFirstChar) {
JsvStringIterator wit;
jsvStringIteratorNew(&wit, customWidth, 0);
jsvStringIteratorNew(&wit, info.widths, 0);
while (jsvStringIteratorHasChar(&wit) && (int)jsvStringIteratorGetIndex(&wit)<(ch-info.customFirstChar)) {
bmpOffset += (unsigned char)jsvStringIteratorGetCharAndNext(&wit);
}
@ -2439,7 +2503,7 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
jsvStringIteratorFree(&wit);
}
} else {
width = (int)jsvGetInteger(customWidth);
width = (int)jsvGetInteger(info.widths);
bmpOffset = width*(ch-info.customFirstChar);
}
if (ch>=info.customFirstChar && (x>minX-width*info.scalex) && (x<maxX) && (y>minY-fontHeight) && y<=maxY) {
@ -2447,7 +2511,7 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
bmpOffset *= ch * customBPP;
// now render character
JsvStringIterator cit;
jsvStringIteratorNew(&cit, customBitmap, (size_t)(bmpOffset>>3));
jsvStringIteratorNew(&cit, info.bitmap, (size_t)(bmpOffset>>3));
bmpOffset &= 7;
int cx,cy;
int citdata = jsvStringIteratorGetChar(&cit);
@ -2480,13 +2544,11 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
}
jsvStringIteratorFree(&it);
jsvUnLock(str);
#ifndef SAVE_ON_FLASH
jsvUnLock2(customBitmap, customWidth);
#endif
#ifndef SAVE_ON_FLASH
gfx.data.flags = oldFlags; // restore flags because of text rotation
graphicsSetVar(&gfx); // gfx data changed because modified area
#endif
_jswrap_graphics_freeFontInfo(&info);
return jsvLockAgain(parent);
}

View File

@ -56,6 +56,7 @@ JsVarInt jswrap_graphics_getColorX(JsVar *parent, bool isForeground);
JsVar *jswrap_graphics_setClipRect(JsVar *parent, int x1, int y1, int x2, int y2);
JsVar *jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool isVectorFont);
JsVar *jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height);
JsVar *jswrap_graphics_setFontPBF(JsVar *parent, JsVar *file);
JsVar *jswrap_graphics_setFontAlign(JsVar *parent, int x, int y, int r);
JsVar *jswrap_graphics_setFont(JsVar *parent, JsVar *name, int size);
JsVar *jswrap_graphics_getFont(JsVar *parent);

135
libs/graphics/pbf_font.c Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Pebble font format parser
* ----------------------------------------------------------------------------
*/
#include "pbf_font.h"
// https://github.com/pebble-dev/wiki/wiki/Firmware-Font-Format
/* Notes for improvements in a new format:
* remove value in hashtable (un-needed)
* Smaller hashtable, might as well use ~64 or something
* per-codepoint codepoint size?
* Allow mix of 1bpp/2bpp/etc glyphs
* Store all glyphs in one chunk - allow bit-level packing
*/
static uint8_t jspbfGetU8(JsvStringIterator *it) {
return (uint8_t)jsvStringIteratorGetCharAndNext(it);
}
static uint16_t jspbfGetU16(JsvStringIterator *it) {
return (uint16_t)((uint16_t)(uint8_t)jsvStringIteratorGetCharAndNext(it) |
(((uint16_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<8));
}
static uint32_t jspbfGetU32(JsvStringIterator *it) {
return (uint32_t)((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<8) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<16) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<24));
}
/// Load a .pbf file
void jspbfFontNew(PbfFontLoaderInfo *info, JsVar *font) {
info->var = jsvLockAgain(font);
jsvStringIteratorNew(&info->it, font, 0);
info->version = jspbfGetU8(&info->it); // 0
info->lineHeight = jspbfGetU8(&info->it); // 1
info->glyphCount = jspbfGetU16(&info->it); // 2
jspbfGetU16(&info->it); // wildcard codepoint
info->hashTableOffset = 6;
info->hashTableSize = 255;
info->codepointSize = 2;
if (info->version>=2) {
info->hashTableOffset = 8;
info->hashTableSize = jspbfGetU8(&info->it);
info->codepointSize = jspbfGetU8(&info->it);
}
if (info->version==3) {
info->hashTableOffset = 10;
assert(0); // v3 not loaded yet
}
info->offsetTableOffset = (uint16_t)(info->hashTableOffset + info->hashTableSize*4);
info->glyphTableOffset = (uint16_t)(info->offsetTableOffset + info->glyphCount*6); // cp=2,address=4
}
void jspbfFontFree(PbfFontLoaderInfo *info) {
jsvStringIteratorFree(&info->it);
jsvUnLock(info->var);
info->var = NULL;
}
// Find the font glyph, fill PbfFontLoaderGlyph with info. Iterator is left pointing to glyph
bool jspbfFontFindGlyph(PbfFontLoaderInfo *info, int codepoint, PbfFontLoaderGlyph *result) {
int hash = codepoint % info->hashTableSize;
/*
HashTable
0x00 u8 : Value. In all examples I encountered, this simply counts 0..255
0x01 u8 : Offset Table Size.
0x02 u16 : Offset Table Offset. Measured in bytes from the beginning of the offset table.
*/
jsvStringIteratorGoto(&info->it, info->var, info->hashTableOffset + 4*hash + 1); // +1 because we don't care about value
uint8_t offsetTableSize = jspbfGetU8(&info->it);
uint16_t o = jspbfGetU16(&info->it);
uint16_t offsetTableOffset = o + info->offsetTableOffset;
jsvStringIteratorGoto(&info->it, info->var, offsetTableOffset);
int cp;
while (offsetTableSize--) {
/* Offset Table
0x00 u16/u32 : Unicode codepoint (for example, 0x0622 for ).
0x02 or 0x04 u16/u32 : Data offset.
*/
cp = jspbfGetU16(&info->it); assert(info->codepointSize==2); // only cp=2 suppported
uint32_t dataOffset = jspbfGetU32(&info->it); // only address=4 byte supported
if (cp==codepoint) {
jsvStringIteratorGoto(&info->it, info->var, info->glyphTableOffset+dataOffset);
result->w = jspbfGetU8(&info->it);
result->h = jspbfGetU8(&info->it);
result->x = (int8_t)jspbfGetU8(&info->it);
result->y = (int8_t)jspbfGetU8(&info->it);
result->advance = (int8_t)jspbfGetU8(&info->it);
return true;
}
}
return false;
}
void jspbfFontRenderGlyph(PbfFontLoaderInfo *info, PbfFontLoaderGlyph *glyph, JsGraphics *gfx, int x, int y, bool solidBackground, int scalex, int scaley) {
//bmpOffset *= ch * customBPP;
// now render character
int bmpOffset = 0;
int bpp = 1;
int bppRange = (1<<bpp)-1;
bmpOffset &= 7;
int cx,cy;
int citdata = jsvStringIteratorGetCharAndNext(&info->it);
citdata <<= bpp*bmpOffset;
for (cy=0;cy<glyph->h;cy++) {
for (cx=0;cx<glyph->w;cx++) {
int col = citdata&bppRange;
if (solidBackground || col)
graphicsFillRect(gfx,
(x + cx*scalex),
(y + cy*scaley),
(x + cx*scalex + scalex-1),
(y + cy*scaley + scaley-1),
graphicsBlendGfxColor(gfx, (256*col)/bppRange));
bmpOffset += bpp;
citdata >>= bpp;
if (bmpOffset>=8) {
bmpOffset=0;
citdata = jsvStringIteratorGetCharAndNext(&info->it);
}
}
}
}

47
libs/graphics/pbf_font.h Normal file
View File

@ -0,0 +1,47 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Pebble font format parser
* ----------------------------------------------------------------------------
*/
#include "jsvar.h"
#include "jsvariterator.h"
#include "graphics.h"
typedef struct {
JsVar *var;
JsvStringIterator it;
uint8_t version;
uint8_t lineHeight;
uint16_t glyphCount;
uint8_t hashTableSize;
uint8_t codepointSize;
uint16_t hashTableOffset;
uint16_t offsetTableOffset;
uint16_t glyphTableOffset;
} PbfFontLoaderInfo;
typedef struct {
uint8_t w;
uint8_t h;
int8_t x;
int8_t y;
int8_t advance; // how wide is the character itself?
} PbfFontLoaderGlyph;
/// Load a .pbf file
void jspbfFontNew(PbfFontLoaderInfo *info, JsVar *font);
void jspbfFontFree(PbfFontLoaderInfo *info);
// Find the font glyph, fill PbfFontLoaderGlyph with info. Iterator is left pointing to glyph
bool jspbfFontFindGlyph(PbfFontLoaderInfo *info, int codepoint, PbfFontLoaderGlyph *result);
void jspbfFontRenderGlyph(PbfFontLoaderInfo *info, PbfFontLoaderGlyph *glyph, JsGraphics *gfx, int x, int y, bool solidBackground, int scalex, int scaley);