Move Smooth Font to new class

This commit is contained in:
adamczuk.piotr 2020-04-15 23:17:19 +02:00
parent 870207dcfa
commit 09c2d137a3
8 changed files with 508 additions and 440 deletions

344
Extensions/SmoothFont.cpp Normal file
View File

@ -0,0 +1,344 @@
////////////////////////////////////////////////////////////////////////////////////////
// New anti-aliased (smoothed) font functions added below
////////////////////////////////////////////////////////////////////////////////////////
/***************************************************************************************
** Function name: loadFont
** Description: loads parameters from a font vlw array in memory
*************************************************************************************x*/
void TFT_eSPI_SmoothFont::loadFont(const uint8_t array[])
{
if (array == nullptr) return;
fontPtr = (uint8_t*) array;
loadFont("", false);
}
#ifdef FONT_FS_AVAILABLE
/***************************************************************************************
** Function name: loadFont
** Description: loads parameters from a font vlw file
*************************************************************************************x*/
void TFT_eSPI_SmoothFont::loadFont(String fontName, fs::FS &ffs)
{
fontFS = ffs;
loadFont(fontName, false);
}
#endif
/***************************************************************************************
** Function name: loadFont
** Description: loads parameters from a font vlw file
*************************************************************************************x*/
void TFT_eSPI_SmoothFont::loadFont(String fontName, bool flash)
{
/*
The vlw font format does not appear to be documented anywhere, so some reverse
engineering has been applied!
Header of vlw file comprises 6 uint32_t parameters (24 bytes total):
1. The gCount (number of character glyphs)
2. A version number (0xB = 11 for the one I am using)
3. The font size (in points, not pixels)
4. Deprecated mboxY parameter (typically set to 0)
5. Ascent in pixels from baseline to top of "d"
6. Descent in pixels from baseline to bottom of "p"
Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes):
1. Glyph Unicode stored as a 32 bit value
2. Height of bitmap bounding box
3. Width of bitmap bounding box
4. gxAdvance for cursor (setWidth in Processing)
5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up)
6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left)
7. padding value, typically 0
The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file.
Each pixel is 1 byte, an 8 bit Alpha value which represents the transparency from
0xFF foreground colour, 0x00 background. The sketch uses a linear interpolation
between the foreground and background RGB component colours. e.g.
pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255
To gain a performance advantage fixed point arithmetic is used with rounding and
division by 256 (shift right 8 bits is faster).
After the bitmaps is:
1 byte for font name string length (excludes null)
a zero terminated character string giving the font name
1 byte for Postscript name string length
a zero/one terminated character string giving the font name
last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed)
Glyph bitmap example is:
// Cursor coordinate positions for this and next character are marked by 'C'
// C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position
// | |
// | | ascent is top of "d", descent is bottom of "p"
// +-- gdX --+ ascent
// | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap
// | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of
// | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX)
// | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions
// | | .@@@.....@@@@ | |
// | gHeight ....@@@@@..@@ + + <-- baseline
// | | ...........@@ |
// | | ...........@@ | gdY is the offset to the top edge of the bitmap
// | | .@@.......@@. descent plot top edge of bitmap at (cursorY + yAdvance - gdY)
// | + x..@@@@@@@..x | x marks the corner pixels of the bitmap
// | |
// +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent)
// some fonts can overlay in y direction so may need a user adjust value
*/
if (fontLoaded) return;
#ifdef FONT_FS_AVAILABLE
if (fontName == "") fs_font = false;
else { fontPtr = nullptr; fs_font = true; }
if (fs_font) {
spiffs = flash; // true if font is in SPIFFS
if(spiffs) fontFS = SPIFFS;
// Avoid a crash on the ESP32 if the file does not exist
if (fontFS.exists("/" + fontName + ".vlw") == false) {
Serial.println("Font file " + fontName + " not found!");
return;
}
fontFile = fontFS.open( "/" + fontName + ".vlw", "r");
if(!fontFile) return;
fontFile.seek(0, fs::SeekSet);
}
#endif
gFont.gArray = (const uint8_t*)fontPtr;
gFont.gCount = (uint16_t)readInt32(); // glyph count in file
readInt32(); // vlw encoder version - discard
gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels
readInt32(); // discard
gFont.ascent = (uint16_t)readInt32(); // top of "d"
gFont.descent = (uint16_t)readInt32(); // bottom of "p"
// These next gFont values might be updated when the Metrics are fetched
gFont.maxAscent = gFont.ascent; // Determined from metrics
gFont.maxDescent = gFont.descent; // Determined from metrics
gFont.yAdvance = gFont.ascent + gFont.descent;
gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width
fontLoaded = true;
// Fetch the metrics for each glyph
loadMetrics();
}
/***************************************************************************************
** Function name: loadMetrics
** Description: Get the metrics for each glyph and store in RAM
*************************************************************************************x*/
//#define SHOW_ASCENT_DESCENT
void TFT_eSPI_SmoothFont::loadMetrics(void)
{
uint32_t headerPtr = 24;
uint32_t bitmapPtr = headerPtr + gFont.gCount * 28;
#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
if ( psramFound() )
{
gUnicode = (uint16_t*)ps_malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
gHeight = (uint8_t*)ps_malloc( gFont.gCount ); // Height of glyph
gWidth = (uint8_t*)ps_malloc( gFont.gCount ); // Width of glyph
gxAdvance = (uint8_t*)ps_malloc( gFont.gCount ); // xAdvance - to move x cursor
gdY = (int16_t*)ps_malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
gdX = (int8_t*)ps_malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
gBitmap = (uint32_t*)ps_malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
}
else
#endif
{
gUnicode = (uint16_t*)malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
gHeight = (uint8_t*)malloc( gFont.gCount ); // Height of glyph
gWidth = (uint8_t*)malloc( gFont.gCount ); // Width of glyph
gxAdvance = (uint8_t*)malloc( gFont.gCount ); // xAdvance - to move x cursor
gdY = (int16_t*)malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
gdX = (int8_t*)malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
gBitmap = (uint32_t*)malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
}
#ifdef SHOW_ASCENT_DESCENT
Serial.print("ascent = "); Serial.println(gFont.ascent);
Serial.print("descent = "); Serial.println(gFont.descent);
#endif
#ifdef FONT_FS_AVAILABLE
if (fs_font) fontFile.seek(headerPtr, fs::SeekSet);
#endif
uint16_t gNum = 0;
while (gNum < gFont.gCount)
{
gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value
gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph
gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph
gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor
gdY[gNum] = (int16_t)readInt32(); // y delta from baseline
gdX[gNum] = (int8_t)readInt32(); // x delta from cursor
readInt32(); // ignored
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]);
// Different glyph sets have different ascent values not always based on "d", so we could get
// the maximum glyph ascent by checking all characters. BUT this method can generate bad values
// for non-existant glyphs, so we will reply on processing for the value and disable this code for now...
if (gdY[gNum] > gFont.maxAscent)
{
// Try to avoid UTF coding values and characters that tend to give duff values
if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0))
{
gFont.maxAscent = gdY[gNum];
#ifdef SHOW_ASCENT_DESCENT
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent);
#endif
}
}
// // Different glyph sets have different descent values not always based on "p", so get maximum glyph descent
// if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent)
// {
// // Avoid UTF coding values and characters that tend to give duff values
// if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF))
// {
// gFont.maxDescent = gHeight[gNum] - gdY[gNum];
// #ifdef SHOW_ASCENT_DESCENT
// Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]);
// #endif
// }
// }
gBitmap[gNum] = bitmapPtr;
bitmapPtr += gWidth[gNum] * gHeight[gNum];
gNum++;
yield();
}
gFont.yAdvance = gFont.maxAscent + gFont.maxDescent;
gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width
}
/***************************************************************************************
** Function name: deleteMetrics
** Description: Delete the old glyph metrics and free up the memory
*************************************************************************************x*/
void TFT_eSPI_SmoothFont::unloadFont( void )
{
if (gUnicode)
{
free(gUnicode);
gUnicode = NULL;
}
if (gHeight)
{
free(gHeight);
gHeight = NULL;
}
if (gWidth)
{
free(gWidth);
gWidth = NULL;
}
if (gxAdvance)
{
free(gxAdvance);
gxAdvance = NULL;
}
if (gdY)
{
free(gdY);
gdY = NULL;
}
if (gdX)
{
free(gdX);
gdX = NULL;
}
if (gBitmap)
{
free(gBitmap);
gBitmap = NULL;
}
gFont.gArray = nullptr;
#ifdef FONT_FS_AVAILABLE
if (fs_font && fontFile) fontFile.close();
#endif
fontLoaded = false;
}
/***************************************************************************************
** Function name: readInt32
** Description: Get a 32 bit integer from the font file
*************************************************************************************x*/
uint32_t TFT_eSPI_SmoothFont::readInt32(void)
{
uint32_t val = 0;
#ifdef FONT_FS_AVAILABLE
if (fs_font) {
val |= fontFile.read() << 24;
val |= fontFile.read() << 16;
val |= fontFile.read() << 8;
val |= fontFile.read();
}
else
#endif
{
val |= pgm_read_byte(fontPtr++) << 24;
val |= pgm_read_byte(fontPtr++) << 16;
val |= pgm_read_byte(fontPtr++) << 8;
val |= pgm_read_byte(fontPtr++);
}
return val;
}
/***************************************************************************************
** Function name: getUnicodeIndex
** Description: Get the font file index of a Unicode character
*************************************************************************************x*/
bool TFT_eSPI_SmoothFont::getUnicodeIndex(uint16_t unicode, uint16_t *index)
{
for (uint16_t i = 0; i < gFont.gCount; i++)
{
if (gUnicode[i] == unicode)
{
*index = i;
return true;
}
}
return false;
}

61
Extensions/SmoothFont.h Normal file
View File

@ -0,0 +1,61 @@
// Coded by Bodmer 10/2/18, see license in root directory.
// This is part of the TFT_eSPI class and is associated with anti-aliased font functions
class TFT_eSPI_SmoothFont {
private:
String filename;
public:
// These are for the new antialiased fonts
void loadFont(const uint8_t array[]);
#ifdef FONT_FS_AVAILABLE
void loadFont(String fontName, fs::FS &ffs);
#endif
void loadFont(String fontName, bool flash = true);
void unloadFont( void );
bool getUnicodeIndex(uint16_t unicode, uint16_t *index);
// This is for the whole font
typedef struct
{
const uint8_t* gArray; //array start pointer
uint16_t gCount; // Total number of characters
uint16_t yAdvance; // Line advance
uint16_t spaceWidth; // Width of a space character
int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller
int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent
uint16_t maxAscent; // Maximum ascent found in font
uint16_t maxDescent; // Maximum descent found in font
} fontMetrics;
fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 };
// These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time)
uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential
uint8_t* gHeight = NULL; //cheight
uint8_t* gWidth = NULL; //cwidth
uint8_t* gxAdvance = NULL; //setWidth
int16_t* gdY = NULL; //topExtent
int8_t* gdX = NULL; //leftExtent
uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap
#ifdef FONT_FS_AVAILABLE
fs::File fontFile;
fs::FS &fontFS = SPIFFS;
bool spiffs = true;
bool fs_font = false; // For ESP32/8266 use smooth font file or FLASH (PROGMEM) array
#else
bool fontFile = true;
#endif
private:
bool fontLoaded = false;
void loadMetrics(void);
uint32_t readInt32(void);
uint8_t* fontPtr = nullptr;
};

View File

@ -12,8 +12,6 @@
*************************************************************************************x*/
void TFT_eSPI::loadFont(const uint8_t array[])
{
if (array == nullptr) return;
fontPtr = (uint8_t*) array;
loadFont("", false);
}
@ -24,7 +22,6 @@ void TFT_eSPI::loadFont(const uint8_t array[])
*************************************************************************************x*/
void TFT_eSPI::loadFont(String fontName, fs::FS &ffs)
{
fontFS = ffs;
loadFont(fontName, false);
}
#endif
@ -35,212 +32,8 @@ void TFT_eSPI::loadFont(String fontName, fs::FS &ffs)
*************************************************************************************x*/
void TFT_eSPI::loadFont(String fontName, bool flash)
{
/*
The vlw font format does not appear to be documented anywhere, so some reverse
engineering has been applied!
Header of vlw file comprises 6 uint32_t parameters (24 bytes total):
1. The gCount (number of character glyphs)
2. A version number (0xB = 11 for the one I am using)
3. The font size (in points, not pixels)
4. Deprecated mboxY parameter (typically set to 0)
5. Ascent in pixels from baseline to top of "d"
6. Descent in pixels from baseline to bottom of "p"
Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes):
1. Glyph Unicode stored as a 32 bit value
2. Height of bitmap bounding box
3. Width of bitmap bounding box
4. gxAdvance for cursor (setWidth in Processing)
5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up)
6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left)
7. padding value, typically 0
The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file.
Each pixel is 1 byte, an 8 bit Alpha value which represents the transparency from
0xFF foreground colour, 0x00 background. The sketch uses a linear interpolation
between the foreground and background RGB component colours. e.g.
pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255
To gain a performance advantage fixed point arithmetic is used with rounding and
division by 256 (shift right 8 bits is faster).
After the bitmaps is:
1 byte for font name string length (excludes null)
a zero terminated character string giving the font name
1 byte for Postscript name string length
a zero/one terminated character string giving the font name
last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed)
Glyph bitmap example is:
// Cursor coordinate positions for this and next character are marked by 'C'
// C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position
// | |
// | | ascent is top of "d", descent is bottom of "p"
// +-- gdX --+ ascent
// | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap
// | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of
// | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX)
// | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions
// | | .@@@.....@@@@ | |
// | gHeight ....@@@@@..@@ + + <-- baseline
// | | ...........@@ |
// | | ...........@@ | gdY is the offset to the top edge of the bitmap
// | | .@@.......@@. descent plot top edge of bitmap at (cursorY + yAdvance - gdY)
// | + x..@@@@@@@..x | x marks the corner pixels of the bitmap
// | |
// +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent)
// some fonts can overlay in y direction so may need a user adjust value
*/
if (fontLoaded) unloadFont();
#ifdef FONT_FS_AVAILABLE
if (fontName == "") fs_font = false;
else { fontPtr = nullptr; fs_font = true; }
if (fs_font) {
spiffs = flash; // true if font is in SPIFFS
if(spiffs) fontFS = SPIFFS;
// Avoid a crash on the ESP32 if the file does not exist
if (fontFS.exists("/" + fontName + ".vlw") == false) {
Serial.println("Font file " + fontName + " not found!");
return;
}
fontFile = fontFS.open( "/" + fontName + ".vlw", "r");
if(!fontFile) return;
fontFile.seek(0, fs::SeekSet);
}
#endif
gFont.gArray = (const uint8_t*)fontPtr;
gFont.gCount = (uint16_t)readInt32(); // glyph count in file
readInt32(); // vlw encoder version - discard
gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels
readInt32(); // discard
gFont.ascent = (uint16_t)readInt32(); // top of "d"
gFont.descent = (uint16_t)readInt32(); // bottom of "p"
// These next gFont values might be updated when the Metrics are fetched
gFont.maxAscent = gFont.ascent; // Determined from metrics
gFont.maxDescent = gFont.descent; // Determined from metrics
gFont.yAdvance = gFont.ascent + gFont.descent;
gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width
sf->loadFont(fontName, flash);
fontLoaded = true;
// Fetch the metrics for each glyph
loadMetrics();
}
/***************************************************************************************
** Function name: loadMetrics
** Description: Get the metrics for each glyph and store in RAM
*************************************************************************************x*/
//#define SHOW_ASCENT_DESCENT
void TFT_eSPI::loadMetrics(void)
{
uint32_t headerPtr = 24;
uint32_t bitmapPtr = headerPtr + gFont.gCount * 28;
#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
if ( psramFound() )
{
gUnicode = (uint16_t*)ps_malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
gHeight = (uint8_t*)ps_malloc( gFont.gCount ); // Height of glyph
gWidth = (uint8_t*)ps_malloc( gFont.gCount ); // Width of glyph
gxAdvance = (uint8_t*)ps_malloc( gFont.gCount ); // xAdvance - to move x cursor
gdY = (int16_t*)ps_malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
gdX = (int8_t*)ps_malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
gBitmap = (uint32_t*)ps_malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
}
else
#endif
{
gUnicode = (uint16_t*)malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
gHeight = (uint8_t*)malloc( gFont.gCount ); // Height of glyph
gWidth = (uint8_t*)malloc( gFont.gCount ); // Width of glyph
gxAdvance = (uint8_t*)malloc( gFont.gCount ); // xAdvance - to move x cursor
gdY = (int16_t*)malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
gdX = (int8_t*)malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
gBitmap = (uint32_t*)malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
}
#ifdef SHOW_ASCENT_DESCENT
Serial.print("ascent = "); Serial.println(gFont.ascent);
Serial.print("descent = "); Serial.println(gFont.descent);
#endif
#ifdef FONT_FS_AVAILABLE
if (fs_font) fontFile.seek(headerPtr, fs::SeekSet);
#endif
uint16_t gNum = 0;
while (gNum < gFont.gCount)
{
gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value
gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph
gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph
gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor
gdY[gNum] = (int16_t)readInt32(); // y delta from baseline
gdX[gNum] = (int8_t)readInt32(); // x delta from cursor
readInt32(); // ignored
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]);
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]);
// Different glyph sets have different ascent values not always based on "d", so we could get
// the maximum glyph ascent by checking all characters. BUT this method can generate bad values
// for non-existant glyphs, so we will reply on processing for the value and disable this code for now...
/*
if (gdY[gNum] > gFont.maxAscent)
{
// Try to avoid UTF coding values and characters that tend to give duff values
if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0))
{
gFont.maxAscent = gdY[gNum];
#ifdef SHOW_ASCENT_DESCENT
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent);
#endif
}
}
*/
// Different glyph sets have different descent values not always based on "p", so get maximum glyph descent
if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent)
{
// Avoid UTF coding values and characters that tend to give duff values
if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF))
{
gFont.maxDescent = gHeight[gNum] - gdY[gNum];
#ifdef SHOW_ASCENT_DESCENT
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]);
#endif
}
}
gBitmap[gNum] = bitmapPtr;
bitmapPtr += gWidth[gNum] * gHeight[gNum];
gNum++;
yield();
}
gFont.yAdvance = gFont.maxAscent + gFont.maxDescent;
gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width
}
@ -250,104 +43,9 @@ void TFT_eSPI::loadMetrics(void)
*************************************************************************************x*/
void TFT_eSPI::unloadFont( void )
{
if (gUnicode)
{
free(gUnicode);
gUnicode = NULL;
}
if (gHeight)
{
free(gHeight);
gHeight = NULL;
}
if (gWidth)
{
free(gWidth);
gWidth = NULL;
}
if (gxAdvance)
{
free(gxAdvance);
gxAdvance = NULL;
}
if (gdY)
{
free(gdY);
gdY = NULL;
}
if (gdX)
{
free(gdX);
gdX = NULL;
}
if (gBitmap)
{
free(gBitmap);
gBitmap = NULL;
}
gFont.gArray = nullptr;
#ifdef FONT_FS_AVAILABLE
if (fs_font && fontFile) fontFile.close();
#endif
fontLoaded = false;
fontLoaded = false;
}
/***************************************************************************************
** Function name: readInt32
** Description: Get a 32 bit integer from the font file
*************************************************************************************x*/
uint32_t TFT_eSPI::readInt32(void)
{
uint32_t val = 0;
#ifdef FONT_FS_AVAILABLE
if (fs_font) {
val |= fontFile.read() << 24;
val |= fontFile.read() << 16;
val |= fontFile.read() << 8;
val |= fontFile.read();
}
else
#endif
{
val |= pgm_read_byte(fontPtr++) << 24;
val |= pgm_read_byte(fontPtr++) << 16;
val |= pgm_read_byte(fontPtr++) << 8;
val |= pgm_read_byte(fontPtr++);
}
return val;
}
/***************************************************************************************
** Function name: getUnicodeIndex
** Description: Get the font file index of a Unicode character
*************************************************************************************x*/
bool TFT_eSPI::getUnicodeIndex(uint16_t unicode, uint16_t *index)
{
for (uint16_t i = 0; i < gFont.gCount; i++)
{
if (gUnicode[i] == unicode)
{
*index = i;
return true;
}
}
return false;
}
/***************************************************************************************
** Function name: drawGlyph
** Description: Write a character to the TFT cursor position
@ -358,20 +56,20 @@ void TFT_eSPI::drawGlyph(uint16_t code)
if (code < 0x21)
{
if (code == 0x20) {
cursor_x += gFont.spaceWidth;
cursor_x += sf->gFont.spaceWidth;
return;
}
if (code == '\n') {
cursor_x = 0;
cursor_y += gFont.yAdvance;
cursor_y += sf->gFont.yAdvance;
if (cursor_y >= _height) cursor_y = 0;
return;
}
}
uint16_t gNum = 0;
bool found = getUnicodeIndex(code, &gNum);
bool found = sf->getUnicodeIndex(code, &gNum);
uint16_t fg = textcolor;
uint16_t bg = textbgcolor;
@ -379,22 +77,22 @@ void TFT_eSPI::drawGlyph(uint16_t code)
if (found)
{
if (textwrapX && (cursor_x + gWidth[gNum] + gdX[gNum] > _width))
if (textwrapX && (cursor_x + sf->gWidth[gNum] + sf->gdX[gNum] > _width))
{
cursor_y += gFont.yAdvance;
cursor_y += sf->gFont.yAdvance;
cursor_x = 0;
}
if (textwrapY && ((cursor_y + gFont.yAdvance) >= _height)) cursor_y = 0;
if (cursor_x == 0) cursor_x -= gdX[gNum];
if (textwrapY && ((cursor_y + sf->gFont.yAdvance) >= _height)) cursor_y = 0;
if (cursor_x == 0) cursor_x -= sf->gdX[gNum];
uint8_t* pbuffer = nullptr;
const uint8_t* gPtr = (const uint8_t*) gFont.gArray;
const uint8_t* gPtr = (const uint8_t*) sf->gFont.gArray;
#ifdef FONT_FS_AVAILABLE
if (fs_font)
if (sf->fs_font)
{
fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is taking >30ms for a significant position shift
pbuffer = (uint8_t*)malloc(gWidth[gNum]);
sf->fontFile.seek(sf->gBitmap[gNum], fs::SeekSet); // This is taking >30ms for a significant position shift
pbuffer = (uint8_t*)malloc(sf->gWidth[gNum]);
}
#endif
@ -402,36 +100,36 @@ void TFT_eSPI::drawGlyph(uint16_t code)
uint32_t dl = 0;
uint8_t pixel;
int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum];
int16_t cx = cursor_x + gdX[gNum];
int16_t cy = cursor_y + sf->gFont.maxAscent - sf->gdY[gNum];
int16_t cx = cursor_x + sf->gdX[gNum];
startWrite(); // Avoid slow ESP32 transaction overhead for every pixel
for (int y = 0; y < gHeight[gNum]; y++)
for (int y = 0; y < sf->gHeight[gNum]; y++)
{
#ifdef FONT_FS_AVAILABLE
if (fs_font) {
if (spiffs)
if (sf->fs_font) {
if (sf->spiffs)
{
fontFile.read(pbuffer, gWidth[gNum]);
sf->fontFile.read(pbuffer, sf->gWidth[gNum]);
//Serial.println("SPIFFS");
}
else
{
endWrite(); // Release SPI for SD card transaction
fontFile.read(pbuffer, gWidth[gNum]);
sf->fontFile.read(pbuffer,sf-> gWidth[gNum]);
startWrite(); // Re-start SPI for TFT transaction
//Serial.println("Not SPIFFS");
}
}
#endif
for (int x = 0; x < gWidth[gNum]; x++)
for (int x = 0; x < sf->gWidth[gNum]; x++)
{
#ifdef FONT_FS_AVAILABLE
if (fs_font) pixel = pbuffer[x];
if (sf->fs_font) pixel = pbuffer[x];
else
#endif
pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
pixel = pgm_read_byte(gPtr + sf->gBitmap[gNum] + x + sf->gWidth[gNum] * y);
if (pixel)
{
@ -460,14 +158,14 @@ void TFT_eSPI::drawGlyph(uint16_t code)
}
if (pbuffer) free(pbuffer);
cursor_x += gxAdvance[gNum];
cursor_x += sf->gxAdvance[gNum];
endWrite();
}
else
{
// Not a Unicode in font so draw a rectangle and move on cursor
drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg);
cursor_x += gFont.spaceWidth + 1;
drawRect(cursor_x, cursor_y + sf->gFont.maxAscent - sf->gFont.ascent, sf->gFont.spaceWidth, sf->gFont.ascent, fg);
cursor_x += sf->gFont.spaceWidth + 1;
}
}
@ -477,7 +175,7 @@ void TFT_eSPI::drawGlyph(uint16_t code)
*************************************************************************************x*/
void TFT_eSPI::showFont(uint32_t td)
{
if(!fontLoaded) return;
if( !fontLoaded || sf == nullptr) return;
int16_t cursorX = width(); // Force start of new page to initialise cursor
int16_t cursorY = height();// for the first character
@ -485,15 +183,15 @@ void TFT_eSPI::showFont(uint32_t td)
fillScreen(textbgcolor);
for (uint16_t i = 0; i < gFont.gCount; i++)
for (uint16_t i = 0; i < sf->gFont.gCount; i++)
{
// Check if this will need a new screen
if (cursorX + gdX[i] + gWidth[i] >= width()) {
cursorX = -gdX[i];
if (cursorX + sf->gdX[i] + sf->gWidth[i] >= width()) {
cursorX = -sf->gdX[i];
cursorY += gFont.yAdvance;
if (cursorY + gFont.maxAscent + gFont.descent >= height()) {
cursorX = -gdX[i];
cursorY += sf->gFont.yAdvance;
if (cursorY + sf->gFont.maxAscent + sf->gFont.descent >= height()) {
cursorX = -sf->gdX[i];
cursorY = 0;
delay(timeDelay);
timeDelay = td;
@ -502,8 +200,8 @@ void TFT_eSPI::showFont(uint32_t td)
}
setCursor(cursorX, cursorY);
drawGlyph(gUnicode[i]);
cursorX += gxAdvance[i];
drawGlyph(sf->gUnicode[i]);
cursorX += sf->gxAdvance[i];
//cursorX += printToSprite( cursorX, cursorY, i );
yield();
}
@ -511,5 +209,4 @@ void TFT_eSPI::showFont(uint32_t td)
delay(timeDelay);
fillScreen(textbgcolor);
//fontFile.close();
}

View File

@ -1,61 +1,24 @@
// Coded by Bodmer 10/2/18, see license in root directory.
// This is part of the TFT_eSPI class and is associated with anti-aliased font functions
// This is part of the TFT_eSPI class and is associated with anti-aliased font function
protected:
TFT_eSPI_SmoothFont * sf = nullptr;
bool fontLoaded = false; // Flags when a anti-aliased font is loaded
public:
// These are for the new antialiased fonts
void loadFont(const uint8_t array[]);
#ifdef FONT_FS_AVAILABLE
void loadFont(String fontName, fs::FS &ffs);
#endif
void loadFont(String fontName, bool flash = true);
void setFont( TFT_eSPI_SmoothFont * font) { sf = font; fontLoaded = true;};
void unloadFont( void );
bool getUnicodeIndex(uint16_t unicode, uint16_t *index);
virtual void drawGlyph(uint16_t code);
void showFont(uint32_t td);
// This is for the whole font
typedef struct
{
const uint8_t* gArray; //array start pointer
uint16_t gCount; // Total number of characters
uint16_t yAdvance; // Line advance
uint16_t spaceWidth; // Width of a space character
int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller
int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent
uint16_t maxAscent; // Maximum ascent found in font
uint16_t maxDescent; // Maximum descent found in font
} fontMetrics;
fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 };
// These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time)
uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential
uint8_t* gHeight = NULL; //cheight
uint8_t* gWidth = NULL; //cwidth
uint8_t* gxAdvance = NULL; //setWidth
int16_t* gdY = NULL; //topExtent
int8_t* gdX = NULL; //leftExtent
uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap
bool fontLoaded = false; // Flags when a anti-aliased font is loaded
#ifdef FONT_FS_AVAILABLE
fs::File fontFile;
fs::FS &fontFS = SPIFFS;
bool spiffs = true;
bool fs_font = false; // For ESP32/8266 use smooth font file or FLASH (PROGMEM) array
#else
bool fontFile = true;
#endif
private:
void loadMetrics(void);
uint32_t readInt32(void);
uint8_t* fontPtr = nullptr;

View File

@ -14,7 +14,7 @@
** Function name: TFT_eSprite
** Description: Class constructor
*************************************************************************************x*/
TFT_eSprite::TFT_eSprite(TFT_eSPI *tft)
TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) : TFT_eSPI()
{
_tft = tft; // Pointer to tft class so we can call member functions
@ -104,7 +104,6 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames)
*************************************************************************************x*/
TFT_eSprite::~TFT_eSprite(void)
{
unloadFont();
deleteSprite();
}
@ -2198,8 +2197,8 @@ void TFT_eSprite::drawGlyph(uint16_t code)
if (code < 0x21)
{
if (code == 0x20) {
if (_created) this->cursor_x += this->gFont.spaceWidth;
else this->cursor_x += this->gFont.spaceWidth;
if (_created) this->cursor_x += sf->gFont.spaceWidth;
else this->cursor_x += sf->gFont.spaceWidth;
return;
}
@ -2207,14 +2206,14 @@ void TFT_eSprite::drawGlyph(uint16_t code)
if (_created)
{
this->cursor_x = 0;
this->cursor_y += this->gFont.yAdvance;
this->cursor_y += sf->gFont.yAdvance;
if (this->cursor_y >= _height) this->cursor_y = 0;
return;
}
else
{
cursor_x = 0;
cursor_y += gFont.yAdvance;
cursor_y += sf->gFont.yAdvance;
if (cursor_y >= _height) cursor_y = 0;
return;
}
@ -2222,7 +2221,7 @@ void TFT_eSprite::drawGlyph(uint16_t code)
}
uint16_t gNum = 0;
bool found = this->getUnicodeIndex(code, &gNum);
bool found = sf->getUnicodeIndex(code, &gNum);
uint16_t fg = this->textcolor;
uint16_t bg = this->textbgcolor;
@ -2234,31 +2233,31 @@ void TFT_eSprite::drawGlyph(uint16_t code)
if (newSprite)
{
createSprite(this->gWidth[gNum], this->gFont.yAdvance);
createSprite(sf->gWidth[gNum], sf->gFont.yAdvance);
if(bg) fillSprite(bg);
this->cursor_x = -this->gdX[gNum];
this->cursor_x = -sf->gdX[gNum];
this->cursor_y = 0;
}
else
{
if( this->textwrapX && ((this->cursor_x + this->gWidth[gNum] + this->gdX[gNum]) > _iwidth)) {
this->cursor_y += this->gFont.yAdvance;
if( this->textwrapX && ((this->cursor_x + sf->gWidth[gNum] + sf->gdX[gNum]) > _iwidth)) {
this->cursor_y += sf->gFont.yAdvance;
this->cursor_x = 0;
}
if( this->textwrapY && ((this->cursor_y + this->gFont.yAdvance) > _iheight)) this->cursor_y = 0;
if( this->textwrapY && ((this->cursor_y + sf->gFont.yAdvance) > _iheight)) this->cursor_y = 0;
if ( this->cursor_x == 0) this->cursor_x -= this->gdX[gNum];
if ( this->cursor_x == 0) this->cursor_x -= sf->gdX[gNum];
}
uint8_t* pbuffer = nullptr;
const uint8_t* gPtr = (const uint8_t*) this->gFont.gArray;
const uint8_t* gPtr = (const uint8_t*) sf->gFont.gArray;
#ifdef FONT_FS_AVAILABLE
if (this->fs_font) {
this->fontFile.seek(this->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift!
pbuffer = (uint8_t*)malloc(this->gWidth[gNum]);
if (sf->fs_font) {
sf->fontFile.seek(sf->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift!
pbuffer = (uint8_t*)malloc(sf->gWidth[gNum]);
}
#endif
@ -2266,60 +2265,60 @@ void TFT_eSprite::drawGlyph(uint16_t code)
uint16_t dl = 0;
uint8_t pixel = 0;
for (int32_t y = 0; y < this->gHeight[gNum]; y++)
for (int32_t y = 0; y < sf->gHeight[gNum]; y++)
{
#ifdef FONT_FS_AVAILABLE
if (this->fs_font) {
this->fontFile.read(pbuffer, this->gWidth[gNum]);
if (sf->fs_font) {
sf->fontFile.read(pbuffer, sf->gWidth[gNum]);
}
#endif
for (int32_t x = 0; x < this->gWidth[gNum]; x++)
for (int32_t x = 0; x < sf->gWidth[gNum]; x++)
{
#ifdef FONT_FS_AVAILABLE
if (this->fs_font) {
if (sf->fs_font) {
pixel = pbuffer[x];
}
else
#endif
pixel = pgm_read_byte(gPtr + this->gBitmap[gNum] + x + this->gWidth[gNum] * y);
pixel = pgm_read_byte(gPtr + sf->gBitmap[gNum] + x + sf->gWidth[gNum] * y);
if (pixel)
{
if (pixel != 0xFF)
{
if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; }
if (_bpp != 1) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], alphaBlend(pixel, fg, bg));
else if (pixel>127) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], fg);
if (dl) { drawFastHLine( xs, y + this->cursor_y + sf->gFont.maxAscent - sf->gdY[gNum], dl, fg); dl = 0; }
if (_bpp != 1) drawPixel(x + this->cursor_x + sf->gdX[gNum], y + this->cursor_y + sf->gFont.maxAscent - sf->gdY[gNum], alphaBlend(pixel, fg, bg));
else if (pixel>127) drawPixel(x + this->cursor_x + sf->gdX[gNum], y + this->cursor_y + sf->gFont.maxAscent - sf->gdY[gNum], fg);
}
else
{
if (dl==0) xs = x + this->cursor_x + this->gdX[gNum];
if (dl==0) xs = x + this->cursor_x + sf->gdX[gNum];
dl++;
}
}
else
{
if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; }
if (dl) { drawFastHLine( xs, y + this->cursor_y + sf->gFont.maxAscent - sf->gdY[gNum], dl, fg); dl = 0; }
}
}
if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; }
if (dl) { drawFastHLine( xs, y + this->cursor_y + sf->gFont.maxAscent - sf->gdY[gNum], dl, fg); dl = 0; }
}
if (pbuffer) free(pbuffer);
if (newSprite)
{
pushSprite(this->cursor_x + this->gdX[gNum], this->cursor_y, bg);
pushSprite(this->cursor_x + sf->gdX[gNum], this->cursor_y, bg);
deleteSprite();
this->cursor_x += this->gxAdvance[gNum];
this->cursor_x += sf->gxAdvance[gNum];
}
else this->cursor_x += this->gxAdvance[gNum];
else this->cursor_x += sf->gxAdvance[gNum];
}
else
{
// Not a Unicode in font so draw a rectangle and move on cursor
drawRect(this->cursor_x, this->cursor_y + this->gFont.maxAscent - this->gFont.ascent, this->gFont.spaceWidth, this->gFont.ascent, fg);
this->cursor_x += this->gFont.spaceWidth + 1;
drawRect(this->cursor_x, this->cursor_y + sf->gFont.maxAscent - sf->gFont.ascent, sf->gFont.spaceWidth, sf->gFont.ascent, fg);
this->cursor_x += sf->gFont.spaceWidth + 1;
}
}
@ -2358,16 +2357,16 @@ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string)
while (n < len)
{
uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
if (this->getUnicodeIndex(unicode, &index))
if (sf->getUnicodeIndex(unicode, &index))
{
if (n == 0) sWidth -= this->gdX[index];
if (n == len-1) sWidth += ( this->gWidth[index] + this->gdX[index]);
else sWidth += this->gxAdvance[index];
if (n == 0) sWidth -= sf->gdX[index];
if (n == len-1) sWidth += ( sf->gWidth[index] + sf->gdX[index]);
else sWidth += sf->gxAdvance[index];
}
else sWidth += this->gFont.spaceWidth + 1;
else sWidth += sf->gFont.spaceWidth + 1;
}
createSprite(sWidth, this->gFont.yAdvance);
createSprite(sWidth, sf->gFont.yAdvance);
if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor);
}
@ -2397,22 +2396,22 @@ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string)
int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index)
{
bool newSprite = !_created;
int16_t sWidth = this->gWidth[index];
int16_t sWidth = sf->gWidth[index];
if (newSprite)
{
createSprite(sWidth, this->gFont.yAdvance);
createSprite(sWidth, sf->gFont.yAdvance);
if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor);
drawGlyph(this->gUnicode[index]);
drawGlyph(sf->gUnicode[index]);
pushSprite(x + this->gdX[index], y, this->textbgcolor);
pushSprite(x + sf->gdX[index], y, this->textbgcolor);
deleteSprite();
}
else drawGlyph(this->gUnicode[index]);
else drawGlyph(sf->gUnicode[index]);
return this->gxAdvance[index];
return sf->gxAdvance[index];
}
#endif

View File

@ -9,7 +9,7 @@ class TFT_eSprite : public TFT_eSPI {
public:
TFT_eSprite(TFT_eSPI *tft);
TFT_eSprite(TFT_eSPI *tft) ;
// Create a sprite of width x height pixels, return a pointer to the RAM area
// Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed

View File

@ -26,7 +26,9 @@
#include "Processors/TFT_eSPI_Generic.c"
#endif
#ifdef SMOOTH_FONT
#include "Extensions/SmoothFont.cpp"
#endif
/***************************************************************************************
** Function name: begin_tft_write (was called spi_begin)
** Description: Start SPI transaction for writes and select TFT
@ -198,9 +200,6 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h)
_cp437 = true;
_utf8 = true;
#ifdef FONT_FS_AVAILABLE
fs_font = true; // Smooth font filing system or array (fs_font = false) flag
#endif
#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
if (psramFound()) _psram_enable = true; // Enable the use of PSRAM (if available)
@ -2358,16 +2357,16 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font)
while (*string) {
uniCode = decodeUTF8(*string++);
if (uniCode) {
if (uniCode == 0x20) str_width += gFont.spaceWidth;
if (uniCode == 0x20) str_width += sf->gFont.spaceWidth;
else {
uint16_t gNum = 0;
bool found = getUnicodeIndex(uniCode, &gNum);
bool found = sf->getUnicodeIndex(uniCode, &gNum);
if (found) {
if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum];
if (*string || isDigits) str_width += gxAdvance[gNum];
else str_width += (gdX[gNum] + gWidth[gNum]);
if(str_width == 0 && sf->gdX[gNum] < 0) str_width -= sf->gdX[gNum];
if (*string || isDigits) str_width += sf->gxAdvance[gNum];
else str_width += (sf->gdX[gNum] + sf->gWidth[gNum]);
}
else str_width += gFont.spaceWidth + 1;
else str_width += sf->gFont.spaceWidth + 1;
}
}
}
@ -2435,7 +2434,7 @@ uint16_t TFT_eSPI::fontsLoaded(void)
int16_t TFT_eSPI::fontHeight(int16_t font)
{
#ifdef SMOOTH_FONT
if(fontLoaded) return gFont.yAdvance;
if(fontLoaded) return sf->gFont.yAdvance;
#endif
#ifdef LOAD_GFXFF
@ -3668,7 +3667,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8
// If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font
#ifdef SMOOTH_FONT
if(fontLoaded) {
baseline = gFont.maxAscent;
baseline = sf->gFont.maxAscent;
cheight = fontHeight();
}
else

View File

@ -144,6 +144,11 @@
#include <User_Setups/User_Custom_Fonts.h>
#endif // #ifdef LOAD_GFXFF
// Load the Anti-aliased font extension
#ifdef SMOOTH_FONT
#include "Extensions/SmoothFont.h" // Loaded if SMOOTH_FONT is defined by user
#endif
// Create a null default font in case some fonts not used (to prevent crash)
const uint8_t widtbl_null[1] = {0};
PROGMEM const uint8_t chr_null[1] = {0};