/************************ * western_char_set.cpp * ************************/ /**************************************************************************** * Written By Marcio Teixeira 2019 - Aleph Objects, Inc. * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * To view a copy of the GNU General Public License, go to the following * * location: . * ****************************************************************************/ #include "../ftdi_extended.h" #if defined(FTDI_EXTENDED) && BOTH(TOUCH_UI_USE_UTF8, TOUCH_UI_UTF8_WESTERN_CHARSET) #include "western_char_set_bitmap_31.h" #define NUM_ELEMENTS(a) (sizeof(a)/sizeof(a[0])) using namespace FTDI; constexpr static uint8_t std_font = 31; constexpr static uint8_t alt_font = 1; static uint32_t bitmap_addr; /* Glyphs in the WesternCharSet bitmap */ enum { GRAVE, ACUTE, CIRCUMFLEX, TILDE, DIAERESIS, DOT_ABOVE, CEDILLA, NO_DOT_I, #if ENABLED(TOUCH_UI_UTF8_GERMANIC) SHARP_S, #endif #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) LRG_O_STROKE, SML_O_STROKE, LRG_AE, SML_AE, LRG_ETH, SML_ETH, LRG_THORN, SML_THORN, #endif #if ENABLED(TOUCH_UI_UTF8_PUNCTUATION) LEFT_DBL_QUOTE, RIGHT_DBL_QUOTE, INV_EXCLAMATION, INV_QUESTION, #endif #if ENABLED(TOUCH_UI_UTF8_CURRENCY) CENT_SIGN, POUND_SIGN, CURRENCY_SIGN, YEN_SIGN, #endif #if ENABLED(TOUCH_UI_UTF8_SUPERSCRIPTS) SUPERSCRIPT_ONE, SUPERSCRIPT_TWO, SUPERSCRIPT_THREE, #endif #if ENABLED(TOUCH_UI_UTF8_ORDINALS) MASCULINE_ORDINAL, FEMININE_ORDINAL, #endif #if ENABLED(TOUCH_UI_UTF8_COPYRIGHT) COPYRIGHT_SIGN, REGISTERED_SIGN, #endif #if ENABLED(TOUCH_UI_UTF8_MATHEMATICS) PLUS_MINUS_SIGN, MULTIPLICATION_SIGN, DIVISION_SIGN, #endif #if ENABLED(TOUCH_UI_UTF8_FRACTIONS) FRACTION_QUARTER, FRACTION_HALF, FRACTION_THREE_FOURTHS, #endif #if ENABLED(TOUCH_UI_UTF8_SYMBOLS) MICRON_SIGN, PILCROW_SIGN, BROKEN_BAR, SECTION_SIGN, NOT_SIGN #endif }; /* Centerline of characters that can take accents */ constexpr int8_t mid_a = 12; constexpr int8_t mid_e = 12; constexpr int8_t mid_i = 5; constexpr int8_t mid_o = 12; constexpr int8_t mid_u = 12; constexpr int8_t mid_y = 11; constexpr int8_t mid_n = 12; constexpr int8_t mid_c = 12; constexpr int8_t mid_A = 13; constexpr int8_t mid_E = 13; constexpr int8_t mid_I = 6; constexpr int8_t mid_O = 14; constexpr int8_t mid_U = 14; constexpr int8_t mid_Y = 13; constexpr int8_t mid_N = 15; constexpr int8_t mid_C = 13; /* Centerline of accent glyphs */ constexpr int8_t mid_accent = 16; /* When reusing the DOT_ABOVE accent glyph for the degree sign, we need to trim the leading space */ constexpr uint8_t deg_sign_leading = 9; /* Look-up table for constructing characters (must be ordered by unicode) * * Characters are either complete symbols from the Western Char Set bitmap, * or they are constructed using a standard letter from the romfont and * drawing an accent from the Western Char Set bitmap over it. */ #define UTF8(A) uint16_t(utf8(U##A)) PROGMEM constexpr struct { uint16_t unicode; uint8_t std_char; // Glyph from standard ROMFONT (zero if none) uint8_t alt_char; // Glyph from Western Char Set bitmap uint8_t alt_data; // For accented characters, the centerline; else char width } char_recipe[] = { {0, 0, NO_DOT_I, 10 }, #if ENABLED(TOUCH_UI_UTF8_PUNCTUATION) {UTF8('¡'), 0 , INV_EXCLAMATION, 13 }, #endif #if ENABLED(TOUCH_UI_UTF8_CURRENCY) {UTF8('¢'), 0 , CENT_SIGN, 23 }, {UTF8('£'), 0 , POUND_SIGN, 24 }, {UTF8('¤'), 0 , CURRENCY_SIGN, 26 }, {UTF8('¥'), 0 , YEN_SIGN, 26 }, #endif #if ENABLED(TOUCH_UI_UTF8_SYMBOLS) {UTF8('¦'), 0 , BROKEN_BAR, 11 }, {UTF8('§'), 0 , SECTION_SIGN, 21 }, #endif #if ENABLED(TOUCH_UI_UTF8_COPYRIGHT) {UTF8('©'), 0 , COPYRIGHT_SIGN, 38 }, #endif #if ENABLED(TOUCH_UI_UTF8_ORDINALS) {UTF8('ª'), 0 , FEMININE_ORDINAL, 19 }, #endif #if ENABLED(TOUCH_UI_UTF8_PUNCTUATION) {UTF8('«'), 0 , LEFT_DBL_QUOTE, 23 }, #endif #if ENABLED(TOUCH_UI_UTF8_SYMBOLS) {UTF8('¬'), 0 , NOT_SIGN, 32 }, #endif #if ENABLED(TOUCH_UI_UTF8_COPYRIGHT) {UTF8('®'), 0 , REGISTERED_SIGN, 38 }, #endif {UTF8('°'), 0 , DOT_ABOVE, 24 }, #if ENABLED(TOUCH_UI_UTF8_MATHEMATICS) {UTF8('±'), 0 , NOT_SIGN, 32 }, #endif #if ENABLED(TOUCH_UI_UTF8_SUPERSCRIPTS) {UTF8('²'), 0 , SUPERSCRIPT_TWO, 16 }, {UTF8('³'), 0 , SUPERSCRIPT_THREE, 16 }, #endif #if ENABLED(TOUCH_UI_UTF8_SYMBOLS) {UTF8('µ'), 0 , MICRON_SIGN, 28 }, {UTF8('¶'), 0 , PILCROW_SIGN, 24 }, #endif #if ENABLED(TOUCH_UI_UTF8_SUPERSCRIPTS) {UTF8('¹'), 0 , SUPERSCRIPT_ONE, 16 }, #endif #if ENABLED(TOUCH_UI_UTF8_ORDINALS) {UTF8('º'), 0 , MASCULINE_ORDINAL, 19 }, #endif #if ENABLED(TOUCH_UI_UTF8_PUNCTUATION) {UTF8('»'), 0 , RIGHT_DBL_QUOTE, 24 }, #endif #if ENABLED(TOUCH_UI_UTF8_FRACTIONS) {UTF8('¼'), 0 , FRACTION_QUARTER, 40 }, {UTF8('½'), 0 , FRACTION_HALF, 40 }, {UTF8('¾'), 0 , FRACTION_THREE_FOURTHS, 40 }, #endif #if ENABLED(TOUCH_UI_UTF8_PUNCTUATION) {UTF8('¿'), 0 , INV_QUESTION, 21 }, #endif {UTF8('À'), 'A', GRAVE, mid_A}, {UTF8('Á'), 'A', ACUTE, mid_A}, {UTF8('Â'), 'A', CIRCUMFLEX, mid_A}, {UTF8('Ã'), 'A', TILDE, mid_A}, {UTF8('Ä'), 'A', DIAERESIS, mid_A}, {UTF8('Å'), 'A', DOT_ABOVE, mid_A}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('Æ'), 0 , LRG_AE, 40}, #endif {UTF8('Ç'), 'C', CEDILLA, mid_C}, {UTF8('È'), 'E', GRAVE, mid_E}, {UTF8('É'), 'E', ACUTE, mid_E}, {UTF8('Ê'), 'E', CIRCUMFLEX, mid_E}, {UTF8('Ë'), 'E', DIAERESIS, mid_E}, {UTF8('Ì'), 'I', GRAVE, mid_I}, {UTF8('Í'), 'I', ACUTE, mid_I}, {UTF8('Î'), 'I', CIRCUMFLEX, mid_I}, {UTF8('Ï'), 'I', DIAERESIS, mid_I}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('Ð'), 0, LRG_ETH, 31 }, #endif {UTF8('Ñ'), 'N', TILDE, mid_N}, {UTF8('Ò'), 'O', GRAVE, mid_O}, {UTF8('Ó'), 'O', ACUTE, mid_O}, {UTF8('Ô'), 'O', CIRCUMFLEX, mid_O}, {UTF8('Õ'), 'O', TILDE, mid_O}, {UTF8('Ö'), 'O', DIAERESIS, mid_O}, #if ENABLED(TOUCH_UI_UTF8_MATHEMATICS) {UTF8('×'), 0 , MULTIPLICATION_SIGN, 32 }, #endif #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('Ø'), 0 , LRG_O_STROKE, 32 }, #endif {UTF8('Ù'), 'U', GRAVE, mid_U}, {UTF8('Ú'), 'U', ACUTE, mid_U}, {UTF8('Û'), 'U', CIRCUMFLEX, mid_U}, {UTF8('Ü'), 'U', DIAERESIS, mid_U}, {UTF8('Ý'), 'Y', ACUTE, mid_Y}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('Þ'), 0 , LRG_THORN, 25 }, #endif #if ENABLED(TOUCH_UI_UTF8_GERMANIC) {UTF8('ß'), 0 , SHARP_S, 26 }, #endif {UTF8('à'), 'a', GRAVE, mid_a}, {UTF8('á'), 'a', ACUTE, mid_a}, {UTF8('â'), 'a', CIRCUMFLEX, mid_a}, {UTF8('ã'), 'a', TILDE, mid_a}, {UTF8('ä'), 'a', DIAERESIS, mid_a}, {UTF8('å'), 'a', DOT_ABOVE, mid_a}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('æ'), 0 , SML_AE, 40 }, #endif {UTF8('ç'), 'c', CEDILLA, mid_c}, {UTF8('è'), 'e', GRAVE, mid_e}, {UTF8('é'), 'e', ACUTE, mid_e}, {UTF8('ê'), 'e', CIRCUMFLEX, mid_e}, {UTF8('ë'), 'e', DIAERESIS, mid_e}, {UTF8('ì'), 'i', GRAVE, mid_i}, {UTF8('í'), 'i', ACUTE, mid_i}, {UTF8('î'), 'i', CIRCUMFLEX, mid_i}, {UTF8('ï'), 'i', DIAERESIS, mid_i}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('ð'), 0, SML_ETH, 24 }, #endif {UTF8('ñ'), 'n', TILDE, mid_n}, {UTF8('ò'), 'o', GRAVE, mid_o}, {UTF8('ó'), 'o', ACUTE, mid_o}, {UTF8('ô'), 'o', CIRCUMFLEX, mid_o}, {UTF8('õ'), 'o', TILDE, mid_o}, {UTF8('ö'), 'o', DIAERESIS, mid_o}, #if ENABLED(TOUCH_UI_UTF8_MATHEMATICS) {UTF8('÷'), 0 , DIVISION_SIGN, 32 }, #endif #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('ø'), 0 , SML_O_STROKE, 25 }, #endif {UTF8('ù'), 'u', GRAVE, mid_u}, {UTF8('ú'), 'u', ACUTE, mid_u}, {UTF8('û'), 'u', CIRCUMFLEX, mid_u}, {UTF8('ü'), 'u', DIAERESIS, mid_u}, {UTF8('ý'), 'y', ACUTE, mid_y}, #if ENABLED(TOUCH_UI_UTF8_SCANDINAVIAN) {UTF8('þ'), 0 , SML_THORN, 25 }, #endif {UTF8('ÿ'), 'y', DIAERESIS, mid_y} }; static_assert(UTF8('¡') == 0xC2A1, "Incorrect encoding for character"); /* Compile-time check that the table is in sorted order */ constexpr bool is_sorted(size_t n) { return n < 2 ? true : char_recipe[n-2].unicode < char_recipe[n-1].unicode && is_sorted(n-1); } static_assert(is_sorted(NUM_ELEMENTS(char_recipe)), "The table must be sorted by unicode value"); /* Performs a binary search to find a unicode character in the table */ static int8_t find_char_data(FTDI::utf8_char_t c) { int8_t min = 0, max = NUM_ELEMENTS(char_recipe), index; for (;;) { index = (min + max)/2; const uint16_t char_at = pgm_read_word(&char_recipe[index].unicode); if (char_at == c) break; if (min == max) return -1; if (c > char_at) min = index + 1; else max = index; } return index; } static void get_char_data(uint8_t index, uint8_t &std_char, uint8_t &alt_char, uint8_t &alt_data) { std_char = pgm_read_byte(&char_recipe[index].std_char); alt_char = pgm_read_byte(&char_recipe[index].alt_char); alt_data = pgm_read_byte(&char_recipe[index].alt_data); } /** * Load bitmap data into RAMG. This function is called once at the start * of the program. * * Parameters: * * addr - Address in RAMG where the font data is written */ void FTDI::WesternCharSet::load_data(uint32_t addr) { // Load the alternative font metrics CLCD::FontMetrics alt_fm; alt_fm.ptr = addr + 148; alt_fm.format = L4; alt_fm.stride = 19; alt_fm.width = 38; alt_fm.height = 49; LOOP_L_N(i, 127) alt_fm.char_widths[i] = 0; // For special characters, copy the character widths from the char tables LOOP_L_N(i, NUM_ELEMENTS(char_recipe)) { uint8_t std_char, alt_char, alt_data; get_char_data(i, std_char, alt_char, alt_data); if (std_char == 0) alt_fm.char_widths[alt_char] = alt_data; } CLCD::mem_write_bulk(addr, &alt_fm, 148); // Decode the RLE data and load it into RAMG as a bitmap write_rle_data(addr + 148, font, sizeof(font)); bitmap_addr = addr; } /** * Populates the bitmap handles for the custom into the display list. * This function is called once at the start of each display list. * * Parameters: * * cmd - Object used for writing to the FTDI chip command queue. */ void FTDI::WesternCharSet::load_bitmaps(CommandProcessor& cmd) { CLCD::FontMetrics alt_fm; alt_fm.ptr = bitmap_addr + 148; alt_fm.format = L4; alt_fm.stride = 19; alt_fm.width = 38; alt_fm.height = 49; set_font_bitmap(cmd, alt_fm, alt_font); } /** * Renders a character at location x and y. The x position is incremented * by the width of the character. * * Parameters: * * cmd - If non-NULL the symbol is drawn to the screen. * If NULL, only increment position for text measurement. * * x, y - The location at which to draw the character. On output, * incremented to the location of the next character. * * fs - A scaling object used to scale the font. The display will * already be configured to scale bitmaps, but positions * must be scaled using fs.scale() * * c - The unicode code point to draw. If the renderer does not * support the character, it should return false. * Returns: Whether the character was supported. */ bool FTDI::WesternCharSet::render_glyph(CommandProcessor* cmd, int &x, int &y, font_size_t fs, utf8_char_t c) { // A supported character? if (c < UTF8('¡') || c > UTF8('ÿ')) return false; int8_t index = find_char_data(c); if (index == -1) return false; // Determine character characteristics uint8_t std_char, alt_char, alt_data; get_char_data(index, std_char, alt_char, alt_data); bool base_special; uint8_t base_width; uint8_t base_char; uint8_t accent_char; int8_t accent_dx, accent_dy; if (std_char == 0) { // Special character, non-accented base_width = alt_data; base_special = true; base_char = alt_char; accent_char = 0; if (c == UTF8('°')) x -= fs.scale(deg_sign_leading); } else { // Regular character with accent: accent_dx = alt_data - mid_accent; accent_dy = isupper(std_char) ? -7 : 0; accent_char = alt_char; base_width = StandardCharSet::std_char_width(std_char); base_special = std_char == 'i'; base_char = base_special ? NO_DOT_I : std_char; } // If cmd != NULL, draw the glyph to the screen if (cmd) { ext_vertex2ii(*cmd, x, y, base_special ? alt_font : std_font, base_char); if (accent_char) ext_vertex2ii(*cmd, x + fs.scale(accent_dx), y + fs.scale(accent_dy), alt_font, accent_char); } // Increment X to the next character position x += fs.scale(base_width); return true; } #endif // FTDI_EXTENDED && TOUCH_UI_USE_UTF8 && TOUCH_UI_UTF8_WESTERN_CHARSET