From 12f9d2ec2cf3f6840b903125901c3bd394b7ebc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 4 Jan 2020 23:28:18 +0100 Subject: [PATCH] add utf8, some glyphs, and more font rendering options --- main/CMakeLists.txt | 6 +- main/app_main.c | 2 +- main/graphics/font.c | 145 +++++++++++++++++++++++ main/graphics/font.h | 24 ++++ main/{ => graphics}/nokia.c | 225 ++++++++++++++++-------------------- main/{ => graphics}/nokia.h | 16 ++- main/graphics/utf8.c | 116 +++++++++++++++++++ main/graphics/utf8.h | 65 +++++++++++ main/gui.c | 4 +- main/liquid/scene_car.c | 2 +- main/liquid/scene_root.c | 24 ++-- 11 files changed, 487 insertions(+), 142 deletions(-) create mode 100644 main/graphics/font.c create mode 100644 main/graphics/font.h rename main/{ => graphics}/nokia.c (68%) rename main/{ => graphics}/nokia.h (86%) create mode 100644 main/graphics/utf8.c create mode 100644 main/graphics/utf8.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1f75fa3..fdef521 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,13 +1,15 @@ set(COMPONENT_SRCS "app_main.c" - "nokia.c" "knob.c" "gui.c" "analog.c" "liquid/liquid.c" "liquid/scene_root.c" "liquid/scene_car.c" + "graphics/nokia.c" + "graphics/utf8.c" + "graphics/font.c" ) -set(COMPONENT_ADD_INCLUDEDIRS "liquid") +set(COMPONENT_ADD_INCLUDEDIRS "liquid" "graphics") register_component() diff --git a/main/app_main.c b/main/app_main.c index 6c3d6be..f2d0be8 100644 --- a/main/app_main.c +++ b/main/app_main.c @@ -11,7 +11,7 @@ #include "freertos/task.h" #include "esp_system.h" #include "esp_spi_flash.h" -#include "nokia.h" +#include "graphics/nokia.h" #include "knob.h" #include "gui.h" #include "analog.h" diff --git a/main/graphics/font.c b/main/graphics/font.c new file mode 100644 index 0000000..19841eb --- /dev/null +++ b/main/graphics/font.c @@ -0,0 +1,145 @@ +#include "font.h" +#include "utf8.h" +#include + +#define FONT_EXTRAS_START (0x7e - 0x20 + 1) + +const struct FontSymbol font[] = { + // ASCII from 0x20 to 0x7e are ordered at the beginning for quick access by index + {.symbol=" ", .graphic = {0x00, 0x00, 0x00, 0x00, 0x00}}, // 0x20 + {.symbol="!", .graphic = {0x00, 0x00, 0x5f, 0x00, 0x00}}, // 0x21 + {.symbol="\"", .graphic = {0x00, 0x07, 0x00, 0x07, 0x00}}, // 0x22 + {.symbol="#", .graphic = {0x14, 0x7f, 0x14, 0x7f, 0x14}}, // 0x23 + {.symbol="$", .graphic = {0x24, 0x2a, 0x7f, 0x2a, 0x12}}, // 0x24 + {.symbol="%", .graphic = {0x23, 0x13, 0x08, 0x64, 0x62}}, // 0x25 + {.symbol="&", .graphic = {0x36, 0x49, 0x55, 0x22, 0x50}}, // 0x26 + {.symbol="'", .graphic = {0x00, 0x05, 0x03, 0x00, 0x00}}, // 0x27 + {.symbol="(", .graphic = {0x00, 0x1c, 0x22, 0x41, 0x00}}, // 0x28 + {.symbol=")", .graphic = {0x00, 0x41, 0x22, 0x1c, 0x00}}, // 0x29 + {.symbol="*", .graphic = {0x14, 0x08, 0x3e, 0x08, 0x14}}, // 0x2a + {.symbol="+", .graphic = {0x08, 0x08, 0x3e, 0x08, 0x08}}, // 0x2b + {.symbol=",", .graphic = {0x00, 0x50, 0x30, 0x00, 0x00}}, // 0x2c + {.symbol="-", .graphic = {0x08, 0x08, 0x08, 0x08, 0x08}}, // 0x2d + {.symbol=".", .graphic = {0x00, 0x60, 0x60, 0x00, 0x00}}, // 0x2e + {.symbol="/", .graphic = {0x20, 0x10, 0x08, 0x04, 0x02}}, // 0x2f + {.symbol="0", .graphic = {0x3e, 0x51, 0x49, 0x45, 0x3e}}, // 0x30 + {.symbol="1", .graphic = {0x00, 0x42, 0x7f, 0x40, 0x00}}, // 0x31 + {.symbol="2", .graphic = {0x42, 0x61, 0x51, 0x49, 0x46}}, // 0x32 + {.symbol="3", .graphic = {0x21, 0x41, 0x45, 0x4b, 0x31}}, // 0x33 + {.symbol="4", .graphic = {0x18, 0x14, 0x12, 0x7f, 0x10}}, // 0x34 + {.symbol="5", .graphic = {0x27, 0x45, 0x45, 0x45, 0x39}}, // 0x35 + {.symbol="6", .graphic = {0x3c, 0x4a, 0x49, 0x49, 0x30}}, // 0x36 + {.symbol="7", .graphic = {0x01, 0x71, 0x09, 0x05, 0x03}}, // 0x37 + {.symbol="8", .graphic = {0x36, 0x49, 0x49, 0x49, 0x36}}, // 0x38 + {.symbol="9", .graphic = {0x06, 0x49, 0x49, 0x29, 0x1e}}, // 0x39 + {.symbol=":", .graphic = {0x00, 0x36, 0x36, 0x00, 0x00}}, // 0x3a + {.symbol=";", .graphic = {0x00, 0x56, 0x36, 0x00, 0x00}}, // 0x3b + {.symbol="<", .graphic = {0x08, 0x14, 0x22, 0x41, 0x00}}, // 0x3c + {.symbol="=", .graphic = {0x14, 0x14, 0x14, 0x14, 0x14}}, // 0x3d + {.symbol=">", .graphic = {0x00, 0x41, 0x22, 0x14, 0x08}}, // 0x3e + {.symbol="?", .graphic = {0x02, 0x01, 0x51, 0x09, 0x06}}, // 0x3f + {.symbol="@", .graphic = {0x32, 0x49, 0x79, 0x41, 0x3e}}, // 0x40 + {.symbol="A", .graphic = {0x7e, 0x11, 0x11, 0x11, 0x7e}}, // 0x41 + {.symbol="B", .graphic = {0x7f, 0x49, 0x49, 0x49, 0x36}}, // 0x42 + {.symbol="C", .graphic = {0x3e, 0x41, 0x41, 0x41, 0x22}}, // 0x43 + {.symbol="D", .graphic = {0x7f, 0x41, 0x41, 0x22, 0x1c}}, // 0x44 + {.symbol="E", .graphic = {0x7f, 0x49, 0x49, 0x49, 0x41}}, // 0x45 + {.symbol="F", .graphic = {0x7f, 0x09, 0x09, 0x09, 0x01}}, // 0x46 + {.symbol="G", .graphic = {0x3e, 0x41, 0x49, 0x49, 0x7a}}, // 0x47 + {.symbol="H", .graphic = {0x7f, 0x08, 0x08, 0x08, 0x7f}}, // 0x48 + {.symbol="I", .graphic = {0x00, 0x41, 0x7f, 0x41, 0x00}}, // 0x49 + {.symbol="J", .graphic = {0x20, 0x40, 0x41, 0x3f, 0x01}}, // 0x4a + {.symbol="K", .graphic = {0x7f, 0x08, 0x14, 0x22, 0x41}}, // 0x4b + {.symbol="L", .graphic = {0x7f, 0x40, 0x40, 0x40, 0x40}}, // 0x4c + {.symbol="M", .graphic = {0x7f, 0x02, 0x0c, 0x02, 0x7f}}, // 0x4d + {.symbol="N", .graphic = {0x7f, 0x04, 0x08, 0x10, 0x7f}}, // 0x4e + {.symbol="O", .graphic = {0x3e, 0x41, 0x41, 0x41, 0x3e}}, // 0x4f + {.symbol="P", .graphic = {0x7f, 0x09, 0x09, 0x09, 0x06}}, // 0x50 + {.symbol="Q", .graphic = {0x3e, 0x41, 0x51, 0x21, 0x5e}}, // 0x51 + {.symbol="R", .graphic = {0x7f, 0x09, 0x19, 0x29, 0x46}}, // 0x52 + {.symbol="S", .graphic = {0x46, 0x49, 0x49, 0x49, 0x31}}, // 0x53 + {.symbol="T", .graphic = {0x01, 0x01, 0x7f, 0x01, 0x01}}, // 0x54 + {.symbol="U", .graphic = {0x3f, 0x40, 0x40, 0x40, 0x3f}}, // 0x55 + {.symbol="V", .graphic = {0x1f, 0x20, 0x40, 0x20, 0x1f}}, // 0x56 + {.symbol="W", .graphic = {0x3f, 0x40, 0x38, 0x40, 0x3f}}, // 0x57 + {.symbol="X", .graphic = {0x63, 0x14, 0x08, 0x14, 0x63}}, // 0x58 + {.symbol="Y", .graphic = {0x07, 0x08, 0x70, 0x08, 0x07}}, // 0x59 + {.symbol="Z", .graphic = {0x61, 0x51, 0x49, 0x45, 0x43}}, // 0x5a + {.symbol="[", .graphic = {0x00, 0x7f, 0x41, 0x41, 0x00}}, // 0x5b + {.symbol="\\", .graphic = {0x02, 0x04, 0x08, 0x10, 0x20}}, // 0x5c + {.symbol="]", .graphic = {0x00, 0x41, 0x41, 0x7f, 0x00}}, // 0x5d + {.symbol="^", .graphic = {0x04, 0x02, 0x01, 0x02, 0x04}}, // 0x5e + {.symbol="_", .graphic = {0x40, 0x40, 0x40, 0x40, 0x40}}, // 0x5f + {.symbol="`", .graphic = {0x00, 0x01, 0x02, 0x04, 0x00}}, // 0x60 + {.symbol="a", .graphic = {0x20, 0x54, 0x54, 0x54, 0x78}}, // 0x61 + {.symbol="b", .graphic = {0x7f, 0x48, 0x44, 0x44, 0x38}}, // 0x62 + {.symbol="c", .graphic = {0x38, 0x44, 0x44, 0x44, 0x20}}, // 0x63 + {.symbol="d", .graphic = {0x38, 0x44, 0x44, 0x48, 0x7f}}, // 0x64 + {.symbol="e", .graphic = {0x38, 0x54, 0x54, 0x54, 0x18}}, // 0x65 + {.symbol="f", .graphic = {0x08, 0x7e, 0x09, 0x01, 0x02}}, // 0x66 + {.symbol="g", .graphic = {0x0c, 0x52, 0x52, 0x52, 0x3e}}, // 0x67 + {.symbol="h", .graphic = {0x7f, 0x08, 0x04, 0x04, 0x78}}, // 0x68 + {.symbol="i", .graphic = {0x00, 0x44, 0x7d, 0x40, 0x00}}, // 0x69 + {.symbol="j", .graphic = {0x20, 0x40, 0x44, 0x3d, 0x00}}, // 0x6a + {.symbol="k", .graphic = {0x7f, 0x10, 0x28, 0x44, 0x00}}, // 0x6b + {.symbol="l", .graphic = {0x00, 0x41, 0x7f, 0x40, 0x00}}, // 0x6c + {.symbol="m", .graphic = {0x7c, 0x04, 0x18, 0x04, 0x78}}, // 0x6d + {.symbol="n", .graphic = {0x7c, 0x08, 0x04, 0x04, 0x78}}, // 0x6e + {.symbol="o", .graphic = {0x38, 0x44, 0x44, 0x44, 0x38}}, // 0x6f + {.symbol="p", .graphic = {0x7c, 0x14, 0x14, 0x14, 0x08}}, // 0x70 + {.symbol="q", .graphic = {0x08, 0x14, 0x14, 0x18, 0x7c}}, // 0x71 + {.symbol="r", .graphic = {0x7c, 0x08, 0x04, 0x04, 0x08}}, // 0x72 + {.symbol="s", .graphic = {0x48, 0x54, 0x54, 0x54, 0x20}}, // 0x73 + {.symbol="t", .graphic = {0x04, 0x3f, 0x44, 0x40, 0x20}}, // 0x74 + {.symbol="u", .graphic = {0x3c, 0x40, 0x40, 0x20, 0x7c}}, // 0x75 + {.symbol="v", .graphic = {0x1c, 0x20, 0x40, 0x20, 0x1c}}, // 0x76 + {.symbol="w", .graphic = {0x3c, 0x40, 0x30, 0x40, 0x3c}}, // 0x77 + {.symbol="x", .graphic = {0x44, 0x28, 0x10, 0x28, 0x44}}, // 0x78 + {.symbol="y", .graphic = {0x0c, 0x50, 0x50, 0x50, 0x3c}}, // 0x79 + {.symbol="z", .graphic = {0x44, 0x64, 0x54, 0x4c, 0x44}}, // 0x7a + {.symbol="{", .graphic = {0x00, 0x08, 0x36, 0x41, 0x00}}, // 0x7b + {.symbol="|", .graphic = {0x00, 0x00, 0x7f, 0x00, 0x00}}, // 0x7c + {.symbol="}", .graphic = {0x00, 0x41, 0x36, 0x08, 0x00}}, // 0x7d + {.symbol="~", .graphic = {0x10, 0x08, 0x08, 0x10, 0x08}}, // 0x7e + + // start of UTF8 glyphs. These can include custom graphics mapped to codepoints + {.symbol="�", .graphic = {0xFF, 0x81, 0x81, 0x81, 0xFF}}, // box + + {.symbol="×", .graphic = { 0x22, 0x14, 0x08, 0x14, 0x22 }}, // cross + {.symbol="↑", .graphic = { 0x08, 0x04, 0x3e, 0x04, 0x08 }}, // arrow_up + {.symbol="↓", .graphic = { 0x08, 0x10, 0x3e, 0x10, 0x08 }}, // arrow_down + {.symbol="←", .graphic = { 0x08, 0x1c, 0x2a, 0x08, 0x08 }}, // arrow_left + {.symbol="→", .graphic = { 0x08, 0x08, 0x2a, 0x1c, 0x08 }}, // arrow_right + {.symbol="⏰", .graphic = { 0x1c, 0x22, 0x2e, 0x2a, 0x1c }}, // clock + {.symbol="⌛", .graphic = { 0x63, 0x55, 0x4d, 0x55, 0x63 }}, // hourglass + {.symbol="☸", .graphic = { 0x1c, 0x22, 0x2a, 0x22, 0x1c }}, // wheel + {.symbol="⏎", .graphic = { 0x10, 0x38, 0x54, 0x10, 0x1e }}, // return + {.symbol="🌡", .graphic = { 0x60, 0x9e, 0x81, 0x9e, 0x6a }}, // thermometer + {.symbol="°", .graphic = { 0x00, 0x07, 0x05, 0x07, 0x00 }}, // degree + {.symbol="🔙", .graphic = { 0x04, 0x4e, 0x55, 0x44, 0x38 }}, // back + {.symbol="▶", .graphic = { 0x7f, 0x3e, 0x1c, 0x08, 0x00 }}, // tri_right + {.symbol="◀", .graphic = { 0x00, 0x08, 0x1c, 0x3e, 0x7f }}, // tri_left + + // End of the list + {}, +}; + +const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch) { + if (ch.bytes[0] < ' ') { + return &font[FONT_EXTRAS_START]; // replacement character + } + + if (ch.bytes[0] < 127) { + return &font[ch.bytes[0] - 0x20]; + } + + const struct FontSymbol *sym = &font[FONT_EXTRAS_START]; + while (sym->symbol[0]) { + if (sym->uint == ch.uint) { + return sym; + } + sym++; + } + + return &font[FONT_EXTRAS_START]; // replacement character +} diff --git a/main/graphics/font.h b/main/graphics/font.h new file mode 100644 index 0000000..b7440e5 --- /dev/null +++ b/main/graphics/font.h @@ -0,0 +1,24 @@ +/** + * TODO file description + * + * Created on 2020/01/04. + */ + +#ifndef REFLOWER_FONT_H +#define REFLOWER_FONT_H + +#include "font.h" +#include "utf8.h" +#include + +struct FontSymbol { + union { + const char symbol[4]; + const uint32_t uint; + }; + uint8_t graphic[5]; +}; + +const struct FontSymbol *Font_GetSymbol(struct Utf8Char ch); + +#endif //REFLOWER_FONT_H diff --git a/main/nokia.c b/main/graphics/nokia.c similarity index 68% rename from main/nokia.c rename to main/graphics/nokia.c index fcbaf82..96cddb6 100644 --- a/main/nokia.c +++ b/main/graphics/nokia.c @@ -2,6 +2,8 @@ #include #include #include "nokia.h" +#include "utf8.h" +#include "font.h" #include /* Pin definitions: @@ -22,112 +24,6 @@ static const int sclkPin = 13; // SCLK - Serial clock, pin 7 on LCD. static spi_device_handle_t hSPI; -/* Font table: -This table contains the hex values that represent pixels for a -font that is 5 pixels wide and 8 pixels high. Each byte in a row -represents one, 8-pixel, vertical column of a character. 5 bytes -per character. */ -static const uint8_t ASCII[][5] = { - // First 32 characters (0x00-0x19) are ignored. These are - // non-displayable, control characters. - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x20 - {0x00, 0x00, 0x5f, 0x00, 0x00}, // 0x21 ! - {0x00, 0x07, 0x00, 0x07, 0x00}, // 0x22 " - {0x14, 0x7f, 0x14, 0x7f, 0x14}, // 0x23 # - {0x24, 0x2a, 0x7f, 0x2a, 0x12}, // 0x24 $ - {0x23, 0x13, 0x08, 0x64, 0x62}, // 0x25 % - {0x36, 0x49, 0x55, 0x22, 0x50}, // 0x26 & - {0x00, 0x05, 0x03, 0x00, 0x00}, // 0x27 ' - {0x00, 0x1c, 0x22, 0x41, 0x00}, // 0x28 ( - {0x00, 0x41, 0x22, 0x1c, 0x00}, // 0x29 ) - {0x14, 0x08, 0x3e, 0x08, 0x14}, // 0x2a * - {0x08, 0x08, 0x3e, 0x08, 0x08}, // 0x2b + - {0x00, 0x50, 0x30, 0x00, 0x00}, // 0x2c , - {0x08, 0x08, 0x08, 0x08, 0x08}, // 0x2d - - {0x00, 0x60, 0x60, 0x00, 0x00}, // 0x2e . - {0x20, 0x10, 0x08, 0x04, 0x02}, // 0x2f / - {0x3e, 0x51, 0x49, 0x45, 0x3e}, // 0x30 0 - {0x00, 0x42, 0x7f, 0x40, 0x00}, // 0x31 1 - {0x42, 0x61, 0x51, 0x49, 0x46}, // 0x32 2 - {0x21, 0x41, 0x45, 0x4b, 0x31}, // 0x33 3 - {0x18, 0x14, 0x12, 0x7f, 0x10}, // 0x34 4 - {0x27, 0x45, 0x45, 0x45, 0x39}, // 0x35 5 - {0x3c, 0x4a, 0x49, 0x49, 0x30}, // 0x36 6 - {0x01, 0x71, 0x09, 0x05, 0x03}, // 0x37 7 - {0x36, 0x49, 0x49, 0x49, 0x36}, // 0x38 8 - {0x06, 0x49, 0x49, 0x29, 0x1e}, // 0x39 9 - {0x00, 0x36, 0x36, 0x00, 0x00}, // 0x3a : - {0x00, 0x56, 0x36, 0x00, 0x00}, // 0x3b ; - {0x08, 0x14, 0x22, 0x41, 0x00}, // 0x3c < - {0x14, 0x14, 0x14, 0x14, 0x14}, // 0x3d = - {0x00, 0x41, 0x22, 0x14, 0x08}, // 0x3e > - {0x02, 0x01, 0x51, 0x09, 0x06}, // 0x3f ? - {0x32, 0x49, 0x79, 0x41, 0x3e}, // 0x40 @ - {0x7e, 0x11, 0x11, 0x11, 0x7e}, // 0x41 A - {0x7f, 0x49, 0x49, 0x49, 0x36}, // 0x42 B - {0x3e, 0x41, 0x41, 0x41, 0x22}, // 0x43 C - {0x7f, 0x41, 0x41, 0x22, 0x1c}, // 0x44 D - {0x7f, 0x49, 0x49, 0x49, 0x41}, // 0x45 E - {0x7f, 0x09, 0x09, 0x09, 0x01}, // 0x46 F - {0x3e, 0x41, 0x49, 0x49, 0x7a}, // 0x47 G - {0x7f, 0x08, 0x08, 0x08, 0x7f}, // 0x48 H - {0x00, 0x41, 0x7f, 0x41, 0x00}, // 0x49 I - {0x20, 0x40, 0x41, 0x3f, 0x01}, // 0x4a J - {0x7f, 0x08, 0x14, 0x22, 0x41}, // 0x4b K - {0x7f, 0x40, 0x40, 0x40, 0x40}, // 0x4c L - {0x7f, 0x02, 0x0c, 0x02, 0x7f}, // 0x4d M - {0x7f, 0x04, 0x08, 0x10, 0x7f}, // 0x4e N - {0x3e, 0x41, 0x41, 0x41, 0x3e}, // 0x4f O - {0x7f, 0x09, 0x09, 0x09, 0x06}, // 0x50 P - {0x3e, 0x41, 0x51, 0x21, 0x5e}, // 0x51 Q - {0x7f, 0x09, 0x19, 0x29, 0x46}, // 0x52 R - {0x46, 0x49, 0x49, 0x49, 0x31}, // 0x53 S - {0x01, 0x01, 0x7f, 0x01, 0x01}, // 0x54 T - {0x3f, 0x40, 0x40, 0x40, 0x3f}, // 0x55 U - {0x1f, 0x20, 0x40, 0x20, 0x1f}, // 0x56 V - {0x3f, 0x40, 0x38, 0x40, 0x3f}, // 0x57 W - {0x63, 0x14, 0x08, 0x14, 0x63}, // 0x58 X - {0x07, 0x08, 0x70, 0x08, 0x07}, // 0x59 Y - {0x61, 0x51, 0x49, 0x45, 0x43}, // 0x5a Z - {0x00, 0x7f, 0x41, 0x41, 0x00}, // 0x5b [ - {0x02, 0x04, 0x08, 0x10, 0x20}, // 0x5c \ (keep this to escape the backslash) - {0x00, 0x41, 0x41, 0x7f, 0x00}, // 0x5d ] - {0x04, 0x02, 0x01, 0x02, 0x04}, // 0x5e ^ - {0x40, 0x40, 0x40, 0x40, 0x40}, // 0x5f _ - {0x00, 0x01, 0x02, 0x04, 0x00}, // 0x60 ` - {0x20, 0x54, 0x54, 0x54, 0x78}, // 0x61 a - {0x7f, 0x48, 0x44, 0x44, 0x38}, // 0x62 b - {0x38, 0x44, 0x44, 0x44, 0x20}, // 0x63 c - {0x38, 0x44, 0x44, 0x48, 0x7f}, // 0x64 d - {0x38, 0x54, 0x54, 0x54, 0x18}, // 0x65 e - {0x08, 0x7e, 0x09, 0x01, 0x02}, // 0x66 f - {0x0c, 0x52, 0x52, 0x52, 0x3e}, // 0x67 g - {0x7f, 0x08, 0x04, 0x04, 0x78}, // 0x68 h - {0x00, 0x44, 0x7d, 0x40, 0x00}, // 0x69 i - {0x20, 0x40, 0x44, 0x3d, 0x00}, // 0x6a j - {0x7f, 0x10, 0x28, 0x44, 0x00}, // 0x6b k - {0x00, 0x41, 0x7f, 0x40, 0x00}, // 0x6c l - {0x7c, 0x04, 0x18, 0x04, 0x78}, // 0x6d m - {0x7c, 0x08, 0x04, 0x04, 0x78}, // 0x6e n - {0x38, 0x44, 0x44, 0x44, 0x38}, // 0x6f o - {0x7c, 0x14, 0x14, 0x14, 0x08}, // 0x70 p - {0x08, 0x14, 0x14, 0x18, 0x7c}, // 0x71 q - {0x7c, 0x08, 0x04, 0x04, 0x08}, // 0x72 r - {0x48, 0x54, 0x54, 0x54, 0x20}, // 0x73 s - {0x04, 0x3f, 0x44, 0x40, 0x20}, // 0x74 t - {0x3c, 0x40, 0x40, 0x20, 0x7c}, // 0x75 u - {0x1c, 0x20, 0x40, 0x20, 0x1c}, // 0x76 v - {0x3c, 0x40, 0x30, 0x40, 0x3c}, // 0x77 w - {0x44, 0x28, 0x10, 0x28, 0x44}, // 0x78 x - {0x0c, 0x50, 0x50, 0x50, 0x3c}, // 0x79 y - {0x44, 0x64, 0x54, 0x4c, 0x44}, // 0x7a z - {0x00, 0x08, 0x36, 0x41, 0x00}, // 0x7b { - {0x00, 0x00, 0x7f, 0x00, 0x00}, // 0x7c | - {0x00, 0x41, 0x36, 0x08, 0x00}, // 0x7d } - {0x10, 0x08, 0x08, 0x10, 0x08}, // 0x7e ~ - {0x78, 0x46, 0x41, 0x46, 0x78}, // 0x7f DEL -}; - /* The displayMap variable stores a buffer representation of the pixels on our display. There are 504 total bits in this array, same as how many pixels there are on a 84 x 48 display. @@ -325,18 +221,52 @@ void LCD_setCircle(int x0, int y0, int radius, bool bw, int lineThickness) // This function will draw a char (defined in the ASCII table // near the beginning of this sketch) at a defined x and y). // The color can be either black (1) or white (0). -void LCD_setChar(char character, int x, int y, bool bw) +void LCD_setChar(struct Utf8Char character, int x, int y, bool bw) +{ + LCD_setCharEx(character, x, y, bw, 1); +} + +void LCD_setCharEx(struct Utf8Char character, int x, int y, bool bw, uint8_t size) { + const struct FontSymbol *symbol = Font_GetSymbol(character); + bool backfill = size &0x80; + size &= 0x7F; + uint8_t column; // temp byte to store character's column bitmap for (int i = 0; i < 5; i++) // 5 columns (x) per character { - column = ASCII[character - 0x20][i]; + column = symbol->graphic[i]; for (int j = 0; j < 8; j++) // 8 rows (y) per character { - if (column & (0x01 << j)) // test bits to set pixels - LCD_setPixel(x + i, y + j, bw); - else - LCD_setPixel(x + i, y + j, !bw); + bool bit = column & (0x01 << j); + + if (size == 1) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i, y + j, bw); + } else if(backfill) { + LCD_setPixel(x + i, y + j, !bw); + } + } else if (size == 2) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i * 2, y + j * 2, bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2, bw); + LCD_setPixel(x + i * 2, y + j * 2 + 1, bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, bw); + } else if(backfill) { + LCD_setPixel(x + i * 2, y + j * 2, !bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2, !bw); + LCD_setPixel(x + i * 2, y + j * 2 + 1, !bw); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, !bw); + } + } else if (size == 3) { + if (bit) {// test bits to set pixels + LCD_setPixel(x + i, y + j, bw); + LCD_setPixel(x + i + 1, y + j, bw); + } else if(backfill) { + LCD_setPixel(x + i, y + j, !bw); + LCD_setPixel(x + i + 1, y + j, !bw); + } + } } } } @@ -347,18 +277,65 @@ void LCD_setChar(char character, int x, int y, bool bw) // library. void LCD_setStr(const char *dString, int x, int y, bool bw) { - while (*dString != 0x00) // loop until null terminator - { - LCD_setChar(*dString++, x, y, bw); - x += 5; - for (int i = y; i < y + 8; i++) { - LCD_setPixel(x, i, !bw); - } - x++; - if (x > (LCD_WIDTH - 5)) // Enables wrap around - { - x = 0; - y += 8; + LCD_setStrEx(dString, x, y, bw, 1); +} + +// setStr draws a string of characters, calling setChar with +// progressive coordinates until it's done. +// This function was grabbed from the SparkFun ColorLCDShield +// library. +void LCD_setStrEx(const char *dString, int x, int y, bool bw, uint8_t size) +{ + struct Utf8Iterator iter; + Utf8Iterator_Start(&iter, dString); + bool backfill = size & 0x80; + uint8_t size_real = size & 0x7F; + + struct Utf8Char uchar; + while ((uchar = Utf8Iterator_Next(&iter)).uint) { + LCD_setCharEx(uchar, x, y, bw, size); + + if (size_real == 1) { + x += 5; + if (backfill) { + for (int i = y; i < y + 8; i++) { + LCD_setPixel(x, i, !bw); + } + } + x++; + if (x > (LCD_WIDTH - 5)) // Enables wrap around + { + x = 0; + y += 8; + } + } else if (size_real == 2) { + x += 10; + if (backfill) { + for (int i = y; i < y + 16; i++) { + LCD_setPixel(x, i, !bw); + LCD_setPixel(x + 1, i, !bw); + } + } + x+=2; + if (x > (LCD_WIDTH - 10)) // Enables wrap around + { + x = 0; + y += 16; + } + } else if (size_real == 3) { + x += 6; + if (backfill) { + for (int i = y; i < y + 8; i++) { + LCD_setPixel(x, i, !bw); + LCD_setPixel(x + 1, i, !bw); + } + } + x+=1; + if (x > (LCD_WIDTH - 6)) // Enables wrap around + { + x = 0; + y += 8; + } } } } diff --git a/main/nokia.h b/main/graphics/nokia.h similarity index 86% rename from main/nokia.h rename to main/graphics/nokia.h index f412f00..acf160f 100644 --- a/main/nokia.h +++ b/main/graphics/nokia.h @@ -12,6 +12,7 @@ LED (backlight) pin should remain on a PWM-capable pin. */ /* 84x48 LCD Defines: */ #include #include +#include "utf8.h" #define LCD_WIDTH 84 // Note: x-coordinates go wide #define LCD_HEIGHT 48 // Note: y-coordinates go high @@ -35,10 +36,21 @@ void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, bool bw); // thickness ranging from 1 to the radius of the circle. void LCD_setCircle (int x0, int y0, int radius, bool bw, int lineThickness); +/* + FONT FUNCTIONS + size = 1 ... normal + size = 2 ... 2x + size = 3 ... normal bold + size | 0x80 ... clear background behind characters +*/ + + // This function will draw a char (defined in the ASCII table // near the beginning of this sketch) at a defined x and y). // The color can be either black (1) or white (0). -void LCD_setChar(char character, int x, int y, bool bw); +void LCD_setChar(struct Utf8Char character, int x, int y, bool bw); + +void LCD_setCharEx(struct Utf8Char character, int x, int y, bool bw, uint8_t size); // setStr draws a string of characters, calling setChar with // progressive coordinates until it's done. @@ -46,6 +58,8 @@ void LCD_setChar(char character, int x, int y, bool bw); // library. void LCD_setStr(const char * dString, int x, int y, bool bw); +void LCD_setStrEx(const char *dString, int x, int y, bool bw, uint8_t size); + // This function clears the entire display either white (0) or // black (1). // The screen won't actually clear until you call updateDisplay()! diff --git a/main/graphics/utf8.c b/main/graphics/utf8.c new file mode 100644 index 0000000..d6fbe97 --- /dev/null +++ b/main/graphics/utf8.c @@ -0,0 +1,116 @@ +#include +#include "utf8.h" +#include + +// +// Created by MightyPork on 2017/08/20. +// +// UTF-8 parser - collects bytes of a code point before writing them +// into a screen cell. +// + +const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) { .uint = 0 }; + +// Code Points First Byte Second Byte Third Byte Fourth Byte +// U+0000 - U+007F 00 - 7F +// U+0080 - U+07FF C2 - DF 80 - BF +// U+0800 - U+0FFF E0 *A0 - BF 80 - BF +// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF +// U+D000 - U+D7FF ED 80 - *9F 80 - BF +// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF +// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF +// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF +// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF + + +/** + * Handle a received character + */ +struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c) +{ + uint8_t *bytes = self->buffer.bytes; + + uint8_t uc = (uint8_t)c; + // collecting unicode glyphs... + if (uc & 0x80) { + if (self->utf_len == 0) { + bytes[0] = uc; + self->utf_j = 1; + + // start + if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) { + // forbidden start codes + goto fail; + } + + if ((uc & 0xE0) == 0xC0) { + self->utf_len = 2; + } + else if ((uc & 0xF0) == 0xE0) { + self->utf_len = 3; + } + else if ((uc & 0xF8) == 0xF0) { + self->utf_len = 4; + } + else { + // chars over 127 that don't start unicode sequences + goto fail; + } + } + else { + if ((uc & 0xC0) != 0x80) { + bytes[self->utf_j++] = uc; + goto fail; + } + else { + bytes[self->utf_j++] = uc; + if (self->utf_j >= self->utf_len) { + // check for bad sequences - overlong or some other problem + if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; + if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail; + if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail; + if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail; + + // trap for surrogates - those break javascript + if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail; + + goto success; + } + } + } + } + else { + bytes[0] = uc; + goto success; + } + + return EMPTY_CHAR; + +success:; + struct Utf8Char result = self->buffer; + self->buffer.uint = 0; // erase the buffer + self->utf_len = 0; + return result; + +fail: + self->buffer.uint = 0; // erase the buffer + self->utf_len = 0; + return EMPTY_CHAR; +} + +void Utf8Iterator_Start(struct Utf8Iterator *self, const char *source) { + memset(self, 0, sizeof(struct Utf8Iterator)); + self->source = source; +} + +struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self) { + char c; + struct Utf8Char uchar; + while ((c = *self->source++) != 0) { + uchar = Utf8Parser_Handle(&self->parser, c); + if (uchar.uint) { + return uchar; + } + } + return EMPTY_CHAR; +} diff --git a/main/graphics/utf8.h b/main/graphics/utf8.h new file mode 100644 index 0000000..89b8467 --- /dev/null +++ b/main/graphics/utf8.h @@ -0,0 +1,65 @@ +/** + * TODO file description + * + * Created on 2020/01/04. + */ + +#ifndef LIQUIDTYPE_UTF8_H +#define LIQUIDTYPE_UTF8_H + +#include +#include + +struct Utf8Char { + union { + uint8_t bytes[4]; + uint32_t uint; + }; +}; + +struct Utf8Parser { + struct Utf8Char buffer; + uint8_t utf_len; + uint8_t utf_j; +}; + +/** + * Utf8 character iterator. + * + * Usage: + * struct Utf8Iterator iter; + * Utf8Iterator_Start(&iter, myString); + * + * union Utf8Char uchar; + * while ((uchar = Utf8Iterator_Next(&iter)).uint) { + * // do something with the char + * } + * + * // Free myString if needed. + * // The iterator does not need any cleanup if it lives on stack. + */ +struct Utf8Iterator { + /* Characters to parse. The pointer is advanced as the iterator progresses. */ + const char *source; + struct Utf8Parser parser; +}; + +void Utf8Iterator_Start(struct Utf8Iterator *self, const char *source); + +/** + * Get the next character from the iterator; Returns empty character if there are no more characters to parse. + * + * Invalid characters are skipped. + */ +struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self); + +/** + * Parse a character. + * + * The returned struct contains NIL (uint == 0) if no character is yet available. + * + * ASCII is passed through, utf-8 is collected and returned in one piece. + */ +struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c); + +#endif //LIQUIDTYPE_UTF8_H diff --git a/main/gui.c b/main/gui.c index dba9008..0ce85be 100644 --- a/main/gui.c +++ b/main/gui.c @@ -1,5 +1,5 @@ #include "gui.h" -#include "nokia.h" +#include "graphics/nokia.h" #include "analog.h" #include #include @@ -17,7 +17,7 @@ void gui_init() { printf("GUI init\n"); LCD_setup(); - LCD_setContrast(60); + LCD_setContrast(48); LCD_clearDisplay(0); LCD_setStr("Hello World", 0, 0, 1); diff --git a/main/liquid/scene_car.c b/main/liquid/scene_car.c index 4a5023c..033308a 100644 --- a/main/liquid/scene_car.c +++ b/main/liquid/scene_car.c @@ -1,6 +1,6 @@ #include "scenes.h" #include "liquid.h" -#include "../nokia.h" +#include "../graphics/nokia.h" #include struct private { diff --git a/main/liquid/scene_root.c b/main/liquid/scene_root.c index 27bae04..cf7b520 100644 --- a/main/liquid/scene_root.c +++ b/main/liquid/scene_root.c @@ -1,6 +1,6 @@ #include "scenes.h" #include "liquid.h" -#include "../nokia.h" +#include "../graphics/nokia.h" #include "../analog.h" #include #include @@ -51,27 +51,29 @@ static void Root_paint(struct Scene *scene) const char *header = "???"; switch (priv->timer_phase) { case 0: - header = "ICE"; + header = "Drink water"; break; case 1: - header = " COLD"; + header = " Drink water"; break; case 2: - header = "COCA"; + header = " Drink water"; break; case 3: - header = " COLA"; + header = " Drink water"; break; } - LCD_setStr(header, 20, 3, 1); + LCD_setStrEx(header, 0, 3, 1, 3); - LCD_setRect(0, 15, 83, 35, 1, 1); + LCD_setRect(0, 12, 83, 33, 1, 1); char buf[10]; - sprintf(buf, "%3d", priv->pos); - LCD_setStr(buf, 2, 17, 0); - sprintf(buf, "%.0f C", analog_read()); - LCD_setStr(buf, 2, 26, 0); +// sprintf(buf, "%3d", priv->pos); +// LCD_setStr(buf, 2, 15, 0); + sprintf(buf, "🌡%.0f°C", analog_read()); + LCD_setStrEx(buf, 2, 15, 0, 2); + + LCD_setStr("×↑↓←→⏰⌛☸⏎🌡°🔙▶◀", 2, 33, 1); LCD_updateDisplay(); }