Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion include/zephyr/display/cfb.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,21 @@ 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 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.
Expand Down
157 changes: 136 additions & 21 deletions subsys/fb/cfb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ' ';
Expand All @@ -105,30 +106,85 @@ 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;
}

int cfb_print(const struct device *dev, char *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;
Expand All @@ -144,13 +200,13 @@ int cfb_print(const struct device *dev, char *str, 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)) {
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);
x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, wrap);
}
return 0;
}
Expand All @@ -159,18 +215,37 @@ int cfb_print(const struct device *dev, char *str, uint16_t x, uint16_t y)
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)
{
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");

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;
}
Expand All @@ -180,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;

fb->buf[index] = ~fb->buf[index];
for (size_t j = y; j < (y + height); j++) {
/*
* Process inversion in the y direction
* by separating per 8-line boundaries.
*/

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);
}
}
}

Expand Down Expand Up @@ -240,6 +351,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;
Expand All @@ -252,6 +364,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);
Expand Down
70 changes: 64 additions & 6 deletions subsys/fb/cfb_shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define HELP_NONE "[none]"
#define HELP_INIT "call \"cfb init\" first"
#define HELP_PRINT "<col: pos> <row: pos> \"<text>\""
#define HELP_INVERT "[<x> <y> <width> <height>]"

static const struct device *const dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
Expand Down Expand Up @@ -119,6 +120,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;
Expand Down Expand Up @@ -266,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");

Expand Down Expand Up @@ -488,6 +540,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),
Expand All @@ -496,10 +553,11 @@ 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, "<idx>", cmd_set_font, 2, 0),
SHELL_CMD_ARG(set_kerning, NULL, "<kerning>", 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),
SHELL_CMD(draw, &sub_cmd_draw, "drawing text", NULL),
SHELL_CMD_ARG(clear, NULL, HELP_NONE, cmd_clear, 1, 0),
SHELL_SUBCMD_SET_END
);
Expand Down