From 17a228faa7dbd4b5b2fe96ffcd19e09186b939be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 11 Jan 2020 22:37:32 +0100 Subject: [PATCH] improve font drawing api --- main/graphics/drawing.c | 148 ++++++++++++++++++------------------- main/graphics/drawing.h | 26 ++++--- main/liquid/gui.c | 2 +- main/scenes/scene_demo.c | 8 +- main/scenes/scene_menu.c | 23 +++++- main/scenes/scene_menu.h | 2 + main/scenes/scene_number.c | 4 +- main/scenes/scene_root.c | 2 +- 8 files changed, 119 insertions(+), 96 deletions(-) diff --git a/main/graphics/drawing.c b/main/graphics/drawing.c index 5e70825..67e6f30 100644 --- a/main/graphics/drawing.c +++ b/main/graphics/drawing.c @@ -150,19 +150,9 @@ void LCD_setCircle(int x0, int y0, int radius, enum Color 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(struct Utf8Char character, int x, int y, enum Color bw) -{ - LCD_setCharEx(character, x, y, bw, 1); -} - -void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8_t style) +static void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color color, struct TextStyle style) { const struct FontGraphic *symbol = Font_GetSymbol(character); - bool bg = style & 0x80; - enum FontStyle style_real = style & 0x7F; uint8_t column; // temp byte to store character's column bitmap for (int i = 0; i < 5; i++) // 5 columns (x) per character @@ -172,36 +162,36 @@ void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8 { bool bit = column & (1 << j); - if (style_real == FONT_NORMAL) { + if (style.size == FONT_NORMAL) { if (bit) {// test bits to set pixels - LCD_setPixel(x + i, y + j, bw); + LCD_setPixel(x + i, y + j, color); } - else if (bg) { - LCD_setPixel(x + i, y + j, !bw); + else if (style.bg) { + LCD_setPixel(x + i, y + j, !color); } } - else if (style_real == FONT_DOUBLE) { + else if (style.size == FONT_DOUBLE) { 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); + LCD_setPixel(x + i * 2, y + j * 2, color); + LCD_setPixel(x + i * 2 + 1, y + j * 2, color); + LCD_setPixel(x + i * 2, y + j * 2 + 1, color); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, color); } - else if (bg) { - 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 (style.bg) { + LCD_setPixel(x + i * 2, y + j * 2, !color); + LCD_setPixel(x + i * 2 + 1, y + j * 2, !color); + LCD_setPixel(x + i * 2, y + j * 2 + 1, !color); + LCD_setPixel(x + i * 2 + 1, y + j * 2 + 1, !color); } } - else if (style_real == FONT_BOLD) { + else if (style.size == FONT_BOLD) { if (bit) {// test bits to set pixels - LCD_setPixel(x + i, y + j, bw); - LCD_setPixel(x + i + 1, y + j, bw); + LCD_setPixel(x + i, y + j, color); + LCD_setPixel(x + i + 1, y + j, color); } - else if (bg) { - LCD_setPixel(x + i, y + j, !bw); - LCD_setPixel(x + i + 1, y + j, !bw); + else if (style.bg) { + LCD_setPixel(x + i, y + j, !color); + LCD_setPixel(x + i + 1, y + j, !color); } } } @@ -214,67 +204,77 @@ void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8 // library. void LCD_setStr(const char *dString, int x, int y, enum Color bw) { - LCD_setStrEx(dString, x, y, bw, 1); + LCD_setStrEx(dString, x, y, bw, (struct TextStyle) {}); } +static const int char_widths[] = { + [FONT_NORMAL] = 5, + [FONT_DOUBLE] = 10, + [FONT_BOLD] = 6 +}; + +static const int char_spacings[] = { + [FONT_NORMAL] = 1, + [FONT_DOUBLE] = 2, + [FONT_BOLD] = 1 +}; + +static const int char_heights[] = { + [FONT_NORMAL] = 8, + [FONT_DOUBLE] = 16, + [FONT_BOLD] = 8 +}; + // 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, enum Color bw, uint8_t style) +// library. AND HEAVILY MODIFIED +void LCD_setStrEx(const char *dString, int x, int y, enum Color color, struct TextStyle style) { + const int x0 = x; struct Utf8Iterator iter; Utf8Iterator_Init(&iter, dString); - bool bg = style & 0x80; - enum FontStyle style_real = style & 0x7F; + + int charw = char_widths[style.size]; + int charh = char_heights[style.size]; + + int spacingx = char_spacings[style.size] + style.spacing_x; + int spacingy = style.spacing_y; struct Utf8Char uchar; + int limit = style.max_chars; while ((uchar = Utf8Iterator_Next(&iter)).uint) { - LCD_setCharEx(uchar, x, y, bw, style); + LCD_setCharEx(uchar, x, y, color, style); - if (style_real == FONT_NORMAL) { - x += 5; - if (bg) { - 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; - } + if (limit > 0) { + limit -= 1; + if (limit == 0) return; } - else if (style_real == FONT_DOUBLE) { - x += 10; - if (bg) { - for (int i = y; i < y + 16; i++) { - LCD_setPixel(x, i, !bw); - LCD_setPixel(x + 1, i, !bw); + + x += charw; + + if (style.bg) { + for (int i = y; i < y + charh; i++) { + for (int j = x; j < x + spacingx; j++) { + LCD_setPixel(j, i, !color); } } - x += 2; - if (x > (LCD_WIDTH - 10)) // Enables wrap around - { - x = 0; - y += 16; - } } - else if (style_real == FONT_BOLD) { - x += 6; - if (bg) { - 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 += spacingx; + if (x > (LCD_WIDTH - charw)) { + if (style.nowrap) break; + if (style.wrap_to_0) { x = 0; - y += 8; + } else { + x = x0; } + y += charh + spacingy; + } + + // no more characters could be seen + if (y >= LCD_HEIGHT) { + break; } } } diff --git a/main/graphics/drawing.h b/main/graphics/drawing.h index 155486a..5f49767 100644 --- a/main/graphics/drawing.h +++ b/main/graphics/drawing.h @@ -44,19 +44,21 @@ void LCD_setBitmap(const uint8_t *bitArray); size | 0x80 ... clear background behind characters */ -enum FontStyle { - FONT_NORMAL = 1, - FONT_DOUBLE = 2, - FONT_BOLD = 3, - FONT_BG = 0x80, +enum TextSize { + FONT_NORMAL = 0, + FONT_DOUBLE = 1, + FONT_BOLD = 2, }; -// 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(struct Utf8Char character, int x, int y, enum Color bw); - -void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8_t style); +struct TextStyle { + bool bg; //!< Fill the characters background with the opposite color + enum TextSize size; //!< Character size + int max_chars; //!< Number of characters to print from the string, if > 0 + int spacing_x; //!< Additional X spacing + int spacing_y; //!< Additional Y spacing + bool nowrap; //!< Stop painting when the right edge of the screen is reached + bool wrap_to_0; //!< wrap to 0 instead of the initial X coordinate +}; // setStr draws a string of characters, calling setChar with // progressive coordinates until it's done. @@ -64,7 +66,7 @@ void LCD_setCharEx(struct Utf8Char character, int x, int y, enum Color bw, uint8 // library. void LCD_setStr(const char * dString, int x, int y, enum Color bw); -void LCD_setStrEx(const char *dString, int x, int y, enum Color bw, uint8_t style); +void LCD_setStrEx(const char *dString, int x, int y, enum Color color, struct TextStyle style); // This function clears the entire display either white (0) or // black (1). diff --git a/main/liquid/gui.c b/main/liquid/gui.c index 63573ac..c114b7b 100644 --- a/main/liquid/gui.c +++ b/main/liquid/gui.c @@ -16,7 +16,7 @@ static TimerHandle_t hTicker = NULL; void gui_init() { printf("GUI init\n"); LCD_setup(); - LCD_setContrast(48); + LCD_setContrast(57); int rv = xTaskCreate(gui_thread, "gui", 4096, NULL, 6, &hGuiThread); assert (rv == pdPASS); diff --git a/main/scenes/scene_demo.c b/main/scenes/scene_demo.c index b57d9a0..0dfa33c 100644 --- a/main/scenes/scene_demo.c +++ b/main/scenes/scene_demo.c @@ -70,16 +70,16 @@ static void paint(struct DemoScene *self) break; } - LCD_setStrEx(header, 0, 3, 1, 3); + LCD_setStrEx(header, 3, 1, BLACK, (struct TextStyle) {.size = FONT_BOLD}); - LCD_setRect(0, 12, 83, 31, 1, 1); + LCD_setRect(0, 12, 83, 31, true, BLACK); char buf[10]; // sprintf(buf, "%3d", priv->pos); // LCD_setStr(buf, 2, 15, 0); sprintf(buf, "🌑%.0fΒ°C", analog_read()); - LCD_setStrEx(buf, 2, 14, 0, 2); + LCD_setStrEx(buf, 2, 14, WHITE, (struct TextStyle) {.size = FONT_DOUBLE}); - LCD_setStr("Γ—β†‘β†“β†β†’β°βŒ›β˜ΈβŽπŸŒ‘Β°πŸ”™β–Άβ—€", 2, 33, 1); + LCD_setStr("Γ—β†‘β†“β†β†’β°βŒ›β˜ΈβŽπŸŒ‘Β°πŸ”™β–Άβ—€", 2, 33, BLACK); } struct Scene *NewScene_Demo(void) { diff --git a/main/scenes/scene_menu.c b/main/scenes/scene_menu.c index 1f556be..a8c4dd5 100644 --- a/main/scenes/scene_menu.c +++ b/main/scenes/scene_menu.c @@ -6,19 +6,37 @@ #include "liquid.h" #include "graphics/drawing.h" +#include "graphics/display_spec.h" #include "scene_menu.h" +#if 0 static void paint(struct MenuScene *self) { LCD_clearDisplay(0); int row = 0; - for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + 6); i++, row++) { + for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->visible_lines); i++, row++) { bool selected = self->selected==i; LCD_setStrEx(self->items[i].label, 0, row * 8, selected?WHITE:BLACK,FONT_NORMAL|(selected?FONT_BG:0)); } } +#endif + +static void paint(struct MenuScene *self) +{ + LCD_clearDisplay(0); + + int row = 0; + for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->visible_lines); i++, row++) { + bool selected = self->selected==i; + if (selected) LCD_setRect(0, row*9, LCD_WIDTH-1, row*9 + 8, 1, 1); + LCD_setStrEx(self->items[i].label, 1, row * 9 + 1, !selected, (struct TextStyle) { + .bg = selected, + .max_chars = 13, + }); + } +} static struct SceneEvent onInput(struct MenuScene *self, struct InputEvent event) { switch (event.kind) { @@ -36,7 +54,7 @@ static struct SceneEvent onInput(struct MenuScene *self, struct InputEvent event self->scroll_up--; } - while (self->selected - self->scroll_up >= 5 && self->scroll_up < self->items_len - 6) { + while (self->selected - self->scroll_up >= (self->visible_lines - 1) && self->scroll_up < self->items_len - self->visible_lines) { self->scroll_up++; } @@ -77,6 +95,7 @@ struct MenuScene *NewScene_Menu(struct MenuItem *items, size_t items_len) { // TODO long selected label scrolling on tick + scene->visible_lines = 5; scene->items = items; scene->items_len = items_len; diff --git a/main/scenes/scene_menu.h b/main/scenes/scene_menu.h index 5a44f75..f5cbee9 100644 --- a/main/scenes/scene_menu.h +++ b/main/scenes/scene_menu.h @@ -44,6 +44,8 @@ struct MenuScene { int32_t scroll_up; // Selected item number int32_t selected; + // Number of lines visible at a time + uint8_t visible_lines; // selection handler MenuScene_onSelect_t onSelect; // Extra field for the subclass's private use. diff --git a/main/scenes/scene_number.c b/main/scenes/scene_number.c index 1e7fd14..fee412a 100644 --- a/main/scenes/scene_number.c +++ b/main/scenes/scene_number.c @@ -40,11 +40,11 @@ static void paint(struct NumberScene *self) struct NumberSceneOpts *opts = self->opts; LCD_clearDisplay(0); - LCD_setStrEx(opts->label, 1, 1, BLACK, FONT_BOLD); + LCD_setStrEx(opts->label, 1, 1, BLACK, (struct TextStyle) {.size = FONT_BOLD}); char buf[10]; snprintf(buf, 10, "%d%s", opts->value, opts->unit); - LCD_setStrEx(buf, 1, 9, BLACK, FONT_DOUBLE); + LCD_setStrEx(buf, 1, 9, BLACK, (struct TextStyle) {.size = FONT_DOUBLE}); } static void deinit(struct NumberScene *self) diff --git a/main/scenes/scene_root.c b/main/scenes/scene_root.c index 839753e..eae1da0 100644 --- a/main/scenes/scene_root.c +++ b/main/scenes/scene_root.c @@ -35,7 +35,7 @@ static void paint(struct Scene *self) { LCD_clearDisplay(0); - LCD_setStrEx("Press DEL to enter setup", 0, 0, 1, 0x3); + LCD_setStrEx("Press DEL to enter setup", 0, 0, BLACK, (struct TextStyle) {.size = FONT_BOLD}); } struct Scene *NewScene_Root(void) {