From 01537cb785057b386033ab9d59a5a72a7b06b5ec Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 12 Apr 2024 19:45:03 +0900 Subject: [PATCH] fb: cfb: Introducing a command structure for framebuffer operations Store operations for the frame buffer as commands, Change to a method that draws at once and transfers at finalize. Commands are managed in a linked list and processed at the finalization. The behavior of drawing APIs does not change. It's just that the timing at which drawing is done to memory is internally delayed a little. Typical usage stores command in the buffer. The behavior of existing drawing APIs has been changed to store commands in this buffer. For this reason, an argument for buffer specification is added to the initialization function cfb_display_init. Signed-off-by: TOKITA Hiroshi --- include/zephyr/display/cfb.h | 781 ++++++++++++++++-- samples/subsys/display/cfb/Kconfig | 4 + .../display/cfb/boards/longan_nano.conf | 3 + samples/subsys/display/cfb/src/main.c | 3 + .../subsys/display/cfb_custom_font/Kconfig | 4 + .../cfb_custom_font/boards/longan_nano.conf | 3 + .../subsys/display/cfb_custom_font/src/main.c | 3 + .../display/cfb_shell/boards/longan_nano.conf | 3 + subsys/fb/cfb.c | 570 ++++++++++--- tests/subsys/display/cfb/basic/Kconfig | 4 + tests/subsys/display/cfb/basic/src/utils.c | 6 +- tests/subsys/display/cfb/basic/testcase.yaml | 24 + 12 files changed, 1212 insertions(+), 196 deletions(-) create mode 100644 samples/subsys/display/cfb/boards/longan_nano.conf create mode 100644 samples/subsys/display/cfb_custom_font/boards/longan_nano.conf create mode 100644 samples/subsys/display/cfb_shell/boards/longan_nano.conf diff --git a/include/zephyr/display/cfb.h b/include/zephyr/display/cfb.h index 85400c6b804f728..8fbf81548f31604 100644 --- a/include/zephyr/display/cfb.h +++ b/include/zephyr/display/cfb.h @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -28,6 +29,267 @@ extern "C" { * @{ */ +/** + * @brief Macro for creating a font entry. + * + * @param _name Name of the font entry. + * @param _width Width of the font in pixels + * @param _height Height of the font in pixels. + * @param _caps Font capabilities. + * @param _data Raw data of the font. + * @param _fc Character mapped to first font element. + * @param _lc Character mapped to last font element. + */ +#define FONT_ENTRY_DEFINE(_name, _width, _height, _caps, _data, _fc, _lc) \ + static const STRUCT_SECTION_ITERABLE(cfb_font, _name) = { \ + .data = _data, \ + .caps = _caps, \ + .width = _width, \ + .height = _height, \ + .first_char = _fc, \ + .last_char = _lc, \ + } + +/** + * @brief Initializer macro for draw-point operation + * + * @param _x x position of the point + * @param _y y position of the point + */ +#define CFB_OP_INIT_DRAW_POINT(str, _x, _y) \ + { \ + .param = { \ + .op = CFB_OP_DRAW_POINT, \ + .draw_figure = \ + { \ + .start = {_x, _y}, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for draw-line operation + * + * @param _s_x Start x position of the line + * @param _s_y Start y position of the line + * @param _e_x End x position of the line + * @param _e_y End y position of the line + */ +#define CFB_OP_INIT_DRAW_LINE(_s_x, _s_y, _e_x, _e_y) \ + { \ + .param = { \ + .op = CFB_OP_DRAW_LINE, \ + .draw_figure = \ + { \ + .start = {_s_x, _s_y}, \ + .end = {_e_x, _e_y}, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for draw-rect operation + * + * @param _s_x Top-Left x position of the rectangle + * @param _s_y Top-Left x position of the rectangle + * @param _e_x Bottom-Right y position of the rectangle + * @param _e_y Top-Left y position of the rectangle + */ +#define CFB_OP_INIT_DRAW_RECT(_s_x, _s_y, _e_x, _e_y) \ + { \ + .param = { \ + .op = CFB_OP_DRAW_RECT, \ + .draw_figure = \ + { \ + .start = {_s_x, _s_y}, \ + .end = {_e_x, _e_y}, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for draw-text operation + * + * @param _str String to print + * @param _x Position in X direction of the beginning of the strings + * @param _y Position in Y direction of the beginning of the strings + */ +#define CFB_OP_INIT_DRAW_TEXT(_str, _x, _y) \ + { \ + .param = { \ + .op = CFB_OP_DRAW_TEXT, \ + .draw_text = \ + { \ + .pos = {_x, _y}, \ + .str = _str, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for print operation + * + * @param _str String to print + * @param _x Position in X direction of the beginning of the strings + * @param _y Position in Y direction of the beginning of the strings + */ +#define CFB_OP_INIT_PRINT(_str, _x, _y) \ + { \ + .param = { \ + .op = CFB_OP_PRINT, \ + .draw_text = \ + { \ + .pos = {_x, _y}, \ + .str = _str, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for draw text-reference operation + * + * @param _str String to print + * @param _x Position in X direction of the beginning of the strings + * @param _y Position in Y direction of the beginning of the strings + */ +#define CFB_OP_INIT_DRAW_TEXT_REF(_str, _x, _y) \ + { \ + .param = { \ + .op = CFB_OP_DRAW_TEXT_REF, \ + .draw_text = \ + { \ + .pos = {_x, _y}, \ + .str = _str, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for print-ref operation + * + * @param _str String to print + * @param _x Position in X direction of the beginning of the strings + * @param _y Position in Y direction of the beginning of the strings + */ +#define CFB_OP_INIT_PRINT_REF(_str, _x, _y) \ + { \ + .param = { \ + .op = CFB_OP_PRINT_REF, \ + .draw_text = \ + { \ + .pos = {_x, _y}, \ + .str = _str, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for invert-area operation + * + * @param _x Position in X direction of the beginning of area + * @param _y Position in Y direction of the beginning of area + * @param _w Width of area in pixels + * @param _h Height of area in pixels + * + */ +#define CFB_OP_INIT_INVERT_AREA(_x, _y, _w, _h) \ + { \ + .param = { \ + .op = CFB_OP_INVERT_AREA, \ + .invert_area = \ + { \ + .x = _x, \ + .y = _y, \ + .w = _w, \ + .h = _h, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for set-font operation + * + * @param _font_idx Font index + */ +#define CFB_OP_INIT_SET_FONT(_font_idx) \ + { \ + .param = { \ + .op = CFB_OP_SET_FONT, \ + .set_font = \ + { \ + .font_idx = _font_idx, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for set-kerning operation + * + * @param _kerning Spacing between each characters in pixels + */ +#define CFB_OP_INIT_SET_KERNING(_kerning) \ + { \ + .param = { \ + .op = CFB_OP_SET_KERNING, \ + .set_kerning = \ + { \ + .kerning = _kerning, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for set-fgcolor operation + * @param r red in 24-bit color notation + * @param g green in 24-bit color notation + * @param b blue in 24-bit color notation + * @param a alpha channel + */ +#define CFB_OP_INIT_SET_FG_COLOR(r, g, b, a) \ + { \ + .param = { \ + .op = CFB_OP_SET_FG_COLOR, \ + .set_color = \ + { \ + .red = r, \ + .green = g, \ + .blue = b, \ + .alpha = a, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for set-bgcolor operation + * @param r red in 24-bit color notation + * @param g green in 24-bit color notation + * @param b blue in 24-bit color notation + * @param a alpha channel + */ +#define CFB_OP_INIT_SET_BG_COLOR(r, g, b, a) \ + { \ + .param = { \ + .op = CFB_OP_SET_BG_COLOR, \ + .set_color = \ + { \ + .red = r, \ + .green = g, \ + .blue = b, \ + .alpha = a, \ + }, \ + }, \ + } + +/** + * @brief Initializer macro for swap fg/bg color operation + */ +#define CFB_OP_INIT_SWAP_FG_BG_COLOR() \ + { \ + .param = { \ + .op = CFB_OP_SWAP_FG_BG_COLOR, \ + }, \ + } + enum cfb_display_param { CFB_DISPLAY_HEIGH = 0, CFB_DISPLAY_WIDTH, @@ -42,6 +304,54 @@ enum cfb_font_caps { CFB_FONT_MSB_FIRST = BIT(2), }; +/** + * @brief CFB operation types. + */ +enum cfb_operation { + CFB_OP_NOP = 0x0, + /** Inverts the color of the specified area */ + CFB_OP_INVERT_AREA, + /** Draw a point */ + CFB_OP_DRAW_POINT, + /** Draw a line */ + CFB_OP_DRAW_LINE, + /** Draw a rectangle */ + CFB_OP_DRAW_RECT, + /** Draw a text */ + CFB_OP_DRAW_TEXT, + /** Print a text */ + CFB_OP_PRINT, + /** Draw a text specified by reference */ + CFB_OP_DRAW_TEXT_REF, + /** Print a text specified by reference */ + CFB_OP_PRINT_REF, + /** Set font for text rendering */ + CFB_OP_SET_FONT, + /** Set kerning for text rendering */ + CFB_OP_SET_KERNING, + /** Set foreground color */ + CFB_OP_SET_FG_COLOR, + /** Set background color */ + CFB_OP_SET_BG_COLOR, + /** Swap foreground and background colors */ + CFB_OP_SWAP_FG_BG_COLOR, + /** + * Fills the drawing surface with the background color. + * This is internal operation. Don't use it. + */ + CFB_OP_FILL = 0xFE, + CFB_OP_TERMINATE = UINT8_MAX, +}; + +enum init_commands { + CFB_INIT_CMD_SET_FONT, + CFB_INIT_CMD_SET_KERNING, + CFB_INIT_CMD_SET_FG_COLOR, + CFB_INIT_CMD_SET_BG_COLOR, + CFB_INIT_CMD_FILL, + NUM_OF_INIT_CMDS, +}; + struct cfb_font { const void *data; enum cfb_font_caps caps; @@ -56,6 +366,91 @@ struct cfb_position { int16_t y; }; +/** + * @brief CFB operation structure + */ +struct cfb_command_param { + union { + /** + * @brief Parameter for CFB_OP_INVERT_AREA operation + */ + struct { + int16_t x; + int16_t y; + uint16_t w; + uint16_t h; + } invert_area; + + /** + * @brief Parameter for CFB_OP_DRAW_POINT, CFB_OP_DRAW_LINE + * and CFB_DRAW_RECT operation + */ + struct { + struct cfb_position start; + struct cfb_position end; + } draw_figure; + + /** + * @brief Parameter for CFB_OP_PRINT and CFB_OP_DRAW_TEXT + * operation + */ + struct { + const char *str; + struct cfb_position pos; + } draw_text; + + /** + * @brief Parameter for CFB_OP_SET_FONT operation + */ + struct { + uint8_t font_idx; + } set_font; + + /** + * @brief Parameter for CFB_OP_SET_KERNING operation + */ + struct { + int8_t kerning; + } set_kerning; + + /** + * @brief Parameter for CFB_OP_SET_FG_COLOR and + * CFB_OP_SET_BG_COLOR operation + */ + union { + struct { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; + }; + uint32_t color; + } set_color; + }; + + /** Type of operation */ + enum cfb_operation op; +} __packed; + +struct cfb_command { + /** Linked-list node */ + sys_snode_t node; + struct cfb_command_param param; +}; + +/** + * @param node current node pointer + * @param param A param element in the buffer. This exists only in the case that the node is + * pointing to the SET_COMMAND_BUFFER command. + */ +struct cfb_command_iterator { + sys_snode_t *node; + struct cfb_command_param *param; + struct cfb_display *disp; + struct cfb_command_iterator *(*next)(struct cfb_command_iterator *ite); + bool (*is_last)(struct cfb_command_iterator *ite); +}; + /** * A framebuffer definition in CFB. * @@ -66,7 +461,7 @@ struct cfb_framebuffer { /** Pointer to a buffer in RAM */ uint8_t *buf; - /** Size of the framebuffer */ + /** Size of the buf */ uint32_t size; /** Display pixel format */ @@ -81,27 +476,61 @@ struct cfb_framebuffer { /** Framebuffer height in pixels */ uint16_t height; - /** Resolution of a framebuffer in pixels in X direction */ + /** The top-left of partial-framebuffer*/ struct cfb_position pos; /** Resolution of a framebuffer in pixels in X direction */ struct cfb_position res; + /** + * @param disp Pointer to display instance + * @return 0 on success, negative value otherwise + */ + int (*finalize)(struct cfb_framebuffer *disp, int16_t x, int16_t y, uint16_t width, + uint16_t height); + + /** + * @param disp Pointer to display instance + * @param clear_display Clear the display as well + * @return 0 on success, negative value otherwise + */ + int (*clear)(struct cfb_framebuffer *disp, bool clear_display); + + /** + * @param disp Pointer to display instance + * @param cmd Pointer to command for append to the list + * @return 0 on success, negative value otherwise + */ + int (*append_command)(struct cfb_framebuffer *disp, struct cfb_command *cmd); + + /** + * @param disp Pointer to display instance + * @return Pointer to iterator, Null on unsucceeded + */ + struct cfb_command_iterator *(*init_iterator)(struct cfb_framebuffer *disp); }; /** - * A display definition in CFB. + * CFB command buffer . * * This is a private definition. * Don't access directly to members of this structure. */ -struct cfb_display { - /** Framebuffer */ - struct cfb_framebuffer fb; +struct cfb_commandbuffer { + /** Pointer to a buffer */ + uint8_t *buf; - /** Pointer to device */ - const struct device *dev; + /** Size of the commandbuffer*/ + uint32_t size; + /** Current position of buffer */ + size_t pos; +}; + +/** + * A collection of draw settings + */ +struct cfb_draw_settings { /** Current font index */ uint8_t font_idx; @@ -113,110 +542,251 @@ struct cfb_display { /** Current background color */ uint32_t bg_color; +}; + +/** + * A display definition in CFB. + * + * This is a private definition. + * Don't access directly to members of this structure. + */ +struct cfb_display { + /** Framebuffer */ + struct cfb_framebuffer fb; + + /** Command buffer */ + struct cfb_commandbuffer cmd; + /** Pointer to device */ + const struct device *dev; + + /** Current draw settings */ + struct cfb_draw_settings settings; + + /** Linked list for queueing commands */ + sys_slist_t cmd_list; + + /** Variable for storing commands that are used every time rendering initializing */ + struct cfb_command init_cmds[NUM_OF_INIT_CMDS]; + + /** iterator for process commands */ + struct cfb_command_iterator iterator; }; struct cfb_display_init_param { /** Pointer to device */ const struct device *dev; - /** Pointer to a buffer in RAM */ + /** Pointer to a transfer buffer */ uint8_t *transfer_buf; - /** Size of the framebuffer */ + /** Size of the transfer buffer */ uint32_t transfer_buf_size; + + /** Pointer to a command buffer */ + uint8_t *command_buf; + + /** Size of the command buffer */ + uint32_t command_buf_size; }; /** - * @brief Macro for creating a font entry. + * @brief Append command to list * - * @param _name Name of the font entry. - * @param _width Width of the font in pixels - * @param _height Height of the font in pixels. - * @param _caps Font capabilities. - * @param _data Raw data of the font. - * @param _fc Character mapped to first font element. - * @param _lc Character mapped to last font element. + * Append command to list. Execute commands in the list at finalizing. + * + * @param fb Pointer to framebuffer to rendering + * @param cmd Pointer to command for append to the list + * + * @retval 0 on success + * @retval -errno Negative errno for other failures. */ -#define FONT_ENTRY_DEFINE(_name, _width, _height, _caps, _data, _fc, _lc) \ - static const STRUCT_SECTION_ITERABLE(cfb_font, _name) = { \ - .data = _data, \ - .caps = _caps, \ - .width = _width, \ - .height = _height, \ - .first_char = _fc, \ - .last_char = _lc, \ - } +static inline int cfb_append_command(struct cfb_framebuffer *fb, struct cfb_command *cmd) +{ + return fb->append_command(fb, cmd); +} /** * @brief Print a string into the framebuffer. * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering * @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 + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_print(struct cfb_framebuffer *fb, const char *const str, int16_t x, int16_t y); +static inline int cfb_print(struct cfb_framebuffer *fb, const char *const str, int16_t x, int16_t y) +{ + struct cfb_command cmd = CFB_OP_INIT_PRINT(str, x, y); + + return fb->append_command(fb, &cmd); +} + +/** + * @brief Print a string reference into the framebuffer. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * + * @param fb Pointer to framebuffer to rendering + * @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 + * + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. + */ +static inline int cfb_print_ref(struct cfb_framebuffer *fb, const char *const str, int16_t x, + int16_t y) +{ + struct cfb_command cmd = CFB_OP_INIT_PRINT_REF(str, x, y); + + return fb->append_command(fb, &cmd); +} /** * @brief Print a string into the framebuffer. * For compare to cfb_print, cfb_draw_text accept non tile-aligned coords * and not line wrapping. * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering * @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 + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_draw_text(struct cfb_framebuffer *fb, const char *const str, int16_t x, int16_t y); +static inline int cfb_draw_text(struct cfb_framebuffer *fb, const char *const str, int16_t x, + int16_t y) +{ + struct cfb_command cmd = CFB_OP_INIT_DRAW_TEXT(str, x, y); + + return fb->append_command(fb, &cmd); +} + +/** + * @brief Print a string reference into the framebuffer. + * For compare to cfb_print, cfb_draw_text accept non tile-aligned coords + * and not line wrapping. + * + * This function copies the argument string into a buffer. + * The argument string can be discarded immediately, but it uses buffer memory. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * + * @param fb Pointer to framebuffer to rendering + * @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 + * + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. + */ +static inline int cfb_draw_text_ref(struct cfb_framebuffer *fb, const char *const str, int16_t x, + int16_t y) +{ + struct cfb_command cmd = CFB_OP_INIT_DRAW_TEXT_REF(str, x, y); + + return fb->append_command(fb, &cmd); +} /** * @brief Draw a point. * + * Draw a point to specified coordination. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering - * @param pos position of the point + * @param pos Position of the point * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_draw_point(struct cfb_framebuffer *fb, const struct cfb_position *pos); +static inline int cfb_draw_point(struct cfb_framebuffer *fb, const struct cfb_position *pos) +{ + struct cfb_command cmd = CFB_OP_INIT_DRAW_POINT(str, pos->x, pos->y); + + return fb->append_command(fb, &cmd); +} /** * @brief Draw a line. * + * Draw a line between specified two points. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering - * @param start start position of the line - * @param end end position of the line + * @param start Start position of the line + * @param end End position of the line * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_draw_line(struct cfb_framebuffer *fb, const struct cfb_position *start, - const struct cfb_position *end); +static inline int cfb_draw_line(struct cfb_framebuffer *fb, const struct cfb_position *start, + const struct cfb_position *end) +{ + struct cfb_command cmd = CFB_OP_INIT_DRAW_LINE(start->x, start->y, end->x, end->y); + + return fb->append_command(fb, &cmd); +} /** * @brief Draw a rectangle. * + * Draw a rectangle that formed by specified start and end points. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering * @param start Top-Left position of the rectangle * @param end Bottom-Right position of the rectangle * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_draw_rect(struct cfb_framebuffer *fb, const struct cfb_position *start, - const struct cfb_position *end); +static inline int cfb_draw_rect(struct cfb_framebuffer *fb, const struct cfb_position *start, + const struct cfb_position *end) +{ + struct cfb_command cmd = CFB_OP_INIT_DRAW_RECT(start->x, start->y, end->x, end->y); + + return fb->append_command(fb, &cmd); +} /** - * @brief Clear framebuffer. + * @brief Clear command buffer and framebuffer. * * @param fb Pointer to framebuffer to rendering * @param clear_display Clear the display as well * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -errno Negative errno for other failures. */ -int cfb_clear(struct cfb_framebuffer *fb, bool clear_display); +static inline int cfb_clear(struct cfb_framebuffer *fb, bool clear_display) +{ + return fb->clear(fb, clear_display); +} /** * @brief Inverts foreground and background colors @@ -225,11 +795,28 @@ int cfb_clear(struct cfb_framebuffer *fb, bool clear_display); * * @return 0 on success, negative value otherwise */ -int cfb_invert(struct cfb_framebuffer *fb); +static inline int cfb_invert(struct cfb_framebuffer *fb) +{ + struct cfb_command swap_fg_bg_cmd = CFB_OP_INIT_SWAP_FG_BG_COLOR(); + struct cfb_command invert_area_cmd = CFB_OP_INIT_INVERT_AREA(0, 0, UINT16_MAX, UINT16_MAX); + int err; + + err = fb->append_command(fb, &swap_fg_bg_cmd); + if (err) { + return err; + } + + return fb->append_command(fb, &invert_area_cmd); +} /** * @brief Invert Pixels in selected area. * + * Invert bits of pixels in selected area. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer to rendering * @param x Position in X direction of the beginning of area * @param y Position in Y direction of the beginning of area @@ -238,18 +825,36 @@ int cfb_invert(struct cfb_framebuffer *fb); * * @return 0 on success, negative value otherwise */ -int cfb_invert_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t width, - uint16_t height); +static inline int cfb_invert_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t width, + uint16_t height) +{ + struct cfb_command cmd = CFB_OP_INIT_INVERT_AREA(x, y, width, height); + + return fb->append_command(fb, &cmd); +} /** - * @brief Finalize framebuffer and write it to display RAM, - * invert or reorder pixels if necessary. + * @brief Finalize framebuffer. + * + * If the buffer is smaller than the screen size, this function executes the command + * in the command buffer to draw and transfer the image. + * If the buffer is larger than the screen size, the drawing has already been completed + * and only the data is transferred. * * @param fb Pointer to framebuffer to rendering * * @return 0 on success, negative value otherwise */ -int cfb_finalize(struct cfb_framebuffer *fb); +static inline int cfb_finalize(struct cfb_framebuffer *fb) +{ + return fb->finalize(fb, 0, 0, UINT16_MAX, UINT16_MAX); +} + +static inline int cfb_finalize_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, + uint16_t width, uint16_t height) +{ + return fb->finalize(fb, x, y, width, height); +} /** * @brief Get display parameter. @@ -264,31 +869,54 @@ int cfb_get_display_parameter(const struct cfb_display *disp, enum cfb_display_p /** * @brief Set font. * + * Select font that is used by cfb_draw_text and cfb_print for drawing. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. + * * @param fb Pointer to framebuffer instance * @param idx Font index * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_set_font(struct cfb_framebuffer *fb, uint8_t idx); +static inline int cfb_set_font(struct cfb_framebuffer *fb, uint8_t idx) +{ + struct cfb_command cmd = CFB_OP_INIT_SET_FONT(idx); + + return fb->append_command(fb, &cmd); +} /** - * @brief Set font kerning (spacing between individual letters). + * @brief Set font kerning. + * + * Set spacing between individual letters. + * + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. * * @param fb Pointer to framebuffer instance - * @param kerning Font kerning + * @param kerning Spacing between each characters in pixels * - * @return 0 on success, negative value otherwise + * @retval 0 on success + * @retval -ENOBUFS The command buffer does not have enough space + * @retval -errno Negative errno for other failures. */ -int cfb_set_kerning(struct cfb_framebuffer *fb, int8_t kerning); +static inline int cfb_set_kerning(struct cfb_framebuffer *fb, int8_t kerning) +{ + struct cfb_command cmd = CFB_OP_INIT_SET_KERNING(kerning); + + return fb->append_command(fb, &cmd); +} /** * @brief Set foreground color. * * Set foreground color with RGBA values in 32-bit color representation * - * This function simply stores the drawing command in the buffer, - * and the actual drawing will be done when cfb_finalize is executed. - * The setting keeps even after finalize is executed. + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. * * @param fb Pointer to framebuffer instance * @param r The red component of the foreground color in 32-bit color @@ -300,17 +928,21 @@ int cfb_set_kerning(struct cfb_framebuffer *fb, int8_t kerning); * @retval -ENOBUFS The command buffer does not have enough space * @retval -errno Negative errno for other failures. */ -int cfb_set_fg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, - uint8_t b, uint8_t a); +static inline int cfb_set_fg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) +{ + struct cfb_command cmd = CFB_OP_INIT_SET_FG_COLOR(r, g, b, a); + + return fb->append_command(fb, &cmd); +} /** * @brief Set background color. * * Set background color with RGBA values in 32-bit color representation * - * This function simply stores the drawing command in the buffer, - * and the actual drawing will be done when cfb_finalize is executed. - * The setting keeps even after finalize is executed. + * This function immediately draws to the buffer if the buffer is large enough + * to store the entire screen. Otherwise, store the command in the command buffer. * * @param fb Pointer to framebuffer instance * @param r The red component of the foreground color in 32-bit color @@ -322,8 +954,13 @@ int cfb_set_fg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, * @retval -ENOBUFS The command buffer does not have enough space * @retval -errno Negative errno for other failures. */ -int cfb_set_bg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, - uint8_t b, uint8_t a); +static inline int cfb_set_bg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) +{ + struct cfb_command cmd = CFB_OP_INIT_SET_BG_COLOR(r, g, b, a); + + return fb->append_command(fb, &cmd); +} /** * @brief Get font size. @@ -349,6 +986,16 @@ int cfb_get_numof_fonts(void); /** * @brief Initialize display * + * Specify the buffer via param. + * If transfer_buf specifies a buffer large enough to store the entire screen's data, + * the drawing operation is performed immediately without using the command buffer. + * If the buffer specified by transfer_buf is not large enough to store the entire screen's data, + * the drawing operations are stored in the command buffer and executed by calling cfb_finalize. + * In this case, the screen is displayed by repeatedly drawing and transferring the area + * that can be contained in the buffer size multiple times. + * Therefore, command_buf specifies a buffer large enough to hold the drawing operation + * you want to perform. + * * @param disp Pointer to display instance to initialize * @param param Pointer to display initialize parameter * diff --git a/samples/subsys/display/cfb/Kconfig b/samples/subsys/display/cfb/Kconfig index cfd7818e0fa2613..f64de3054cb29f9 100644 --- a/samples/subsys/display/cfb/Kconfig +++ b/samples/subsys/display/cfb/Kconfig @@ -9,4 +9,8 @@ config CFB_SAMPLE_TRANSFER_BUFFER_SIZE help Dynamically allocate display object if set 0 to this option, +config CFB_SAMPLE_COMMAND_BUFFER_SIZE + int "Buffer size for store commands" + default 0 + source "Kconfig.zephyr" diff --git a/samples/subsys/display/cfb/boards/longan_nano.conf b/samples/subsys/display/cfb/boards/longan_nano.conf new file mode 100644 index 000000000000000..f04c3049284b487 --- /dev/null +++ b/samples/subsys/display/cfb/boards/longan_nano.conf @@ -0,0 +1,3 @@ +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_CFB_SAMPLE_TRANSFER_BUFFER_SIZE=6400 +CONFIG_CFB_SAMPLE_COMMAND_BUFFER_SIZE=128 diff --git a/samples/subsys/display/cfb/src/main.c b/samples/subsys/display/cfb/src/main.c index 80795895d0b9857..215f595dbaac5b7 100644 --- a/samples/subsys/display/cfb/src/main.c +++ b/samples/subsys/display/cfb/src/main.c @@ -11,6 +11,7 @@ #if CONFIG_CFB_SAMPLE_TRANSFER_BUFFER_SIZE != 0 static uint8_t transfer_buffer[CONFIG_CFB_SAMPLE_TRANSFER_BUFFER_SIZE]; +static uint8_t command_buffer[CONFIG_CFB_SAMPLE_COMMAND_BUFFER_SIZE]; #endif int main(void) @@ -30,6 +31,8 @@ int main(void) .dev = dev, .transfer_buf = transfer_buffer, .transfer_buf_size = sizeof(transfer_buffer), + .command_buf = command_buffer, + .command_buf_size = sizeof(transfer_buffer), }; #endif diff --git a/samples/subsys/display/cfb_custom_font/Kconfig b/samples/subsys/display/cfb_custom_font/Kconfig index ed2a587a1c6299a..0d621f05e346d58 100644 --- a/samples/subsys/display/cfb_custom_font/Kconfig +++ b/samples/subsys/display/cfb_custom_font/Kconfig @@ -7,4 +7,8 @@ config CFB_CUSTOM_FONT_SAMPLE_TRANSFER_BUFFER_SIZE int "Buffer size for image transfer" default 0 +config CFB_CUSTOM_FONT_SAMPLE_COMMAND_BUFFER_SIZE + int "Buffer size for store commands" + default 0 + source "Kconfig.zephyr" diff --git a/samples/subsys/display/cfb_custom_font/boards/longan_nano.conf b/samples/subsys/display/cfb_custom_font/boards/longan_nano.conf new file mode 100644 index 000000000000000..f04c3049284b487 --- /dev/null +++ b/samples/subsys/display/cfb_custom_font/boards/longan_nano.conf @@ -0,0 +1,3 @@ +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_CFB_SAMPLE_TRANSFER_BUFFER_SIZE=6400 +CONFIG_CFB_SAMPLE_COMMAND_BUFFER_SIZE=128 diff --git a/samples/subsys/display/cfb_custom_font/src/main.c b/samples/subsys/display/cfb_custom_font/src/main.c index 994fee7748b3feb..807eedec65253ae 100644 --- a/samples/subsys/display/cfb_custom_font/src/main.c +++ b/samples/subsys/display/cfb_custom_font/src/main.c @@ -13,6 +13,7 @@ #if CONFIG_CFB_CUSTOM_FONT_SAMPLE_TRANSFER_BUFFER_SIZE != 0 static uint8_t transfer_buffer[CONFIG_CFB_CUSTOM_FONT_SAMPLE_TRANSFER_BUFFER_SIZE]; +static uint8_t command_buffer[CONFIG_CFB_CUSTOM_FONT_SAMPLE_COMMAND_BUFFER_SIZE]; #endif int main(void) @@ -27,6 +28,8 @@ int main(void) .dev = dev, .transfer_buf = transfer_buffer, .transfer_buf_size = sizeof(transfer_buffer), + .command_buf = command_buffer, + .command_buf_size = sizeof(command_buffer), }; #endif diff --git a/samples/subsys/display/cfb_shell/boards/longan_nano.conf b/samples/subsys/display/cfb_shell/boards/longan_nano.conf new file mode 100644 index 000000000000000..f04c3049284b487 --- /dev/null +++ b/samples/subsys/display/cfb_shell/boards/longan_nano.conf @@ -0,0 +1,3 @@ +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_CFB_SAMPLE_TRANSFER_BUFFER_SIZE=6400 +CONFIG_CFB_SAMPLE_COMMAND_BUFFER_SIZE=128 diff --git a/subsys/fb/cfb.c b/subsys/fb/cfb.c index c79e29528f8a5b5..b2f6fbe668bace6 100644 --- a/subsys/fb/cfb.c +++ b/subsys/fb/cfb.c @@ -10,6 +10,7 @@ #include #include #include +#include #define LOG_LEVEL CONFIG_CFB_LOG_LEVEL #include @@ -21,6 +22,16 @@ STRUCT_SECTION_END_EXTERN(cfb_font); #define LSB_BIT_MASK(x) BIT_MASK(x) #define MSB_BIT_MASK(x) (BIT_MASK(x) << (8 - x)) +/** + * Command List processing mode + */ +enum command_process_mode { + FINALIZE, + IMMEDIATE, + CLEAR_COMMANDS, + CLEAR_DISPLAY, +}; + static inline uint8_t byte_reverse(uint8_t b) { b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; @@ -526,69 +537,17 @@ static void draw_text(struct cfb_framebuffer *fb, const char *const str, int16_t } } -int cfb_draw_point(struct cfb_framebuffer *fb, const struct cfb_position *pos) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - draw_point(fb, pos->x, pos->y, disp->fg_color); - - return 0; -} - -int cfb_draw_line(struct cfb_framebuffer *fb, const struct cfb_position *start, - const struct cfb_position *end) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - draw_line(fb, start->x, start->y, end->x, end->y, disp->fg_color); - - return 0; -} - -int cfb_draw_rect(struct cfb_framebuffer *fb, const struct cfb_position *start, - const struct cfb_position *end) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - draw_line(fb, start->x, start->y, end->x, start->y, disp->fg_color); - draw_line(fb, end->x, start->y, end->x, end->y, disp->fg_color); - draw_line(fb, end->x, end->y, start->x, end->y, disp->fg_color); - draw_line(fb, start->x, end->y, start->x, start->y, disp->fg_color); - - return 0; -} - -int cfb_draw_text(struct cfb_framebuffer *fb, const char *const str, int16_t x, int16_t y) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - draw_text(fb, str, x, y, false, font_get(disp->font_idx), disp->kerning, disp->fg_color, - disp->bg_color); - - return 0; -} - -int cfb_print(struct cfb_framebuffer *fb, const char *const str, int16_t x, int16_t y) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - draw_text(fb, str, x, y, true, font_get(disp->font_idx), disp->kerning, disp->fg_color, - disp->bg_color); - - return 0; -} - -int cfb_invert_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t width, - uint16_t height) +static void invert_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t width, + uint16_t height) { const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0); if ((x + width) < fb_left(fb) || x >= fb_right(fb)) { - return 0; + return; } if ((y + height) < fb_top(fb) || y >= fb_bottom(fb)) { - return 0; + return; } x -= fb->pos.x; @@ -688,57 +647,420 @@ int cfb_invert_area(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t w } } } +} - return 0; +static void execute_command(struct cfb_framebuffer *fb, const struct cfb_command_param *param, + struct cfb_draw_settings *settings) +{ + switch (param->op) { + case CFB_OP_FILL: + fill_fb(fb, settings->bg_color, bytes_per_pixel(fb->pixel_format)); + break; + case CFB_OP_DRAW_POINT: + draw_point(fb, param->draw_figure.start.x, param->draw_figure.start.y, + settings->fg_color); + break; + case CFB_OP_DRAW_LINE: + draw_line(fb, param->draw_figure.start.x, param->draw_figure.start.y, + param->draw_figure.end.x, param->draw_figure.end.y, settings->fg_color); + break; + case CFB_OP_DRAW_RECT: + draw_line(fb, param->draw_figure.start.x, param->draw_figure.start.y, + param->draw_figure.end.x, param->draw_figure.start.y, settings->fg_color); + draw_line(fb, param->draw_figure.end.x, param->draw_figure.start.y, + param->draw_figure.end.x, param->draw_figure.end.y, settings->fg_color); + draw_line(fb, param->draw_figure.end.x, param->draw_figure.end.y, + param->draw_figure.start.x, param->draw_figure.end.y, settings->fg_color); + draw_line(fb, param->draw_figure.start.x, param->draw_figure.end.y, + param->draw_figure.start.x, param->draw_figure.start.y, + settings->fg_color); + break; + case CFB_OP_DRAW_TEXT: + draw_text(fb, param->draw_text.str, param->draw_text.pos.x, param->draw_text.pos.y, + false, font_get(settings->font_idx), settings->kerning, + settings->fg_color, settings->bg_color); + break; + case CFB_OP_PRINT: + draw_text(fb, param->draw_text.str, param->draw_text.pos.x, param->draw_text.pos.y, + true, font_get(settings->font_idx), settings->kerning, settings->fg_color, + settings->bg_color); + break; + case CFB_OP_DRAW_TEXT_REF: + draw_text(fb, param->draw_text.str, param->draw_text.pos.x, param->draw_text.pos.y, + false, font_get(settings->font_idx), settings->kerning, + settings->fg_color, settings->bg_color); + break; + case CFB_OP_PRINT_REF: + draw_text(fb, param->draw_text.str, param->draw_text.pos.x, param->draw_text.pos.y, + true, font_get(settings->font_idx), settings->kerning, settings->fg_color, + settings->bg_color); + break; + case CFB_OP_INVERT_AREA: + invert_area(fb, param->invert_area.x, param->invert_area.y, param->invert_area.w, + param->invert_area.h); + break; + case CFB_OP_SWAP_FG_BG_COLOR: + const uint32_t tmp_fg = settings->fg_color; + + settings->fg_color = settings->bg_color; + settings->bg_color = tmp_fg; + break; + case CFB_OP_SET_FONT: + settings->font_idx = param->set_font.font_idx; + break; + case CFB_OP_SET_KERNING: + settings->kerning = param->set_kerning.kerning; + break; + case CFB_OP_SET_FG_COLOR: + settings->fg_color = rgba_to_color(fb->pixel_format, param->set_color.red, + param->set_color.green, param->set_color.blue, + param->set_color.alpha); + break; + case CFB_OP_SET_BG_COLOR: + settings->bg_color = rgba_to_color(fb->pixel_format, param->set_color.red, + param->set_color.green, param->set_color.blue, + param->set_color.alpha); + break; + default: + return; + } } -int cfb_clear(struct cfb_framebuffer *fb, bool clear_display) +static bool display_is_last_iterator(struct cfb_command_iterator *ite) +{ + return !ite->node && + (!ite->disp->cmd.buf || + ((void *)ite->param >= (void *)(ite->disp->cmd.buf + ite->disp->cmd.pos))); +} + +/** + * Advances the iterator by one. + * + * This iterator handles linked list structures and buffers in a unified manner. + * When the end of the linked list is reached, it scans the elements stored in the buffer + * and returns NULL when the last element is reached. + * This function is called via the next function pointer of cfb_command_iterator. + * + * @param ite current iterator + * @return iterator that is pointing next node + */ +static struct cfb_command_iterator *display_next_iterator(struct cfb_command_iterator *ite) +{ + if (ite->node != NULL) { + struct cfb_command *pcmd; + + ite->node = ite->node->next; + if (ite->node == NULL) { + ite->param = (void *)ite->disp->cmd.buf; + } else { + pcmd = CONTAINER_OF(ite->node, struct cfb_command, node); + ite->param = &pcmd->param; + } + } else { + uint8_t *buf_ptr = (uint8_t *)(ite->param + 1); + + ite->node = NULL; + if (ite->param->op == CFB_OP_DRAW_TEXT || ite->param->op == CFB_OP_PRINT) { + buf_ptr += strlen(buf_ptr) + 1; + } + + ite->param = (struct cfb_command_param *)buf_ptr; + } + + return ite; +} + +/** + * Initialize iterator. + * + * This function initialize command-list iterator. + * Iterator traverse linked lists and buffers in a same manner. + * This function sets the iterator to the beginning of the linked list. + * The iterator cannot process concurrently. + * + * @param fb framebuffer pointer that is linked to display + * @return Pointer that reference the iterator. + */ +static struct cfb_command_iterator *display_init_iterator(struct cfb_framebuffer *fb) { struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); + struct cfb_command *pcmd; + + disp->iterator.disp = disp; + disp->iterator.node = sys_slist_peek_head(&disp->cmd_list); + pcmd = CONTAINER_OF(disp->iterator.node, struct cfb_command, node); + disp->iterator.param = &pcmd->param; + disp->iterator.next = display_next_iterator; + disp->iterator.is_last = display_is_last_iterator; + + return &disp->iterator; +} - if (!fb || !fb->buf) { - return -ENODEV; +/** + * Executes a list of commands. + * Called by cfb_finalize and cfb_clear. + * When called from cfb_clear, only apply the settings without executing the drawing command. + * + * @param fb Framebuffer and rendering info + * @param x The start x pos of rendering rect + * @param y The start y pos of rendering rect + * @param w The width of rendering rect + * @param h The height of rendering rect + * @param settings Pointer to draw settings + * @param mode Execution mode + * + * @return negative value if failed, otherwise 0 + */ +static int display_process_commands(struct cfb_display *disp, uint16_t x, uint16_t y, uint16_t w, + uint16_t h, struct cfb_draw_settings *settings, + enum command_process_mode mode) +{ + struct display_buffer_descriptor desc; + const uint16_t draw_width = x + w; + const uint16_t draw_height = y + h; + const uint16_t start_x = x; + const struct cfb_draw_settings restore = *settings; + int err = 0; + + if (disp->fb.size < (w * bytes_per_pixel(disp->fb.pixel_format))) { + w = DIV_ROUND_UP(w * bytes_per_pixel(disp->fb.pixel_format), + DIV_ROUND_UP(w * bytes_per_pixel(disp->fb.pixel_format), + disp->fb.size)) / + bytes_per_pixel(disp->fb.pixel_format); + h = pixels_per_tile(disp->fb.pixel_format); + } else { + h = MIN(disp->fb.size / (w * bytes_per_pixel(disp->fb.pixel_format)) * + pixels_per_tile(disp->fb.pixel_format), + h); } - fill_fb(&disp->fb, disp->bg_color, bytes_per_pixel(disp->fb.pixel_format)); + for (; y < draw_height; y += h) { + for (x = start_x; x < draw_width; x += w) { + *settings = restore; + for (struct cfb_command_iterator *ite = disp->fb.init_iterator(&disp->fb); + !ite->is_last(ite); ite = ite->next(ite)) { + disp->fb.pos.x = x; + disp->fb.pos.y = y; + disp->fb.width = w; + disp->fb.height = h; + + /* Process only settings change commands if clear. */ + if (mode >= CLEAR_COMMANDS) { + if ((ite->param->op != CFB_OP_FILL) && + (ite->param->op != CFB_OP_SET_FONT) && + (ite->param->op != CFB_OP_SET_KERNING) && + (ite->param->op != CFB_OP_SET_FG_COLOR) && + (ite->param->op != CFB_OP_SET_BG_COLOR) && + (ite->param->op != CFB_OP_SWAP_FG_BG_COLOR)) { + continue; + } + } + + execute_command(&disp->fb, ite->param, settings); + } - if (clear_display) { - cfb_finalize(fb); + /* Don't update display on clear-command case */ + if (mode != FINALIZE && mode != CLEAR_DISPLAY) { + continue; + } + + desc.buf_size = disp->fb.size; + desc.height = MIN(h, disp->fb.res.y - y); + desc.width = MIN(w, disp->fb.res.x - x); + desc.pitch = MIN(w, disp->fb.res.x - x); + + err = display_write(disp->dev, x, y, &desc, disp->fb.buf); + + if (err) { + LOG_DBG("display_write(%d %d %d %d) size: %d: err=%d", x, y, w, h, + disp->fb.size, err); + return err; + } + } } return 0; } -int cfb_invert(struct cfb_framebuffer *fb) +/** + * Set up an initialization command to be executed every time partial frame buffer drawing. + * + * @param disp display structure + */ +static void display_append_init_commands(struct cfb_display *disp) +{ + const struct cfb_draw_settings *settings = &disp->settings; + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + uint8_t a = 0; + + disp->init_cmds[CFB_INIT_CMD_SET_FONT].param.op = CFB_OP_SET_FONT; + disp->init_cmds[CFB_INIT_CMD_SET_FONT].param.set_font.font_idx = settings->font_idx; + + disp->init_cmds[CFB_INIT_CMD_SET_KERNING].param.op = CFB_OP_SET_KERNING; + disp->init_cmds[CFB_INIT_CMD_SET_KERNING].param.set_kerning.kerning = settings->kerning; + + color_to_rgba(disp->fb.pixel_format, settings->fg_color, &r, &g, &b, &a); + disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].param.op = CFB_OP_SET_FG_COLOR; + disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].param.set_color.red = r; + disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].param.set_color.green = g; + disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].param.set_color.blue = b; + disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].param.set_color.alpha = a; + + color_to_rgba(disp->fb.pixel_format, settings->bg_color, &r, &g, &b, &a); + disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].param.op = CFB_OP_SET_BG_COLOR; + disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].param.set_color.red = r; + disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].param.set_color.green = g; + disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].param.set_color.blue = b; + disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].param.set_color.alpha = a; + + disp->init_cmds[CFB_INIT_CMD_FILL].param.op = CFB_OP_FILL; + + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_SET_FONT].node); + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_SET_KERNING].node); + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_SET_FG_COLOR].node); + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_SET_BG_COLOR].node); + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_FILL].node); +} + +/** + * Run finalizing process. + * + * This function is called via function-pointer in the cfb_framebuffer + * struct on calling cfb_finalize. + * + * @param fb framebuffer pointer that is linked to display + * @param x X pos + * @param y Y pos + * @param w width + * @param h height + * + * @return negative value if failed, otherwise 0 + */ +static int display_finalize(struct cfb_framebuffer *fb, int16_t x, int16_t y, uint16_t width, + uint16_t height) { struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - const uint32_t tmp_fg = disp->fg_color; - disp->fg_color = disp->bg_color; - disp->bg_color = tmp_fg; + return display_process_commands(disp, MAX(x, 0), MAX(y, 0), MIN(width, fb->res.x - x), + MIN(height, fb->res.y - y), &disp->settings, FINALIZE); +} - cfb_invert_area(fb, 0, 0, fb->width, fb->height); +/** + * Clear commands and display. + * + * This function is called via function-pointer in the cfb_framebuffer + * struct on calling cfb_clear. + * + * @param fb framebuffer pointer that is linked to display + * @param clear_display Clears the display as well as the command buffer + * + * @return negative value if failed, otherwise 0 + */ +static int display_clear(struct cfb_framebuffer *fb, bool clear_display) +{ + struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); + int err; - return 0; + if (disp->fb.size < fb_screen_buf_size(&disp->fb)) { + /* if clear processing, filling with the background color is done last. */ + sys_slist_find_and_remove(&disp->cmd_list, + &disp->init_cmds[CFB_INIT_CMD_FILL].node); + err = display_process_commands(disp, 0, 0, fb->res.x, fb->res.y, &disp->settings, + CLEAR_COMMANDS); + } else { + sys_slist_init(&disp->cmd_list); + } + + sys_slist_init(&disp->cmd_list); + sys_slist_append(&disp->cmd_list, &disp->init_cmds[CFB_INIT_CMD_FILL].node); + err = display_process_commands(disp, 0, 0, fb->res.x, fb->res.y, &disp->settings, + clear_display ? CLEAR_DISPLAY : CLEAR_COMMANDS); + + /* reset command list */ + disp->cmd.pos = 0; + memset(disp->cmd.buf, 0, disp->cmd.size); + sys_slist_init(&disp->cmd_list); + if (disp->fb.size < fb_screen_buf_size(&disp->fb)) { + display_append_init_commands(disp); + } + + return err; } -int cfb_finalize(struct cfb_framebuffer *fb) +/** + * Immediately render to buffer + * + * @param fb framebuffer pointer that is linked to display + * + * @return negative value if failed, otherwise 0 + */ +static int display_immediate(struct cfb_display *disp) { - struct display_buffer_descriptor desc; - struct cfb_display *disp; + int err = display_process_commands(disp, 0, 0, disp->fb.res.x, disp->fb.res.y, + &disp->settings, IMMEDIATE); - if (!fb || !fb->buf) { - return -ENODEV; + sys_slist_init(&disp->cmd_list); + + return err; +} + +static int display_append_command_to_buffer(struct cfb_display *disp, struct cfb_command *cmd) +{ + const bool store_text = + (cmd->param.op == CFB_OP_DRAW_TEXT || cmd->param.op == CFB_OP_PRINT); + const size_t str_len = store_text ? strlen(cmd->param.draw_text.str) + 1 : 0; + const size_t allocate_size = sizeof(struct cfb_command) + str_len + 1; + struct cfb_command_param *bufparam = + (struct cfb_command_param *)&disp->cmd.buf[disp->cmd.pos]; + + if (disp->cmd.size < disp->cmd.pos + allocate_size) { + return -ENOBUFS; } - disp = CONTAINER_OF(fb, struct cfb_display, fb); + memcpy(&disp->cmd.buf[disp->cmd.pos], &cmd->param, sizeof(struct cfb_command_param)); + + disp->cmd.pos += sizeof(struct cfb_command_param); - desc.buf_size = fb->size; - desc.width = fb->width; - desc.height = fb->height; - desc.pitch = fb->width; + if (store_text) { + bufparam->draw_text.str = (const char *)&disp->cmd.buf[disp->cmd.pos]; + memcpy(&disp->cmd.buf[disp->cmd.pos], cmd->param.draw_text.str, str_len); + disp->cmd.pos += str_len; + disp->cmd.buf[disp->cmd.pos] = '\0'; + } - return display_write(disp->dev, 0, 0, &desc, fb->buf); + return 0; +} + +/** + * Append a command to buffer or list + * + * This function is called via function-pointer in the cfb_framebuffer + * struct on calling cfb_append_command and rendering functions. + * + * If the entire screen buffer is allocated, the command is processed immediately + * and written to the buffer. + * + * @param fb Framebuffer pointer that is linked to display + * @param cmd A command to append buffer or list + * + * @retval -ENOBUFS Not enough buffers remain to append the command. + * @retval 0 Succeedsed + */ +static int display_append_command(struct cfb_framebuffer *fb, struct cfb_command *cmd) +{ + struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); + + if (!disp->cmd.buf) { + sys_slist_append(&disp->cmd_list, &cmd->node); + if (disp->fb.size >= fb_screen_buf_size(&disp->fb)) { + display_immediate(disp); + } + return 0; + } else { + return display_append_command_to_buffer(disp, cmd); + } } int cfb_get_display_parameter(const struct cfb_display *disp, enum cfb_display_param param) @@ -768,19 +1090,6 @@ int cfb_get_display_parameter(const struct cfb_display *disp, enum cfb_display_p return 0; } -int cfb_set_font(struct cfb_framebuffer *fb, uint8_t idx) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - if (idx >= cfb_get_numof_fonts()) { - return -EINVAL; - } - - disp->font_idx = idx; - - return 0; -} - int cfb_get_font_size(uint8_t idx, uint8_t *width, uint8_t *height) { if (idx >= cfb_get_numof_fonts()) { @@ -798,33 +1107,6 @@ int cfb_get_font_size(uint8_t idx, uint8_t *width, uint8_t *height) return 0; } -int cfb_set_kerning(struct cfb_framebuffer *fb, int8_t kerning) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - disp->kerning = kerning; - - return 0; -} - -int cfb_set_bg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - disp->bg_color = rgba_to_color(fb->pixel_format, r, g, b, a); - - return 0; -} - -int cfb_set_fg_color(struct cfb_framebuffer *fb, uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - struct cfb_display *disp = CONTAINER_OF(fb, struct cfb_display, fb); - - disp->fg_color = rgba_to_color(fb->pixel_format, r, g, b, a); - - return 0; -} - int cfb_get_numof_fonts(void) { static int numof_fonts; @@ -843,17 +1125,21 @@ int cfb_display_init(struct cfb_display *disp, const struct cfb_display_init_par display_get_capabilities(param->dev, &cfg); disp->dev = param->dev; - disp->font_idx = 0U; - disp->kerning = 0U; + disp->settings.font_idx = 0U; + disp->settings.kerning = 0U; if (cfg.current_pixel_format == PIXEL_FORMAT_MONO10) { - disp->bg_color = 0xFFFFFFFFU; - disp->fg_color = 0x0U; + disp->settings.bg_color = 0xFFFFFFFFU; + disp->settings.fg_color = 0x0U; } else { - disp->fg_color = 0xFFFFFFFFU; - disp->bg_color = 0x0U; + disp->settings.fg_color = 0xFFFFFFFFU; + disp->settings.bg_color = 0x0U; } + disp->cmd.buf = param->command_buf; + disp->cmd.size = param->command_buf_size; + disp->cmd.pos = 0U; + disp->fb.pixel_format = cfg.current_pixel_format; disp->fb.screen_info = cfg.screen_info; disp->fb.pos.x = 0; @@ -865,7 +1151,37 @@ int cfb_display_init(struct cfb_display *disp, const struct cfb_display_init_par disp->fb.size = param->transfer_buf_size; disp->fb.buf = param->transfer_buf; - fill_fb(&disp->fb, disp->bg_color, bytes_per_pixel(disp->fb.pixel_format)); + disp->fb.finalize = display_finalize; + disp->fb.clear = display_clear; + disp->fb.append_command = display_append_command; + disp->fb.init_iterator = display_init_iterator; + + if (!disp->fb.buf) { + return -EINVAL; + } + + if (disp->fb.size < bytes_per_pixel(cfg.current_pixel_format)) { + return -EINVAL; + } + + if (param->transfer_buf_size < fb_screen_buf_size(&disp->fb)) { + if (!disp->cmd.buf) { + return -EINVAL; + } + } else { + disp->cmd.buf = NULL; + disp->cmd.size = 0; + } + + fill_fb(&disp->fb, disp->settings.bg_color, bytes_per_pixel(disp->fb.pixel_format)); + + sys_slist_init(&disp->cmd_list); + + if (disp->cmd.buf) { + memset(disp->cmd.buf, 0, disp->cmd.size); + } + + display_append_init_commands(disp); return 0; } diff --git a/tests/subsys/display/cfb/basic/Kconfig b/tests/subsys/display/cfb/basic/Kconfig index b1a1a2c9e034b18..2fb70dce8bf1634 100644 --- a/tests/subsys/display/cfb/basic/Kconfig +++ b/tests/subsys/display/cfb/basic/Kconfig @@ -8,4 +8,8 @@ config TEST_TRANSFER_BUF_SIZE int "transfer buffer size" default 0 +config TEST_COMMAND_BUF_SIZE + int "command buffer size" + default 0 + source "Kconfig.zephyr" diff --git a/tests/subsys/display/cfb/basic/src/utils.c b/tests/subsys/display/cfb/basic/src/utils.c index 4891bc23a84a076..da0dc9ee9610436 100644 --- a/tests/subsys/display/cfb/basic/src/utils.c +++ b/tests/subsys/display/cfb/basic/src/utils.c @@ -21,8 +21,8 @@ const uint32_t display_height = DT_PROP(DT_CHOSEN(zephyr_display), height); uint8_t read_buffer[DT_PROP(DT_CHOSEN(zephyr_display), width) * DT_PROP(DT_CHOSEN(zephyr_display), height) * 4]; #if CONFIG_TEST_TRANSFER_BUF_SIZE != 0 -uint8_t transfer_buffer[DT_PROP(DT_CHOSEN(zephyr_display), width) * - DT_PROP(DT_CHOSEN(zephyr_display), height) * 4]; +static uint8_t transfer_buffer[CONFIG_TEST_CFB_BASIC_TRANSFER_BUF_SIZE]; +static uint8_t command_buffer[CONFIG_TEST_CFB_BASIC_COMMAND_BUF_SIZE]; static struct cfb_display disp; #endif @@ -61,6 +61,8 @@ struct cfb_display *display_init(void) .dev = dev, .transfer_buf = transfer_buffer, .transfer_buf_size = sizeof(transfer_buffer), + .command_buf = command_buffer, + .command_buf_size = sizeof(command_buffer), }; #else struct cfb_display *pdisp; diff --git a/tests/subsys/display/cfb/basic/testcase.yaml b/tests/subsys/display/cfb/basic/testcase.yaml index 86df41ded855a5c..c3082fde206219f 100644 --- a/tests/subsys/display/cfb/basic/testcase.yaml +++ b/tests/subsys/display/cfb/basic/testcase.yaml @@ -87,3 +87,27 @@ tests: - CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n - CONFIG_SDL_DISPLAY_MONO_MSB_FIRST=n - CONFIG_TEST_MSB_FIRST_FONT=y + cfb.basic.mono01.transfer_buffersize_1: + filter: dt_compat_enabled("zephyr,sdl-dc") + extra_configs: + - CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_MONO01=y + - CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n + cfb.basic.mono01.transfer_buffersize_320: + filter: dt_compat_enabled("zephyr,sdl-dc") + extra_configs: + - CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_MONO01=y + - CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n + cfb.basic.argb_8888.transfer_buffersize_600: + filter: dt_compat_enabled("zephyr,sdl-dc") + extra_configs: + - CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_ARGB_8888=y + - CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n + - CONFIG_TEST_TRANSFER_BUF_SIZE=600 + - CONFIG_TEST_COMMAND_BUF_SIZE=512 + cfb.basic.rgb_565.transfer_buffersize_300: + filter: dt_compat_enabled("zephyr,sdl-dc") + extra_configs: + - CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_RGB_565=y + - CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n + - CONFIG_TEST_TRANSFER_BUF_SIZE=300 + - CONFIG_TEST_COMMAND_BUF_SIZE=512