From 1e1e888fa0767e3e72c93950921bb5ed281fba2c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Feb 2019 21:36:40 +0000 Subject: [PATCH] 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 --- Extensions/Smooth_font.cpp | 17 ++- Extensions/Smooth_font.h | 6 - Extensions/Sprite.cpp | 43 +++--- Extensions/Sprite.h | 2 +- Fonts/GFXFF/gfxfont.h | 4 +- TFT_eSPI.cpp | 113 ++++++++------- TFT_eSPI.h | 7 +- .../Create_font/Create_font.pde | 130 +++++++++++------- .../Read_User_Setup/Read_User_Setup.ino | 4 +- library.json | 2 +- library.properties | 2 +- 11 files changed, 188 insertions(+), 142 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 855aa62..8d7628f 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -97,7 +97,7 @@ void TFT_eSPI::loadFont(String fontName) gFont.ascent = (uint16_t)readInt32(); // top of "d" 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.maxDescent = gFont.descent; // Determined from metrics gFont.yAdvance = gFont.ascent + gFont.descent; @@ -147,11 +147,19 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) gdX[gNum] = (int8_t)readInt32(); // x delta from cursor 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) { - // 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)) + // 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 @@ -159,6 +167,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) #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) diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h index 2c7faa2..1bc9fd2 100644 --- a/Extensions/Smooth_font.h +++ b/Extensions/Smooth_font.h @@ -8,9 +8,6 @@ void unloadFont( void ); 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); virtual void drawGlyph(uint16_t code); @@ -44,9 +41,6 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; 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 private: diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 6bc58b2..3782775 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1312,13 +1312,16 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t *************************************************************************************x*/ size_t TFT_eSprite::write(uint8_t utf8) { + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + if (utf8 == '\r') return 1; #ifdef SMOOTH_FONT 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( this->_gFontFilename, "r" ); @@ -1330,7 +1333,8 @@ size_t TFT_eSprite::write(uint8_t utf8) //} //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); - drawGlyph(unicode); + drawGlyph(uniCode); + //fontFile.close(); return 1; } @@ -1338,10 +1342,8 @@ size_t TFT_eSprite::write(uint8_t utf8) if (!_created ) return 1; - - uint8_t uniCode = utf8; // Work with a copy - if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors - else if (utf8 < 32) return 1; + if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (uniCode < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -1362,7 +1364,7 @@ size_t TFT_eSprite::write(uint8_t utf8) if (textfont == 2) { 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); height = chr_hgt_f16; // 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) { if (utf8 > 127) return 1; // 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 ); height= pgm_read_byte( &fontdata[textfont].height ); } @@ -1420,15 +1421,14 @@ size_t TFT_eSprite::write(uint8_t utf8) } // Custom GFX font else { - if(utf8 == '\n') { this->cursor_x = 0; this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; + if (uniCode > pgm_read_word(&gfxFont->last )) 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]); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); @@ -1456,7 +1456,7 @@ size_t TFT_eSprite::write(uint8_t utf8) ** Function name: drawChar ** Description: draw a single character in the Adafruit GLCD or freefont *************************************************************************************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; @@ -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 return; + if (c < 32) return; #ifdef LOAD_GLCD //>>>>>>>>>>>>>>>>>> #ifdef LOAD_GFXFF @@ -1534,15 +1535,15 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color #ifdef LOAD_GFXFF // 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]); 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), h = pgm_read_byte(&glyph->height); //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 ** Function name: drawChar ** Description: draw a unicode onto the screen *************************************************************************************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) { 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) { if (!_created ) return 0; + if (!uniCode) return 0; + if (font==1) { #ifdef LOAD_GLCD @@ -1630,9 +1635,9 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo } 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]); return pgm_read_byte(&glyph->xAdvance) * textsize; } diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 2bf800c..5dea24e 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -31,7 +31,7 @@ class TFT_eSprite : public TFT_eSPI { 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), diff --git a/Fonts/GFXFF/gfxfont.h b/Fonts/GFXFF/gfxfont.h index eae3997..cd2bccc 100644 --- a/Fonts/GFXFF/gfxfont.h +++ b/Fonts/GFXFF/gfxfont.h @@ -12,7 +12,7 @@ #ifdef LOAD_GFXFF 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 xAdvance; // Distance to advance cursor (x axis) int8_t xOffset, yOffset; // Dist from cursor pos to UL corner @@ -21,7 +21,7 @@ typedef struct { // Data stored PER GLYPH typedef struct { // Data stored for FONT AS A WHOLE: uint8_t *bitmap; // Glyph bitmaps, concatenated GFXglyph *glyph; // Glyph array - uint8_t first, last; // ASCII extents + uint16_t first, last; // ASCII extents uint8_t yAdvance; // Newline distance (y axis) } GFXfont; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4dc8679..9c4fc66 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2272,21 +2272,22 @@ int16_t TFT_eSPI::textWidth(const char *string) int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) { - int32_t str_width = 0; + int32_t str_width = 0; + uint16_t uniCode = 0; #ifdef SMOOTH_FONT if(fontLoaded) { while (*string) { - uint16_t unicode = decodeUTF8(*string++); - if (unicode) + uniCode = decodeUTF8(*string++); + if (uniCode) { - if (unicode == 0x20) str_width += gFont.spaceWidth; + if (uniCode == 0x20) str_width += gFont.spaceWidth; else { uint16_t gNum = 0; - bool found = getUnicodeIndex(unicode, &gNum); + bool found = getUnicodeIndex(uniCode, &gNum); if (found) { if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; @@ -2302,18 +2303,15 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) } #endif - unsigned char uniCode; - char *widthtable; - if (font>1 && font<9) { - widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop + char *widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop while (*string) { uniCode = *(string++); if (uniCode > 31 && uniCode < 128) - str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subract 32 from uniCode + str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width } } @@ -2326,9 +2324,9 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) while (*string) { uniCode = decodeUTF8(*string++); - if ((uniCode >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (uniCode <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + if ((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last ))) { - uniCode -= pgm_read_byte(&gfxFont->first); + uniCode -= pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); // If this is not the last character or is a digit then use xAdvance if (*string || isDigits) str_width += pgm_read_byte(&glyph->xAdvance); @@ -2393,7 +2391,7 @@ int16_t TFT_eSPI::fontHeight(void) ** Function name: drawChar ** Description: draw a single character in the Adafruit GLCD font ***************************************************************************************/ -void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) +void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) { if ((x >= _width) || // Clip right (y >= _height) || // Clip bottom @@ -2498,17 +2496,17 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u #ifdef LOAD_GFXFF // 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 ))) { spi_begin(); inTransaction = true; //>>>>>>>>>>>>>>>>>>>>>>>>>>> - c -= pgm_read_byte(&gfxFont->first); + c -= pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); 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), h = pgm_read_byte(&glyph->height); //xa = pgm_read_byte(&glyph->xAdvance); @@ -3991,18 +3989,18 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) ***************************************************************************************/ size_t TFT_eSPI::write(uint8_t utf8) { + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + if (utf8 == '\r') return 1; #ifdef SMOOTH_FONT if(fontLoaded) { - uint16_t unicode = decodeUTF8(utf8); - - //Serial.print("UniCode="); Serial.println(unicode); + //Serial.print("UniCode="); Serial.println(uniCode); //Serial.print("UTF8 ="); Serial.println(utf8); - if (!unicode) return 1; - //fontFile = SPIFFS.open( _gFontFilename, "r" ); //if(!fontFile) @@ -4011,16 +4009,15 @@ size_t TFT_eSPI::write(uint8_t utf8) // return 1; //} - drawGlyph(unicode); + drawGlyph(uniCode); //fontFile.close(); return 1; } #endif - uint8_t uniCode = utf8; // Work with a copy - if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors - else if (utf8 < 32) return 1; + if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (uniCode < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -4040,8 +4037,8 @@ size_t TFT_eSPI::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { - if (utf8 > 127) return 1; - // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) + if (uniCode > 127) return 1; + width = pgm_read_byte(widtbl_f16 + uniCode-32); height = chr_hgt_f16; // Font 2 is rendered in whole byte widths so we must allow for this @@ -4057,9 +4054,8 @@ size_t TFT_eSPI::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { - if (utf8 > 127) return 1; + if (uniCode > 127) return 1; // 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 ); height= pgm_read_byte( &fontdata[textfont].height ); } @@ -4098,18 +4094,15 @@ size_t TFT_eSPI::write(uint8_t utf8) } // Custom GFX font else { - uniCode = (uint8_t)decodeUTF8(utf8); - if (!uniCode) return 1; - if(utf8 == '\n') { cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; + if (uniCode > pgm_read_word(&gfxFont->last )) return 1; + if (uniCode < pgm_read_word(&gfxFont->first)) return 1; - 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]); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); @@ -4136,17 +4129,17 @@ size_t TFT_eSPI::write(uint8_t utf8) /*************************************************************************************** ** Function name: drawChar -** Description: draw a Unicode onto the screen +** Description: draw a Unicode glyph onto the screen ***************************************************************************************/ -int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y) + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) { - return drawChar(utf8, x, y, textfont); + return drawChar(uniCode, x, y, textfont); } -int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { - - uint16_t uniCode = decodeUTF8(utf8); if (!uniCode) return 0; if (font==1) @@ -4173,9 +4166,9 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) } 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]); return pgm_read_byte(&glyph->xAdvance) * textsize; } @@ -4197,7 +4190,6 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) #ifdef LOAD_FONT2 if (font == 2) { - // This is faster than using the fontdata structure flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); width = pgm_read_byte(widtbl_f16 + uniCode); height = chr_hgt_f16; @@ -4211,7 +4203,6 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) { if ((font>2) && (font<9)) { - // This is slower than above but is more convenient for the RLE fonts flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); height= pgm_read_byte( &fontdata[font].height ); @@ -4309,6 +4300,7 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) // Font is not 2 and hence is RLE encoded { spi_begin(); + inTransaction = true; w *= height; // Now w is total number of pixels in the character if ((textsize != 1) || (textcolor == textbgcolor)) { @@ -4360,6 +4352,7 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) } } + inTransaction = false; spi_end(); } else // Text colour != background && textsize = 1 @@ -4442,7 +4435,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY) return drawString(string, poX, poY, textfont); } -// With font number +// With font number. Note: font number is over-ridden if a smooth font is loaded int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font) { int16_t sumX = 0; @@ -4553,15 +4546,15 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 { cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets - uint8_t c2 = 0; + uint16_t c2 = 0; uint16_t len = strlen(string); uint16_t n = 0; while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n); - if((c2 >= pgm_read_byte(&gfxFont->first)) && (c2 <= pgm_read_byte(&gfxFont->last) )) + if((c2 >= pgm_read_word(&gfxFont->first)) && (c2 <= pgm_read_word(&gfxFont->last) )) { - c2 -= pgm_read_byte(&gfxFont->first); + c2 -= pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); xo = pgm_read_byte(&glyph->xOffset) * textsize; // Adjust for negative xOffset @@ -4576,6 +4569,9 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 } #endif + uint16_t len = strlen(string); + uint16_t n = 0; + #ifdef SMOOTH_FONT if(fontLoaded) { @@ -4584,14 +4580,13 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 //drawLine(poX, poY - 5, poX, poY + 5, TFT_GREEN); //fontFile = SPIFFS.open( _gFontFilename, "r"); if(!fontFile) return 0; - uint16_t len = strlen(string); - uint16_t n = 0; + setCursor(poX, poY); while (n < len) { - uint16_t unicode = decodeUTF8((uint8_t*)string, &n, len - n); - drawGlyph(unicode); + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + drawGlyph(uniCode); } sumX += cwidth; //fontFile.close(); @@ -4599,7 +4594,11 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 else #endif { - while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); + while (n < len) + { + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + sumX += drawChar(uniCode, poX+sumX, poY, font); + } } //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv @@ -4669,7 +4668,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 #endif //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -return sumX; +return sumX + poX; } @@ -4782,7 +4781,7 @@ int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t // No chance of overflow from here on // Get integer part - unsigned long temp = (unsigned long)floatNumber; + uint32_t temp = (uint32_t)floatNumber; // Put integer part into array ltoa(temp, str + ptr, 10); @@ -4830,7 +4829,7 @@ void TFT_eSPI::setFreeFont(const GFXfont *f) glyph_ab = 0; glyph_bb = 0; - uint8_t numChars = pgm_read_byte(&gfxFont->last) - pgm_read_byte(&gfxFont->first); + uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first); // Find the biggest above and below baseline offsets for (uint8_t c = 0; c < numChars; c++) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index b1b0cb0..55110f0 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -668,7 +668,7 @@ class TFT_eSPI : public Print { // 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), - 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), 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), @@ -813,6 +813,8 @@ class TFT_eSPI : public Print { void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead 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); #ifdef TFT_SDA_READ @@ -838,6 +840,9 @@ class TFT_eSPI : public Print { int16_t _xpivot; // 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: inline void spi_begin() __attribute__((always_inline)); diff --git a/Tools/Create_Smooth_Font/Create_font/Create_font.pde b/Tools/Create_Smooth_Font/Create_font/Create_font.pde index 1363b6b..6832248 100644 --- a/Tools/Create_Smooth_Font/Create_font/Create_font.pde +++ b/Tools/Create_Smooth_Font/Create_font/Create_font.pde @@ -1,6 +1,8 @@ // This is a Processing sketch, see https://processing.org/ to download the IDE -// Select the character range in the user configure section starting at line 100 +// Select the font, size and character ranges in the user configuration section +// of this sketch, which starts at line 120. Instructions start at line 50. + /* Software License Agreement (FreeBSD License) @@ -36,47 +38,61 @@ Software License Agreement (FreeBSD License) //////////////////////////////////////////////////////////////////////////////////////////////// - // This is a processing sketch to create font files for the TFT_eSPI library: // https://github.com/Bodmer/TFT_eSPI -// Coded by Bodmer January 2018 +// Coded by Bodmer January 2018, updated 10/2/19 +// Version 0.8 -// See comments below in code for specifying the font parameters -// (point size, unicode blocks to include etc). Ranges of characers or -// specific individual unicodes can be included in the created font file/ +// >>>>>>>>>>>>>>>>>>>> INSTRUCTIONS <<<<<<<<<<<<<<<<<<<< + +// See comments below in code for specifying the font parameters (point size, +// unicode blocks to include etc). Ranges of characters (glyphs) and specific +// individual glyphs can be included in the created "*.vlw" font file. // Created fonts are saved in the sketches "FontFiles" folder. Press Ctrl+K to -// see that folder. +// see that folder location. -// 16 bit unicodes in the range 0x0000 - 0xFFFF are supported. +// 16 bit Unicode point codes in the range 0x0000 - 0xFFFF are supported. +// Codes 0-31 are control codes such as "tab" and "carraige return" etc. +// and 32 is a "space", these should NOT be included. // The sketch will convert True Type (a .ttf or .otf file) file stored in the -// sketches "Data" folder as well as your computers system fonts. +// sketches "Data" folder as well as your computers' system fonts. -// To maximise rendering performance only include the characters you will use. -// Characters at the start of the file will render faster than those at the end. +// To maximise rendering performance and the memory consumed only include the characters +// you will use. Characters at the start of the file will render faster than those at +// the end due to the buffering and file seeking overhead. + +// The inclusion of "non-existant" characters in a font may give unpredicatable results +// when rendering with the TFT_eSPI library. The Processing sketch window that pops up +// to show the font characters will print "boxes" (also known as Tofu!) for non existant +// characters. // Once created the files must be loaded into the ESP32 or ESP8266 SPIFFS memory // using the Arduino IDE plugin detailed here: // https://github.com/esp8266/arduino-esp8266fs-plugin // https://github.com/me-no-dev/arduino-esp32fs-plugin -// The sketch list all the available PC fonts to the console, you may need to increase -// console line count (in preferences.txt) to stop some fonts scrolling out of view. +// When the sketch is run it will generate a file called "System_Font_List.txt" in the +// sketch "FontFiles" folder, press Ctrl+K to see it. Open the file in a text editor to +// view it. This list provides the font reference number needed below to locate that +// font on your system. + +// The sketch also lists all the available system fonts to the console, you can increase +// the console line count (in preferences.txt) to stop some fonts scrolling out of view. // See link in File>Preferences to locate "preferences.txt" file. You must close // Processing then edit the file lines. If Processing is not closed first then the // edits will be overwritten by defaults! Edit "preferences.txt" as follows for -// 1000 lines, then save, then run Processing again: +// 3000 lines, then save, then run Processing again: + +// console.length=3000; // Line 4 in file +// console.scrollback.lines=3000; // Line 7 in file - /* - console.length=1000 // Line 4 in file - console.scrollback.lines=1000 // Line 7 in file - */ // Useful links: - /* +/* https://en.wikipedia.org/wiki/Unicode_font @@ -86,33 +102,39 @@ Software License Agreement (FreeBSD License) http://savannah.gnu.org/projects/freefont/ http://www.google.com/get/noto/ - + https://github.com/Bodmer/TFT_eSPI https://github.com/esp8266/arduino-esp8266fs-plugin https://github.com/me-no-dev/arduino-esp32fs-plugin - */ -//////////////////////////////////////////////////////////////////////////////////////////////// + >>>>>>>>>>>>>>>>>>>> END OF INSTRUCTIONS <<<<<<<<<<<<<<<<<<<< */ -import java.awt.Desktop; + +import java.awt.Desktop; // Required to allow sketch to open file windows + + +//////////////////////////////////////////////////////////////////////////////////////////////// // >>>>>>>>>> USER CONFIGURED PARAMETERS START HERE <<<<<<<<<< +// Use font number or name, -1 for fontNumber means use fontName below, a value >=0 means use system font number from list. +// When the sketch is run it will generate a file called "systemFontList.txt" in the sketch folder, press Ctrl+K to see it. +// Open the "systemFontList.txt" in a text editor to view the font files and reference numbers for your system. -// Use font name for ttf files placed in the "Data" folder or the font number seen in IDE Console for system fonts +int fontNumber = -1; // << Use [Number] in brackets from the fonts listed. + +// OR use font name for ttf files placed in the "Data" folder or the font number seen in IDE Console for system fonts // the font numbers are listed when the sketch is run. -// | 1 2 | Maximum filename size for SPIFFS is 32 including leading / +// | 1 2 | Maximum filename size for SPIFFS is 31 including leading / // 1234567890123456789012345 and added point size and .vlw extension, so max is 25 -String fontName = "Final-Frontier"; //Manually crop the filename length later after creation if needed - -String fontType = ".ttf"; //SPIFFS does not accept underscore in filename! +String fontName = "Final-Frontier"; // Manually crop the filename length later after creation if needed + // Note: SPIFFS does NOT accept underscore in a filename! +String fontType = ".ttf"; //String fontType = ".otf"; -// Use font number instead of name, -1 means use name above, or a value >=0 means use system font number from list. -int fontNumber = -1; // << Use [Number] in brackets from the fonts listed in console window -// Define the font size in points for the created font file -int fontSize = 28; +// Define the font size in points for the TFT_eSPI font file +int fontSize = 20; // Font size to use in the Processing sketch display window that pops up (can be different to above) int displayFontSize = 28; @@ -125,7 +147,7 @@ int displayFontSize = 28; static final int[] unicodeBlocks = { // The list below has been created from the table here: https://en.wikipedia.org/wiki/Unicode_block // Remove // at start of lines below to include that unicode block, different code ranges can also be specified by - // editting the start and end of range values. Multiple lines from the list below can be included, limited only by + // editting the start and end-of-range values. Multiple lines from the list below can be included, limited only by // the final font file size! // Block range, //Block name, Code points, Assigned characters, Scripts @@ -298,20 +320,20 @@ static final int[] unicodeBlocks = { //0x0061, 0x007A, //Example custom range (Lower case a-z) }; -// Here we specify specific individual Unicodes to be included (appended at end of selected range) +// Here we specify particular individual Unicodes to be included (appended at end of selected range) static final int[] specificUnicodes = { // Commonly used codes, add or remove // in next line // 0x00A3, 0x00B0, 0x00B5, 0x03A9, 0x20AC, // £ ° µ Ω € // Numbers and characters for showing time, change next line to //* to use - /* +/* 0x002B, 0x002D, 0x002E, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, // - + . 0 1 2 3 4 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x0061, 0x006D, // 5 6 7 8 9 : a m 0x0070, // p - //*/ + //*/ - // More characters, change next line to //* to use + // More characters for TFT_eSPI test sketches, change next line to //* to use /* 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, @@ -337,8 +359,8 @@ static final int[] specificUnicodes = { //*/ }; - // >>>>>>>>>> USER CONFIGURED PARAMETERS END HERE <<<<<<<<<< + //////////////////////////////////////////////////////////////////////////////////////////////// // Variable to hold the inclusive Unicode range (16 bit values only for this sketch) @@ -347,7 +369,10 @@ int lastUnicode = 0; PFont myFont; +PrintWriter logOutput; + void setup() { + logOutput = createWriter("FontFiles/System_Font_List.txt"); size(1000, 800); @@ -355,6 +380,15 @@ void setup() { String[] fontList = PFont.list(); printArray(fontList); + // Save font list to file + for (int x = 0; x < fontList.length; x++) + { + logOutput.print("[" + x + "] "); + logOutput.println(fontList[x]); + } + logOutput.flush(); // Writes the remaining data to the file + logOutput.close(); // Finishes the file + // Set the fontName from the array number or the defined fontName if (fontNumber >= 0) { @@ -410,19 +444,19 @@ void setup() { } } - // loading the range specified + // loading the specific point codes for (int i = 0; i < specificUnicodes.length; i++) { charset[index] = Character.toChars(specificUnicodes[i])[0]; index++; } - - // Make font smooth + + // Make font smooth (anti-aliased) boolean smooth = true; // Create the font in memory myFont = createFont(fontName+fontType, displayFontSize, smooth, charset); - // Print a few characters to the sketch window + // Print characters to the sketch window fill(0, 0, 0); textFont(myFont); @@ -444,10 +478,10 @@ void setup() { int unicode = charset[index]; float cwidth = textWidth((char)unicode) + 2; if ( (x + cwidth) > (width - gapx) ) break; - - // Draw the letter to the screen + + // Draw the glyph to the screen text(new String(Character.toChars(unicode)), x, y); - + // Move cursor x += cwidth; // Increment the counter @@ -458,12 +492,12 @@ void setup() { } - // creating font + // creating font to save as a file PFont font; font = createFont(fontName+fontType, fontSize, smooth, charset); - println("Created font " + fontName + str(fontSize) + ".vlw"); + println("Created font " + fontName + str(fontSize) + ".vlw"); // creating file try { @@ -486,4 +520,4 @@ void setup() { catch(IOException e) { println("Doh! Failed to create the file"); } -} \ No newline at end of file +} diff --git a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino index 1cf8f3f..d0a2f1f 100644 --- a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino +++ b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino @@ -41,8 +41,8 @@ void loop(void) { tft.getSetup(user); // 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("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 diff --git a/library.json b/library.json index 7b1724d..f3f1f04 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "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", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 707afde..d65fa12 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.3 +version=1.4.4 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE