From 3941d5a1e3217858bcfe8c9908382ee5b9e9948d Mon Sep 17 00:00:00 2001 From: Piotr Gaczkowski Date: Fri, 3 Feb 2023 17:12:36 +0100 Subject: [PATCH] Add option to use UTF-8 glyphs --- Adafruit_GFX.cpp | 98 +++++++++++++++++++++++++++++++++++++++--------- Adafruit_GFX.h | 27 +++++++++++-- 2 files changed, 103 insertions(+), 22 deletions(-) diff --git a/Adafruit_GFX.cpp b/Adafruit_GFX.cpp index af989002..1020c538 100644 --- a/Adafruit_GFX.cpp +++ b/Adafruit_GFX.cpp @@ -116,6 +116,7 @@ Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) { textcolor = textbgcolor = 0xFFFF; wrap = true; _cp437 = false; + _utf8 = false; gfxFont = NULL; } @@ -1105,15 +1106,15 @@ void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, @brief Draw a single character @param x Bottom left corner x coordinate @param y Bottom left corner y coordinate - @param c The 8-bit font-indexed character (likely ascii) + @param c The 16-bit font-indexed character (likely ascii) @param color 16-bit 5-6-5 Color to draw chraracter with @param bg 16-bit 5-6-5 Color to fill background with (if same as color, no background) @param size Font magnification level, 1 is 'original' size */ /**************************************************************************/ -void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, - uint16_t color, uint16_t bg, uint8_t size) { +void Adafruit_GFX::drawChar(int16_t x, int16_t y, uint16_t c, uint16_t color, + uint16_t bg, uint8_t size) { drawChar(x, y, c, color, bg, size, size); } @@ -1123,7 +1124,7 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, @brief Draw a single character @param x Bottom left corner x coordinate @param y Bottom left corner y coordinate - @param c The 8-bit font-indexed character (likely ascii) + @param c The 16-bit font-indexed character (likely ascii) @param color 16-bit 5-6-5 Color to draw chraracter with @param bg 16-bit 5-6-5 Color to fill background with (if same as color, no background) @@ -1131,9 +1132,8 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, @param size_y Font magnification level in Y-axis, 1 is 'original' size */ /**************************************************************************/ -void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, - uint16_t color, uint16_t bg, uint8_t size_x, - uint8_t size_y) { +void Adafruit_GFX::drawChar(int16_t x, int16_t y, uint16_t c, uint16_t color, + uint16_t bg, uint8_t size_x, uint8_t size_y) { if (!gfxFont) { // 'Classic' built-in font @@ -1178,9 +1178,9 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, // newlines, returns, non-printable characters, etc. Calling // drawChar() directly with 'bad' characters of font may cause mayhem! - c -= (uint8_t)pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c); - uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont); + c -= pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); uint16_t bo = pgm_read_word(&glyph->bitmapOffset); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); @@ -1233,15 +1233,76 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, } // End classic vs custom font } + +/**************************************************************************/ +/*! + @brief Serial UTF-8 decoder + @param c 8 bit value from encoded stream + @returns 0 if decoding is not complete yet, 16 bit code point + otherwise. Can cast to 8 bits for ASCII range (0-255) +*/ +/**************************************************************************/ + +uint16_t Adafruit_GFX::decodeUTF8(uint8_t c) { + // 7 bit Unicode Code Point + if ((c & 0x80) == 0x00) { + decoderState = 0; + return (uint16_t)c; + } + + if (decoderState == 0) { + // 11 bit Unicode Code Point + if ((c & 0xE0) == 0xC0) { + decoderBuffer = ((c & 0x1F) << 6); // Save first 5 bits + decoderState = 1; + return 0; + } + + // 16 bit Unicode Code Point + if ((c & 0xF0) == 0xE0) { + decoderBuffer = ((c & 0x0F) << 12); // Save first 4 bits + decoderState = 2; + return 0; + } + + // 21 bit Unicode Code Point not supported so fall-back to extended ASCII + if ((c & 0xF8) == 0xF0) + return (uint16_t)c; + } else { + if (decoderState == 2) { + decoderBuffer |= + ((c & 0x3F) << 6); // Add next 6 bits of 16 bit code point + decoderState--; + return 0; + } else // decoderState must be == 1 + { + decoderBuffer |= (c & 0x3F); // Add last 6 bits of code point + decoderState = 0; + return decoderBuffer; + } + } + + decoderState = 0; + + return (uint16_t)c; // fall-back to extended ASCII +} + /**************************************************************************/ /*! @brief Print one byte/character of data, used to support print() - @param c The 8-bit ascii character to write + @param data The 8-bit UTF-8 or ascii character to write */ /**************************************************************************/ -size_t Adafruit_GFX::write(uint8_t c) { - if (!gfxFont) { // 'Classic' built-in font +size_t Adafruit_GFX::write(uint8_t data) { + uint16_t c = (uint16_t)data; + if (_utf8) + c = decodeUTF8(data); + if (c == 0) + return 1; + if (!gfxFont) { // 'Classic' built-in font + if (c > 255) + return 1; // Stop 16 bit characters if (c == '\n') { // Newline? cursor_x = 0; // Reset x to zero, cursor_y += textsize_y * 8; // advance y one line @@ -1262,9 +1323,10 @@ size_t Adafruit_GFX::write(uint8_t c) { cursor_y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if (c != '\r') { - uint8_t first = pgm_read_byte(&gfxFont->first); - if ((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { - GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); + uint16_t first = pgm_read_word(&gfxFont->first); + if ((c >= first) && (c <= pgm_read_word(&gfxFont->last))) { + GFXglyph *glyph = + &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); if ((w > 0) && (h > 0)) { // Is there an associated bitmap? @@ -1378,8 +1440,8 @@ void Adafruit_GFX::charBounds(unsigned char c, int16_t *x, int16_t *y, *x = 0; // Reset x to zero, advance y by one line *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if (c != '\r') { // Not a carriage return; is normal char - uint8_t first = pgm_read_byte(&gfxFont->first), - last = pgm_read_byte(&gfxFont->last); + uint16_t first = pgm_read_word(&gfxFont->first), + last = pgm_read_word(&gfxFont->last); if ((c >= first) && (c <= last)) { // Char present in this font? GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); uint8_t gw = pgm_read_byte(&glyph->width), diff --git a/Adafruit_GFX.h b/Adafruit_GFX.h index 63c6ab68..24527c07 100644 --- a/Adafruit_GFX.h +++ b/Adafruit_GFX.h @@ -107,10 +107,10 @@ class Adafruit_GFX : public Print { const uint8_t mask[], int16_t w, int16_t h); void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h); - void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, - uint16_t bg, uint8_t size); - void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, - uint16_t bg, uint8_t size_x, uint8_t size_y); + void drawChar(int16_t x, int16_t y, uint16_t c, uint16_t color, uint16_t bg, + uint8_t size); + void drawChar(int16_t x, int16_t y, uint16_t c, uint16_t color, uint16_t bg, + uint8_t size_x, uint8_t size_y); void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); void getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y, @@ -121,6 +121,9 @@ class Adafruit_GFX : public Print { void setTextSize(uint8_t sx, uint8_t sy); void setFont(const GFXfont *f = NULL); + // Serial UTF-8 decoder + uint16_t decodeUTF8(uint8_t c); + /**********************************************************************/ /*! @brief Set text cursor location @@ -180,6 +183,18 @@ class Adafruit_GFX : public Print { /**********************************************************************/ void cp437(bool x = true) { _cp437 = x; } + /**********************************************************************/ + /*! + @brief Enable (or disable) UTF-8-compatible charset in custom made fonts + By default, the font.h files boundled to the library use ASCII + charset as UTF-8 fonts need more memory as many AVR chips can offer. Pass + 'true' to this function if you are willing to use UTF-8 fonts that you + generated whith fontconvert and also your board has enough free memory. + @param x true = enable (new behavior), false = disable (old behavior) + */ + /**********************************************************************/ + void utf8(boolean x = true) { _utf8 = x; } + using Print::write; #if ARDUINO >= 100 virtual size_t write(uint8_t); @@ -245,7 +260,11 @@ class Adafruit_GFX : public Print { uint8_t rotation; ///< Display rotation (0 thru 3) bool wrap; ///< If set, 'wrap' text at right edge of display bool _cp437; ///< If set, use correct CP437 charset (default is off) + bool _utf8; ///< If set, use correct UTF-8 charset (default is off) GFXfont *gfxFont; ///< Pointer to special font + + uint8_t decoderState = 0; ///< UTF-8 decoder state + uint16_t decoderBuffer; ///< Unicode code-point buffer }; /// A simple drawn button UI element