Commit 1e1e888f authored by Bodmer's avatar Bodmer

Support extended font formats

Adafruit_GFX font support extended to Unidode Basic Multilingual Plane
Print stream deocdes UTF-8
Smooth font ascent and descent (affects line spacing) changed to rely on
metrics provided by Processing IDE (issue #303)
Bug fix for font rendering with no background on RLE native fonts
parent 6a42e4b1
...@@ -97,7 +97,7 @@ void TFT_eSPI::loadFont(String fontName) ...@@ -97,7 +97,7 @@ void TFT_eSPI::loadFont(String fontName)
gFont.ascent = (uint16_t)readInt32(); // top of "d" gFont.ascent = (uint16_t)readInt32(); // top of "d"
gFont.descent = (uint16_t)readInt32(); // bottom of "p" gFont.descent = (uint16_t)readInt32(); // bottom of "p"
// These next gFont values will be updated when the Metrics are fetched // These next gFont values might be updated when the Metrics are fetched
gFont.maxAscent = gFont.ascent; // Determined from metrics gFont.maxAscent = gFont.ascent; // Determined from metrics
gFont.maxDescent = gFont.descent; // Determined from metrics gFont.maxDescent = gFont.descent; // Determined from metrics
gFont.yAdvance = gFont.ascent + gFont.descent; gFont.yAdvance = gFont.ascent + gFont.descent;
...@@ -147,11 +147,19 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) ...@@ -147,11 +147,19 @@ void TFT_eSPI::loadMetrics(uint16_t gCount)
gdX[gNum] = (int8_t)readInt32(); // x delta from cursor gdX[gNum] = (int8_t)readInt32(); // x delta from cursor
readInt32(); // ignored readInt32(); // ignored
// Different glyph sets have different ascent values not always based on "d", so get maximum glyph ascent //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) if (gdY[gNum] > gFont.maxAscent)
{ {
// Avoid UTF coding values and characters that tend to give duff values // Try to 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)) if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0))
{ {
gFont.maxAscent = gdY[gNum]; gFont.maxAscent = gdY[gNum];
#ifdef SHOW_ASCENT_DESCENT #ifdef SHOW_ASCENT_DESCENT
...@@ -159,6 +167,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) ...@@ -159,6 +167,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount)
#endif #endif
} }
} }
*/
// Different glyph sets have different descent values not always based on "p", so get maximum glyph descent // 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) if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent)
......
...@@ -8,9 +8,6 @@ ...@@ -8,9 +8,6 @@
void unloadFont( void ); void unloadFont( void );
bool getUnicodeIndex(uint16_t unicode, uint16_t *index); bool getUnicodeIndex(uint16_t unicode, uint16_t *index);
uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining);
uint16_t decodeUTF8(uint8_t c);
uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc);
virtual void drawGlyph(uint16_t code); virtual void drawGlyph(uint16_t code);
...@@ -44,9 +41,6 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; ...@@ -44,9 +41,6 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 };
String _gFontFilename; String _gFontFilename;
uint8_t decoderState = 0; // UTF8 decoder state
uint16_t decoderBuffer; // Unicode code-point buffer
bool fontLoaded = false; // Flags when a anti-aliased font is loaded bool fontLoaded = false; // Flags when a anti-aliased font is loaded
private: private:
......
...@@ -1312,13 +1312,16 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t ...@@ -1312,13 +1312,16 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t
*************************************************************************************x*/ *************************************************************************************x*/
size_t TFT_eSprite::write(uint8_t utf8) size_t TFT_eSprite::write(uint8_t utf8)
{ {
uint16_t uniCode = decodeUTF8(utf8);
if (!uniCode) return 1;
if (utf8 == '\r') return 1; if (utf8 == '\r') return 1;
#ifdef SMOOTH_FONT #ifdef SMOOTH_FONT
if(this->fontLoaded) if(this->fontLoaded)
{ {
uint16_t unicode = decodeUTF8(utf8); if (uniCode < 32 && utf8 != '\n') return 1;
if (unicode < 32 && utf8 != '\n') return 1;
//fontFile = SPIFFS.open( _gFontFilename, "r" ); //fontFile = SPIFFS.open( _gFontFilename, "r" );
//fontFile = SPIFFS.open( this->_gFontFilename, "r" ); //fontFile = SPIFFS.open( this->_gFontFilename, "r" );
...@@ -1330,7 +1333,8 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1330,7 +1333,8 @@ size_t TFT_eSprite::write(uint8_t utf8)
//} //}
//Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX);
drawGlyph(unicode); drawGlyph(uniCode);
//fontFile.close(); //fontFile.close();
return 1; return 1;
} }
...@@ -1338,10 +1342,8 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1338,10 +1342,8 @@ size_t TFT_eSprite::write(uint8_t utf8)
if (!_created ) return 1; if (!_created ) return 1;
if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors
uint8_t uniCode = utf8; // Work with a copy else if (uniCode < 32) return 1;
if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors
else if (utf8 < 32) return 1;
uint16_t width = 0; uint16_t width = 0;
uint16_t height = 0; uint16_t height = 0;
...@@ -1362,7 +1364,7 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1362,7 +1364,7 @@ size_t TFT_eSprite::write(uint8_t utf8)
if (textfont == 2) if (textfont == 2)
{ {
if (utf8 > 127) return 1; if (utf8 > 127) return 1;
// This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms)
width = pgm_read_byte(widtbl_f16 + uniCode-32); width = pgm_read_byte(widtbl_f16 + uniCode-32);
height = chr_hgt_f16; height = chr_hgt_f16;
// Font 2 is rendered in whole byte widths so we must allow for this // Font 2 is rendered in whole byte widths so we must allow for this
...@@ -1380,7 +1382,6 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1380,7 +1382,6 @@ size_t TFT_eSprite::write(uint8_t utf8)
{ {
if (utf8 > 127) return 1; if (utf8 > 127) return 1;
// Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements
// A tad slower than above but this is not significant and is more convenient for the RLE fonts
width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 );
height= pgm_read_byte( &fontdata[textfont].height ); height= pgm_read_byte( &fontdata[textfont].height );
} }
...@@ -1420,15 +1421,14 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1420,15 +1421,14 @@ size_t TFT_eSprite::write(uint8_t utf8)
} // Custom GFX font } // Custom GFX font
else else
{ {
if(utf8 == '\n') { if(utf8 == '\n') {
this->cursor_x = 0; this->cursor_x = 0;
this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
} else { } else {
if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; if (uniCode > pgm_read_word(&gfxFont->last )) return 1;
if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; if (uniCode < pgm_read_word(&gfxFont->first)) return 1;
uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); uint8_t c2 = uniCode - pgm_read_word(&gfxFont->first);
GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
uint8_t w = pgm_read_byte(&glyph->width), uint8_t w = pgm_read_byte(&glyph->width),
h = pgm_read_byte(&glyph->height); h = pgm_read_byte(&glyph->height);
...@@ -1456,7 +1456,7 @@ size_t TFT_eSprite::write(uint8_t utf8) ...@@ -1456,7 +1456,7 @@ size_t TFT_eSprite::write(uint8_t utf8)
** Function name: drawChar ** Function name: drawChar
** Description: draw a single character in the Adafruit GLCD or freefont ** Description: draw a single character in the Adafruit GLCD or freefont
*************************************************************************************x*/ *************************************************************************************x*/
void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size)
{ {
if (!_created ) return; if (!_created ) return;
...@@ -1466,6 +1466,7 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ...@@ -1466,6 +1466,7 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color
((y + 8 * size - 1) < 0)) // Clip top ((y + 8 * size - 1) < 0)) // Clip top
return; return;
if (c < 32) return;
#ifdef LOAD_GLCD #ifdef LOAD_GLCD
//>>>>>>>>>>>>>>>>>> //>>>>>>>>>>>>>>>>>>
#ifdef LOAD_GFXFF #ifdef LOAD_GFXFF
...@@ -1534,15 +1535,15 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ...@@ -1534,15 +1535,15 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color
#ifdef LOAD_GFXFF #ifdef LOAD_GFXFF
// Filter out bad characters not present in font // Filter out bad characters not present in font
if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last )))
{ {
//>>>>>>>>>>>>>>>>>>>>>>>>>>> //>>>>>>>>>>>>>>>>>>>>>>>>>>>
c -= pgm_read_byte(&gfxFont->first); c -= pgm_read_word(&gfxFont->first);
GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap);
uint16_t bo = pgm_read_word(&glyph->bitmapOffset); uint32_t bo = pgm_read_word(&glyph->bitmapOffset);
uint8_t w = pgm_read_byte(&glyph->width), uint8_t w = pgm_read_byte(&glyph->width),
h = pgm_read_byte(&glyph->height); h = pgm_read_byte(&glyph->height);
//xa = pgm_read_byte(&glyph->xAdvance); //xa = pgm_read_byte(&glyph->xAdvance);
...@@ -1597,15 +1598,19 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ...@@ -1597,15 +1598,19 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color
** Function name: drawChar ** Function name: drawChar
** Description: draw a unicode onto the screen ** Description: draw a unicode onto the screen
*************************************************************************************x*/ *************************************************************************************x*/
// Any UTF-8 decoding must be done before calling drawChar()
int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y)
{ {
return drawChar(uniCode, x, y, textfont); return drawChar(uniCode, x, y, textfont);
} }
// Any UTF-8 decoding must be done before calling drawChar()
int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font)
{ {
if (!_created ) return 0; if (!_created ) return 0;
if (!uniCode) return 0;
if (font==1) if (font==1)
{ {
#ifdef LOAD_GLCD #ifdef LOAD_GLCD
...@@ -1630,9 +1635,9 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo ...@@ -1630,9 +1635,9 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo
} }
else else
{ {
if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) ))
{ {
uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first);
GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
return pgm_read_byte(&glyph->xAdvance) * textsize; return pgm_read_byte(&glyph->xAdvance) * textsize;
} }
......
...@@ -31,7 +31,7 @@ class TFT_eSprite : public TFT_eSPI { ...@@ -31,7 +31,7 @@ class TFT_eSprite : public TFT_eSPI {
void drawPixel(int32_t x, int32_t y, uint32_t color); void drawPixel(int32_t x, int32_t y, uint32_t color);
void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), void drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size),
fillSprite(uint32_t color), fillSprite(uint32_t color),
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#ifdef LOAD_GFXFF #ifdef LOAD_GFXFF
typedef struct { // Data stored PER GLYPH typedef struct { // Data stored PER GLYPH
uint16_t bitmapOffset; // Pointer into GFXfont->bitmap uint32_t bitmapOffset; // Pointer into GFXfont->bitmap
uint8_t width, height; // Bitmap dimensions in pixels uint8_t width, height; // Bitmap dimensions in pixels
uint8_t xAdvance; // Distance to advance cursor (x axis) uint8_t xAdvance; // Distance to advance cursor (x axis)
int8_t xOffset, yOffset; // Dist from cursor pos to UL corner int8_t xOffset, yOffset; // Dist from cursor pos to UL corner
...@@ -21,7 +21,7 @@ typedef struct { // Data stored PER GLYPH ...@@ -21,7 +21,7 @@ typedef struct { // Data stored PER GLYPH
typedef struct { // Data stored for FONT AS A WHOLE: typedef struct { // Data stored for FONT AS A WHOLE:
uint8_t *bitmap; // Glyph bitmaps, concatenated uint8_t *bitmap; // Glyph bitmaps, concatenated
GFXglyph *glyph; // Glyph array GFXglyph *glyph; // Glyph array
uint8_t first, last; // ASCII extents uint16_t first, last; // ASCII extents
uint8_t yAdvance; // Newline distance (y axis) uint8_t yAdvance; // Newline distance (y axis)
} GFXfont; } GFXfont;
......
This diff is collapsed.
...@@ -668,7 +668,7 @@ class TFT_eSPI : public Print { ...@@ -668,7 +668,7 @@ class TFT_eSPI : public Print {
// These are virtual so the TFT_eSprite class can override them with sprite specific functions // These are virtual so the TFT_eSprite class can override them with sprite specific functions
virtual void drawPixel(int32_t x, int32_t y, uint32_t color), virtual void drawPixel(int32_t x, int32_t y, uint32_t color),
drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size),
drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color),
drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color),
drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color),
...@@ -813,6 +813,8 @@ class TFT_eSPI : public Print { ...@@ -813,6 +813,8 @@ class TFT_eSPI : public Print {
void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead
void endWrite(void); // End SPI transaction void endWrite(void); // End SPI transaction
uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining);
uint16_t decodeUTF8(uint8_t c);
size_t write(uint8_t); size_t write(uint8_t);
#ifdef TFT_SDA_READ #ifdef TFT_SDA_READ
...@@ -838,6 +840,9 @@ class TFT_eSPI : public Print { ...@@ -838,6 +840,9 @@ class TFT_eSPI : public Print {
int16_t _xpivot; // x pivot point coordinate int16_t _xpivot; // x pivot point coordinate
int16_t _ypivot; // x pivot point coordinate int16_t _ypivot; // x pivot point coordinate
uint8_t decoderState = 0; // UTF8 decoder state
uint16_t decoderBuffer; // Unicode code-point buffer
private: private:
inline void spi_begin() __attribute__((always_inline)); inline void spi_begin() __attribute__((always_inline));
......
...@@ -41,8 +41,8 @@ void loop(void) { ...@@ -41,8 +41,8 @@ void loop(void) {
tft.getSetup(user); // tft.getSetup(user); //
Serial.printf("\n[code]\n"); Serial.printf("\n[code]\n");
String ver = user.version;
Serial.println("TFT_eSPI ver = " + ver +"\n"); Serial.printf("TFT_eSPI ver = " + user.version) +"\n");
Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Processor = ESP%i\n", user.esp, HEX);
Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz());
#ifdef ESP8266 #ifdef ESP8266
......
{ {
"name": "TFT_eSPI", "name": "TFT_eSPI",
"version": "1.4.3", "version": "1.4.4",
"keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789",
"description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32",
"repository": "repository":
......
name=TFT_eSPI name=TFT_eSPI
version=1.4.3 version=1.4.4
author=Bodmer author=Bodmer
maintainer=Bodmer maintainer=Bodmer
sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment