Skip to content
Draft
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
# A link option will be used with esp-idf 4.x
if (IDF_VERSION_MAJOR GREATER_EQUAL 5)
set(OPTIONAL_WHOLE_ARCHIVE WHOLE_ARCHIVE)
set(OPTIONAL_ESP_LCD_REQUIRES "esp_lcd")
set(OPTIONAL_RGB_LCD_SRCS "rgb_lcd_display_driver.c")
else()
set(OPTIONAL_WHOLE_ARCHIVE "")
set(OPTIONAL_ESP_LCD_REQUIRES "")
set(OPTIONAL_RGB_LCD_SRCS "")
endif()

idf_component_register(SRCS
Expand All @@ -44,14 +48,15 @@ idf_component_register(SRCS
"memory_display_driver.c"
"oled_commands.c"
"oled_display_driver.c"
${OPTIONAL_RGB_LCD_SRCS}
"spi_dc_driver.c"
"spi_display.c"
"ufontlib.c"
"backlight_gpio.c"
"image_helpers.c"
"spng.c"
"miniz.c"
PRIV_REQUIRES "libatomvm" "avm_sys" "avm_builtins" "driver" "sdmmc" "vfs" "fatfs"
PRIV_REQUIRES "libatomvm" "avm_sys" "avm_builtins" "driver" ${OPTIONAL_ESP_LCD_REQUIRES} "sdmmc" "vfs" "fatfs"
${OPTIONAL_WHOLE_ARCHIVE}
)

Expand Down
2 changes: 2 additions & 0 deletions README.Md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ software dithering
* `sharp,memory-lcd`: Sharp Memory LCDs - 400x240, 1-bit monochrome
* `solomon-systech,ssd1306`: Solomon Systech SSD1306 - 128x64, 1-bit monochrome
* `sino-wealth,sh1106`: Sino Wealth SH1106 - 128x64, 1-bit monochrome
* `esp_lcd,rgb` / `waveshare,esp32-s3-touch-lcd-7`: RGB LCD panels using the ESP32-S3 LCD peripheral,
requires ESP-IDF 5 or later, 16-bit colors (RGB565)

[SDL Linux display](sdl_display/) is also supported and can be built as an AtomVM plugin.

Expand Down
18 changes: 17 additions & 1 deletion dcs_lcd_color.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,16 @@ static inline uint8_t rgba8888_get_alpha(uint32_t color)

static inline uint16_t rgba8888_color_to_rgb565(uint32_t color)
{
uint8_t r = color >> 24;
uint8_t r = (color >> 24) & 0xFF;
uint8_t g = (color >> 16) & 0xFF;
uint8_t b = (color >> 8) & 0xFF;

return (((uint16_t) (r >> 3)) << 11) | (((uint16_t) (g >> 2)) << 5) | ((uint16_t) b >> 3);
}

static inline uint16_t display_color_to_rgb565(uint32_t color)
{
uint8_t r = (color >> 24) & 0xFF;
uint8_t g = (color >> 16) & 0xFF;
uint8_t b = (color >> 8) & 0xFF;

Expand All @@ -55,6 +64,13 @@ static inline uint16_t rgb565_color_to_surface(uint16_t color16)
return (uint16_t) SPI_SWAP_DATA_TX(color16, 16);
}

static inline uint16_t display_color_to_surface(uint32_t color)
{
uint16_t color16 = display_color_to_rgb565(color);

return rgb565_color_to_surface(color16);
}

static inline uint16_t uint32_color_to_surface(uint32_t color)
{
uint16_t color16 = rgba8888_color_to_rgb565(color);
Expand Down
170 changes: 158 additions & 12 deletions dcs_lcd_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,144 @@ int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen,
return line_len;
}

static bool dcs_lcd_resolve_pixel_rgb565(const struct DCSLCDScreen *screen,
int xpos, int ypos, BaseDisplayItem items[], size_t items_len, size_t start_index, uint16_t *out_color);

static bool dcs_lcd_image_pixel_rgb565(const struct DCSLCDScreen *screen,
BaseDisplayItem *item, int xpos, int ypos, BaseDisplayItem items[], size_t items_len, size_t item_index, uint16_t *out_color)
{
int x = item->x;
int y = item->y;
int rel_x = xpos - x;
int rel_y = ypos - y;
uint32_t *pixels = ((uint32_t *) item->data.image_data.pix) + (rel_y * item->width) + rel_x;
uint32_t img_pixel = READ_32_UNALIGNED(pixels);
uint8_t alpha = rgba8888_get_alpha(img_pixel);

if (alpha == 0xFF) {
*out_color = rgba8888_color_to_rgb565(img_pixel);
return true;
}
if (item->brcolor != 0) {
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t bgcolor = display_color_to_rgb565(item->brcolor);
*out_color = alpha_blend_rgb565(color, bgcolor, alpha);
return true;
}
if (alpha > 0) {
uint16_t lower = 0;
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
(void) dcs_lcd_resolve_pixel_rgb565(screen, xpos, ypos, items, items_len, item_index + 1, &lower);
*out_color = alpha_blend_rgb565(color, lower, alpha);
return true;
}

return dcs_lcd_resolve_pixel_rgb565(screen, xpos, ypos, items, items_len, item_index + 1, out_color);
}

static bool dcs_lcd_scaled_image_pixel_rgb565(const struct DCSLCDScreen *screen,
BaseDisplayItem *item, int xpos, int ypos, BaseDisplayItem items[], size_t items_len, size_t item_index, uint16_t *out_color)
{
int x = item->x;
int y = item->y;
int source_x = item->source_x + ((xpos - x) / item->x_scale);
int source_y = item->source_y + ((ypos - y) / item->y_scale);
int img_width = item->data.image_data_with_size.width;
uint32_t *pixels = ((uint32_t *) item->data.image_data_with_size.pix) + (source_y * img_width) + source_x;
uint32_t img_pixel = READ_32_UNALIGNED(pixels);
uint8_t alpha = rgba8888_get_alpha(img_pixel);

if (alpha == 0xFF) {
*out_color = rgba8888_color_to_rgb565(img_pixel);
return true;
}
if (item->brcolor != 0) {
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t bgcolor = display_color_to_rgb565(item->brcolor);
*out_color = alpha_blend_rgb565(color, bgcolor, alpha);
return true;
}
if (alpha > 0) {
uint16_t lower = 0;
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
(void) dcs_lcd_resolve_pixel_rgb565(screen, xpos, ypos, items, items_len, item_index + 1, &lower);
*out_color = alpha_blend_rgb565(color, lower, alpha);
return true;
}

return dcs_lcd_resolve_pixel_rgb565(screen, xpos, ypos, items, items_len, item_index + 1, out_color);
}

static bool dcs_lcd_text_pixel_rgb565(const struct DCSLCDScreen *screen,
BaseDisplayItem *item, int xpos, int ypos, BaseDisplayItem items[], size_t items_len, size_t item_index, uint16_t *out_color)
{
int x = item->x;
int y = item->y;
char *text = (char *) item->data.text_data.text;
int char_index = (xpos - x) / CHAR_WIDTH;
char c = text[char_index];
unsigned const char *glyph = fontdata + ((unsigned char) c) * 16;
unsigned char row = glyph[ypos - y];
int k = (xpos - x) % CHAR_WIDTH;

if (row & (1 << (7 - k))) {
*out_color = display_color_to_rgb565(item->data.text_data.fgcolor);
return true;
}
if (item->brcolor != 0) {
*out_color = display_color_to_rgb565(item->brcolor);
return true;
}

return dcs_lcd_resolve_pixel_rgb565(screen, xpos, ypos, items, items_len, item_index + 1, out_color);
}

static bool dcs_lcd_resolve_pixel_rgb565(const struct DCSLCDScreen *screen,
int xpos, int ypos, BaseDisplayItem items[], size_t items_len, size_t start_index, uint16_t *out_color)
{
for (size_t i = start_index; i < items_len; i++) {
BaseDisplayItem *item = &items[i];
if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) {
continue;
}

switch (item->primitive) {
case PrimitiveImage:
if (dcs_lcd_image_pixel_rgb565(screen, item, xpos, ypos, items, items_len, i, out_color)) {
return true;
}
break;
case PrimitiveRect:
*out_color = display_color_to_rgb565(item->brcolor);
return true;
case PrimitiveScaledCroppedImage:
if (dcs_lcd_scaled_image_pixel_rgb565(screen, item, xpos, ypos, items, items_len, i, out_color)) {
return true;
}
break;
case PrimitiveText:
if (dcs_lcd_text_pixel_rgb565(screen, item, xpos, ypos, items, items_len, i, out_color)) {
return true;
}
break;
default:
break;
}
}
return false;
}

int dcs_lcd_draw_image_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item)
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index)
{
int x = item->x;
int y = item->y;

uint16_t bgcolor = 0;
bool visible_bg;
if (item->brcolor != 0) {
bgcolor = rgba8888_color_to_rgb565(item->brcolor);
bgcolor = display_color_to_rgb565(item->brcolor);
visible_bg = true;
} else {
visible_bg = false;
Expand Down Expand Up @@ -83,6 +211,12 @@ int dcs_lcd_draw_image_x(const struct DCSLCDScreen *screen,
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha);
pixmem16[drawn_pixels] = rgb565_color_to_surface(blended);
} else if (alpha > 0) {
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t lower = 0;
(void) dcs_lcd_resolve_pixel_rgb565(screen, xpos + drawn_pixels, ypos, items, items_len, item_index + 1, &lower);
uint16_t blended = alpha_blend_rgb565(color, lower, alpha);
pixmem16[drawn_pixels] = rgb565_color_to_surface(blended);
} else {
return drawn_pixels;
}
Expand All @@ -98,7 +232,7 @@ int dcs_lcd_draw_rect_x(const struct DCSLCDScreen *screen,
{
int x = item->x;
int width = item->width;
uint16_t color = uint32_color_to_surface(item->brcolor);
uint16_t color = display_color_to_surface(item->brcolor);

int drawn_pixels = 0;

Expand All @@ -117,15 +251,16 @@ int dcs_lcd_draw_rect_x(const struct DCSLCDScreen *screen,
}

int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item)
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index)
{
int x = item->x;
int y = item->y;
uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor);
uint16_t fgcolor = display_color_to_surface(item->data.text_data.fgcolor);
uint16_t bgcolor;
bool visible_bg;
if (item->brcolor != 0) {
bgcolor = uint32_color_to_surface(item->brcolor);
bgcolor = display_color_to_surface(item->brcolor);
visible_bg = true;
} else {
visible_bg = false;
Expand Down Expand Up @@ -163,7 +298,11 @@ int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen,
} else if (visible_bg) {
pixmem16[drawn_pixels] = bgcolor;
} else {
return drawn_pixels;
uint16_t lower = 0;
if (!dcs_lcd_resolve_pixel_rgb565(screen, xpos + drawn_pixels, ypos, items, items_len, item_index + 1, &lower)) {
return drawn_pixels;
}
pixmem16[drawn_pixels] = rgb565_color_to_surface(lower);
}
drawn_pixels++;
}
Expand All @@ -172,15 +311,16 @@ int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen,
}

int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item)
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index)
{
int x = item->x;
int y = item->y;

uint16_t bgcolor = 0;
bool visible_bg;
if (item->brcolor != 0) {
bgcolor = rgba8888_color_to_rgb565(item->brcolor);
bgcolor = display_color_to_rgb565(item->brcolor);
visible_bg = true;
} else {
visible_bg = false;
Expand Down Expand Up @@ -219,6 +359,12 @@ int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen,
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha);
pixmem16[drawn_pixels] = rgb565_color_to_surface(blended);
} else if (alpha > 0) {
uint16_t color = rgba8888_color_to_rgb565(img_pixel);
uint16_t lower = 0;
(void) dcs_lcd_resolve_pixel_rgb565(screen, xpos + drawn_pixels, ypos, items, items_len, item_index + 1, &lower);
uint16_t blended = alpha_blend_rgb565(color, lower, alpha);
pixmem16[drawn_pixels] = rgb565_color_to_surface(blended);
} else {
return drawn_pixels;
}
Expand All @@ -245,19 +391,19 @@ int dcs_lcd_draw_x(const struct DCSLCDScreen *screen,
int drawn_pixels = 0;
switch (items[i].primitive) {
case PrimitiveImage:
drawn_pixels = dcs_lcd_draw_image_x(screen, xpos, ypos, max_line_len, item);
drawn_pixels = dcs_lcd_draw_image_x(screen, xpos, ypos, max_line_len, item, items, items_len, i);
break;

case PrimitiveRect:
drawn_pixels = dcs_lcd_draw_rect_x(screen, xpos, ypos, max_line_len, item);
break;

case PrimitiveScaledCroppedImage:
drawn_pixels = dcs_lcd_draw_scaled_cropped_img_x(screen, xpos, ypos, max_line_len, item);
drawn_pixels = dcs_lcd_draw_scaled_cropped_img_x(screen, xpos, ypos, max_line_len, item, items, items_len, i);
break;

case PrimitiveText:
drawn_pixels = dcs_lcd_draw_text_x(screen, xpos, ypos, max_line_len, item);
drawn_pixels = dcs_lcd_draw_text_x(screen, xpos, ypos, max_line_len, item, items, items_len, i);
break;
default: {
fprintf(stderr, "unexpected display list command.\n");
Expand Down
9 changes: 6 additions & 3 deletions dcs_lcd_draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@
#include "display_items.h"

int dcs_lcd_draw_image_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item);
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index);

int dcs_lcd_draw_rect_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item);

