From 0ccb84266783b9693c762bc27b4ac95d72d38e50 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 14 Jan 2023 16:07:31 +0900 Subject: [PATCH 1/7] fb: cfb: Restore inverted framebuffer to make able to reuse cfb_framebuffer_finalize() invert the framebuffer, and it still leave as inverted after executing the function. It restores the framebuffer at the end of the cfb_framebuffer_finalize() for the continued drawing. Signed-off-by: TOKITA Hiroshi --- subsys/fb/cfb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index e2ceb74fdff41..487c553f65665 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -240,6 +240,7 @@ int cfb_framebuffer_finalize(const struct device *dev) const struct display_driver_api *api = dev->api; const struct char_framebuffer *fb = &char_fb; struct display_buffer_descriptor desc; + int err; if (!fb || !fb->buf) { return -ENODEV; @@ -252,6 +253,9 @@ int cfb_framebuffer_finalize(const struct device *dev) if (!(fb->pixel_format & PIXEL_FORMAT_MONO10) != !(fb->inverted)) { cfb_invert(fb); + err = api->write(dev, 0, 0, &desc, fb->buf); + cfb_invert(fb); + return err; } return api->write(dev, 0, 0, &desc, fb->buf); From 83f63eb5739ccefef6311566e9e7e7207a90273f Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Thu, 2 Mar 2023 23:02:43 +0900 Subject: [PATCH 2/7] fb: cfb: add const modifier for argument of cfb_print cfb_print does not modify either address and contents of the pointer that is pointing drawing text. Thus, it can add a const modifier. Signed-off-by: TOKITA Hiroshi --- include/zephyr/display/cfb.h | 2 +- subsys/fb/cfb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/zephyr/display/cfb.h b/include/zephyr/display/cfb.h index cc3242e6fd0d9..1de9cafac3747 100644 --- a/include/zephyr/display/cfb.h +++ b/include/zephyr/display/cfb.h @@ -87,7 +87,7 @@ struct cfb_font { * * @return 0 on success, negative value otherwise */ -int cfb_print(const struct device *dev, char *str, uint16_t x, uint16_t y); +int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y); /** * @brief Clear framebuffer. diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index 487c553f65665..65e5b0de35048 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -128,7 +128,7 @@ static uint8_t draw_char_vtmono(const struct char_framebuffer *fb, return fptr->width; } -int cfb_print(const struct device *dev, char *str, uint16_t x, uint16_t y) +int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y) { const struct char_framebuffer *fb = &char_fb; const struct cfb_font *fptr; From 0aa46069adcca8bcae8714b86cbdc39e83f8589d Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Thu, 12 Jan 2023 19:34:35 +0900 Subject: [PATCH 3/7] fb: cfb: support drawing to any coordinates Implements dot-by-dot font rendering to make cfb_print() able to draw text even at a coordinate that is not on tile boundaries. Signed-off-by: TOKITA Hiroshi --- subsys/fb/cfb.c | 83 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index 65e5b0de35048..42d94498b8ff9 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -88,12 +88,13 @@ static inline uint8_t get_glyph_byte(uint8_t *glyph_ptr, const struct cfb_font * * a byte is interpreted as 8 pixels ordered vertically among each other. */ static uint8_t draw_char_vtmono(const struct char_framebuffer *fb, - char c, uint16_t x, uint16_t y) + char c, uint16_t x, uint16_t y, + bool draw_bg) { const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]); - uint8_t *glyph_ptr; - bool need_reverse = (((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0) + const bool need_reverse = (((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0) != ((fptr->caps & CFB_FONT_MSB_FIRST) != 0)); + uint8_t *glyph_ptr; if (c < fptr->first_char || c > fptr->last_char) { c = ' '; @@ -105,24 +106,78 @@ static uint8_t draw_char_vtmono(const struct char_framebuffer *fb, } for (size_t g_x = 0; g_x < fptr->width; g_x++) { - uint32_t y_segment = y / 8U; - - for (size_t g_y = 0; g_y < fptr->height / 8U; g_y++) { - uint32_t fb_y = (y_segment + g_y) * fb->x_res; + const int16_t fb_x = x + g_x; + + for (size_t g_y = 0; g_y < fptr->height; g_y++) { + /* + * Process glyph rendering in the y direction + * by separating per 8-line boundaries. + */ + + const int16_t fb_y = y + g_y; + const size_t fb_index = (fb_y / 8U) * fb->x_res + fb_x; + const size_t offset = y % 8; + uint8_t bg_mask; uint8_t byte; - if ((fb_y + x + g_x) >= fb->size) { - return 0; + if (fb_x < 0 || fb->x_res <= fb_x || fb_y < 0 || fb->y_res <= fb_y) { + continue; + } + + byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y / 8); + + if (offset == 0) { + /* + * The start row is on an 8-line boundary. + * Therefore, it can set the value directly. + */ + bg_mask = 0; + g_y += 7; + } else if (g_y == 0) { + /* + * If the starting row is not on the 8-line boundary, + * shift the glyph to the starting line, and create a mask + * from the 8-line boundary to the starting line. + */ + byte = byte << offset; + bg_mask = BIT_MASK(offset); + g_y += (7 - offset); + } else { + /* + * After the starting row, read and concatenate the next 8-rows + * glyph and clip to the 8-line boundary and write 8-lines + * at the time. + * Create a mask to prevent overwriting the drawing contents + * after the end row. + */ + const size_t lines = ((fptr->height - g_y) < 8) ? offset : 8; + + if (lines == 8) { + uint16_t byte2 = byte; + + byte2 |= (get_glyph_byte(glyph_ptr, fptr, g_x, + (g_y + 8) / 8)) + << 8; + byte = (byte2 >> (8 - offset)) & BIT_MASK(lines); + } else { + byte = (byte >> (8 - offset)) & BIT_MASK(lines); + } + + bg_mask = (BIT_MASK(8 - lines) << (lines)) & 0xFF; + g_y += (lines - 1); } - byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y); if (need_reverse) { byte = byte_reverse(byte); + bg_mask = byte_reverse(bg_mask); } - fb->buf[fb_y + x + g_x] = byte; - } + if (draw_bg) { + fb->buf[fb_index] &= bg_mask; + } + fb->buf[fb_index] |= byte; + } } return fptr->width; @@ -144,13 +199,13 @@ int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint1 return -EINVAL; } - if ((fb->screen_info & SCREEN_INFO_MONO_VTILED) && !(y % 8)) { + if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) { for (size_t i = 0; i < strlen(str); i++) { if (x + fptr->width > fb->x_res) { x = 0U; y += fptr->height; } - x += fb->kerning + draw_char_vtmono(fb, str[i], x, y); + x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, true); } return 0; } From e9c2cdbef2ff971e3198630d27083fe8778c9713 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 14 Jan 2023 16:33:07 +0900 Subject: [PATCH 4/7] fb: cfb: Add cfb_draw_text() API Add cfb_draw_text() API for rendering text. It is similar to cfb_print(), the difference is cfb_draw_text() accepts coordinate that is not on tile boundaries and don't wrap the line. Signed-off-by: TOKITA Hiroshi --- include/zephyr/display/cfb.h | 14 ++++++++++++++ subsys/fb/cfb.c | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/zephyr/display/cfb.h b/include/zephyr/display/cfb.h index 1de9cafac3747..e80053994bd15 100644 --- a/include/zephyr/display/cfb.h +++ b/include/zephyr/display/cfb.h @@ -89,6 +89,20 @@ struct cfb_font { */ int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y); +/** + * @brief Print a string into the framebuffer. + * For compare to cfb_print, cfb_draw_text accept non tile-aligned coords + * and not line wrapping. + * + * @param dev Pointer to device structure for driver instance + * @param str String to print + * @param x Position in X direction of the beginning of the string + * @param y Position in Y direction of the beginning of the string + * + * @return 0 on success, negative value otherwise + */ +int cfb_draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y); + /** * @brief Clear framebuffer. * diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index 42d94498b8ff9..631916105c48c 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -183,7 +183,8 @@ static uint8_t draw_char_vtmono(const struct char_framebuffer *fb, return fptr->width; } -int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y) +static int draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y, + bool wrap) { const struct char_framebuffer *fb = &char_fb; const struct cfb_font *fptr; @@ -201,11 +202,11 @@ int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint1 if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) { for (size_t i = 0; i < strlen(str); i++) { - if (x + fptr->width > fb->x_res) { + if ((x + fptr->width > fb->x_res) && wrap) { x = 0U; y += fptr->height; } - x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, true); + x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, wrap); } return 0; } @@ -214,6 +215,16 @@ int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint1 return -EINVAL; } +int cfb_draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y) +{ + return draw_text(dev, str, x, y, false); +} + +int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y) +{ + return draw_text(dev, str, x, y, true); +} + int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { From 1655cd13a6e0e159a467d18252281e73bfbffc13 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Thu, 12 Jan 2023 22:26:34 +0900 Subject: [PATCH 5/7] fb: cfb: support inverting with coordinates that do not align with the tile Improve `cfb_invert_area()` able to invert even at a coordinate not on tile boundaries. Signed-off-by: TOKITA Hiroshi --- subsys/fb/cfb.c | 53 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index 631916105c48c..665000cecc05c 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -229,6 +229,7 @@ int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { const struct char_framebuffer *fb = &char_fb; + const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0); if (x >= fb->x_res || y >= fb->y_res) { LOG_ERR("Coordinates outside of framebuffer"); @@ -236,7 +237,15 @@ int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y, return -EINVAL; } - if ((fb->screen_info & SCREEN_INFO_MONO_VTILED) && !(y % 8)) { + if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) { + if (x > fb->x_res) { + x = fb->x_res; + } + + if (y > fb->y_res) { + y = fb->y_res; + } + if (x + width > fb->x_res) { width = fb->x_res - x; } @@ -246,10 +255,46 @@ int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y, } for (size_t i = x; i < x + width; i++) { - for (size_t j = y / 8U; j < (y + height) / 8U; j++) { - size_t index = (j * fb->x_res) + i; + for (size_t j = y; j < (y + height); j++) { + /* + * Process inversion in the y direction + * by separating per 8-line boundaries. + */ - fb->buf[index] = ~fb->buf[index]; + const size_t index = ((j / 8) * fb->x_res) + i; + const uint8_t remains = y + height - j; + + /* + * Make mask to prevent overwriting the drawing contents that on + * between the start line or end line and the 8-line boundary. + */ + if ((j % 8) > 0) { + uint8_t m = BIT_MASK((j % 8)); + uint8_t b = fb->buf[index]; + + if (need_reverse) { + m = byte_reverse(m); + b = byte_reverse(b); + } + + fb->buf[index] = ~(b | m) | (b & m); + j += 7 - (j % 8); + } else if (remains >= 8) { + /* No mask required if no start or end line is included */ + fb->buf[index] = ~fb->buf[index]; + j += 7; + } else { + uint8_t m = BIT_MASK(remains % 8) << (8 - (remains % 8)); + uint8_t b = fb->buf[index]; + + if (need_reverse) { + m = byte_reverse(m); + b = byte_reverse(b); + } + + fb->buf[index] = ~(b | m) | (b & m); + j += (remains - 1); + } } } From 24be0f17a2cb1db41848473565aea3492ae63e61 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 14 Jan 2023 16:07:05 +0900 Subject: [PATCH 6/7] fb: cfb_shell: Add `draw text` command Add the `draw text` command to execute the `cfb_draw_text()` API. The command is similar to the `print` command. The difference is `draw text` can render to coordinate that is not on tile boundaries. The command is not run `cfb_framebuffer_clear()`, So it allows rendering appendiceal with keeping already drawn image. Signed-off-by: TOKITA Hiroshi --- subsys/fb/cfb_shell.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/subsys/fb/cfb_shell.c b/subsys/fb/cfb_shell.c index 103cc1e2ed223..407721be20105 100644 --- a/subsys/fb/cfb_shell.c +++ b/subsys/fb/cfb_shell.c @@ -119,6 +119,29 @@ static int cmd_print(const struct shell *shell, size_t argc, char *argv[]) return err; } +static int cmd_draw_text(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + int x, y; + + if (!dev) { + shell_error(sh, HELP_INIT); + return -ENODEV; + } + + x = strtol(argv[1], NULL, 10); + y = strtol(argv[2], NULL, 10); + err = cfb_draw_text(dev, argv[3], x, y); + if (err) { + shell_error(sh, "Failed text drawing to Framebuffer error=%d", err); + return err; + } + + err = cfb_framebuffer_finalize(dev); + + return err; +} + static int cmd_scroll_vert(const struct shell *shell, size_t argc, char *argv[]) { int err = 0; @@ -488,6 +511,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_scroll, SHELL_SUBCMD_SET_END ); +SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_draw, + SHELL_CMD_ARG(text, NULL, HELP_PRINT, cmd_draw_text, 4, 0), + SHELL_SUBCMD_SET_END +); + SHELL_STATIC_SUBCMD_SET_CREATE(cfb_cmds, SHELL_CMD_ARG(init, NULL, HELP_NONE, cmd_init, 1, 0), SHELL_CMD_ARG(get_device, NULL, HELP_NONE, cmd_get_device, 1, 0), @@ -500,6 +528,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(cfb_cmds, SHELL_CMD_ARG(print, NULL, HELP_PRINT, cmd_print, 4, 0), SHELL_CMD(scroll, &sub_cmd_scroll, "scroll a text in vertical or " "horizontal direction", NULL), + SHELL_CMD(draw, &sub_cmd_draw, "drawing text", NULL), SHELL_CMD_ARG(clear, NULL, HELP_NONE, cmd_clear, 1, 0), SHELL_SUBCMD_SET_END ); From 75eda2729273449d768246bd9b1eaf32e48d71f0 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 13 Jan 2023 19:59:35 +0900 Subject: [PATCH 7/7] fb: cfb_shell: support area inverting Extend the invert command to run cfb_invert_area when given four arguments. Make a change to invert the screen immediately in case of run invert without arguments. Signed-off-by: TOKITA Hiroshi --- subsys/fb/cfb_shell.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/subsys/fb/cfb_shell.c b/subsys/fb/cfb_shell.c index 407721be20105..9e9af1df8f0c0 100644 --- a/subsys/fb/cfb_shell.c +++ b/subsys/fb/cfb_shell.c @@ -18,6 +18,7 @@ #define HELP_NONE "[none]" #define HELP_INIT "call \"cfb init\" first" #define HELP_PRINT " \"\"" +#define HELP_INVERT "[ ]" static const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); @@ -289,21 +290,49 @@ static int cmd_invert(const struct shell *shell, size_t argc, char *argv[]) { int err; - ARG_UNUSED(argc); - ARG_UNUSED(argv); - if (!dev) { shell_error(shell, HELP_INIT); return -ENODEV; } - err = cfb_framebuffer_invert(dev); + if (argc == 1) { + uint8_t width = cfb_get_display_parameter(dev, CFB_DISPLAY_WIDTH); + uint8_t height = cfb_get_display_parameter(dev, CFB_DISPLAY_HEIGH); + + err = cfb_invert_area(dev, 0, 0, width, height); + if (err) { + shell_error(shell, "Error inverting area"); + return err; + } + + err = cfb_framebuffer_invert(dev); + if (err) { + return err; + } + } else if (argc == 5) { + int x, y, w, h; + + x = strtol(argv[1], NULL, 10); + y = strtol(argv[2], NULL, 10); + w = strtol(argv[3], NULL, 10); + h = strtol(argv[4], NULL, 10); + + err = cfb_invert_area(dev, x, y, w, h); + if (err) { + shell_error(shell, "Error invert area"); + return err; + } + } else { + shell_help(shell); + return 0; + } + if (err) { shell_error(shell, "Error inverting Framebuffer"); return err; } - cmd_cfb_print(shell, 0, 0, ""); + cfb_framebuffer_finalize(dev); shell_print(shell, "Framebuffer Inverted"); @@ -524,7 +553,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(cfb_cmds, SHELL_CMD_ARG(get_fonts, NULL, HELP_NONE, cmd_get_fonts, 1, 0), SHELL_CMD_ARG(set_font, NULL, "", cmd_set_font, 2, 0), SHELL_CMD_ARG(set_kerning, NULL, "", cmd_set_kerning, 2, 0), - SHELL_CMD_ARG(invert, NULL, HELP_NONE, cmd_invert, 1, 0), + SHELL_CMD_ARG(invert, NULL, HELP_INVERT, cmd_invert, 1, 5), SHELL_CMD_ARG(print, NULL, HELP_PRINT, cmd_print, 4, 0), SHELL_CMD(scroll, &sub_cmd_scroll, "scroll a text in vertical or " "horizontal direction", NULL),