int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item);
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index);

int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen,
int xpos, int ypos, int max_line_len, BaseDisplayItem *item);
int xpos, int ypos, int max_line_len, BaseDisplayItem *item,
BaseDisplayItem items[], size_t items_len, size_t item_index);

int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen,
BaseDisplayItem items[], size_t items_len, int xpos, int ypos);
Expand Down
9 changes: 9 additions & 0 deletions display_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <stdlib.h>

#include <esp_idf_version.h>
#include <esp_log.h>

#include <context.h>
Expand All @@ -33,6 +34,9 @@ Context *epaper_display_create_port(GlobalContext *global, term opts);
Context *dcs_lcd_display_create_port(GlobalContext *global, term opts);
Context *memory_lcd_display_create_port(GlobalContext *global, term opts);
Context *oled_display_create_port(GlobalContext *global, term opts);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
Context *rgb_lcd_display_create_port(GlobalContext *global, term opts);
#endif

Context *display_create_port(GlobalContext *global, term opts)
{
Expand All @@ -54,6 +58,11 @@ Context *display_create_port(GlobalContext *global, term opts)
if (!strcmp(compat_string, "waveshare,5in65-acep-7c")
|| !strcmp(compat_string, "good-display/gdep073e01")) {
ctx = epaper_display_create_port(global, opts);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
} else if (!strcmp(compat_string, "waveshare,esp32-s3-touch-lcd-7")
|| !strcmp(compat_string, "esp_lcd,rgb")) {
ctx = rgb_lcd_display_create_port(global, opts);
#endif
} else if (!strcmp(compat_string, "sharp,memory-lcd")) {
ctx = memory_lcd_display_create_port(global, opts);
} else if (!strcmp(compat_string, "ilitek,ili9341")
Expand Down
Loading