Grayscale quantization and fixing some defaults to support rgb565

This commit is contained in:
2026-03-15 22:01:45 -04:00
parent 7f296f9857
commit 2c79be36ef
5 changed files with 368 additions and 288 deletions

View File

@@ -1,8 +1,11 @@
#include "lv_setup.hpp"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/task.h"
#include "lodepng_alloc.hpp"
#include "types.hpp"
internal const char *kTagLvgl = "LVGL";
@@ -13,94 +16,117 @@ uint8_t *g_LvglDrawBuffer = nullptr;
internal void lvgl_tick_task(void *arg)
{
while (true)
{
vTaskDelay(pdMS_TO_TICKS(10));
while (true)
{
vTaskDelay(pdMS_TO_TICKS(10));
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
{
lv_timer_handler();
xSemaphoreGive(g_LvglMutex);
}
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
{
lv_timer_handler();
xSemaphoreGive(g_LvglMutex);
}
}
}
internal uint32_t my_tick_get_cb()
{
return (uint32_t)(esp_timer_get_time() / 1000);
return (uint32_t)(esp_timer_get_time() / 1000);
}
internal void lv_dummy_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
internal void lv_dummy_flush_cb(lv_display_t *disp, const lv_area_t *area,
uint8_t *px_map)
{
// Headless display, so we don't actually flush to SPI/I2C.
// We just tell LVGL that the "flush" is completed so it unblocks wait_for_flushing.
lv_display_flush_ready(disp);
// Headless display, so we don't actually flush to SPI/I2C.
// We just tell LVGL that the "flush" is completed so it unblocks
// wait_for_flushing.
lv_display_flush_ready(disp);
}
internal void lv_draw_sample_ui()
{
lv_obj_t *scr = lv_screen_active();
// Default background to white for the grayscale PNG
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
lv_obj_t *scr = lv_screen_active();
// Default background to white for the grayscale PNG
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
lv_obj_t *label = lv_label_create(scr);
lv_label_set_text(label, "Calendink Provider\nLVGL Headless Renderer");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_t *label = lv_label_create(scr);
lv_label_set_text(label, "Calendink Provider\nLVGL Headless Renderer");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
static lv_style_t style;
lv_style_init(&style);
lv_style_set_line_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_line_width(&style, 6);
lv_style_set_line_rounded(&style, true);
/*Create an object with the new style*/
lv_obj_t *obj = lv_line_create(scr);
lv_obj_add_style(obj, &style, 0);
static lv_point_precise_t p[] = {{10, 30}, {30, 50}, {100, 0}};
lv_line_set_points(obj, p, 3);
lv_obj_center(obj);
}
void setup_lvgl()
{
ESP_LOGI(kTagLvgl, "Initializing LVGL");
ESP_LOGI(kTagLvgl, "Initializing LVGL");
g_LvglMutex = xSemaphoreCreateMutex();
g_LvglMutex = xSemaphoreCreateMutex();
lv_init();
lv_tick_set_cb(my_tick_get_cb);
lv_init();
lv_tick_set_cb(my_tick_get_cb);
uint32_t width = CONFIG_CALENDINK_DISPLAY_WIDTH;
uint32_t height = CONFIG_CALENDINK_DISPLAY_HEIGHT;
uint32_t width = CONFIG_CALENDINK_DISPLAY_WIDTH;
uint32_t height = CONFIG_CALENDINK_DISPLAY_HEIGHT;
// Create a virtual display
g_LvglDisplay = lv_display_create(width, height);
lv_display_set_flush_cb(g_LvglDisplay, lv_dummy_flush_cb);
// Create a virtual display
g_LvglDisplay = lv_display_create(width, height);
lv_display_set_flush_cb(g_LvglDisplay, lv_dummy_flush_cb);
// Initialize LodePNG custom bump allocator
lodepng_allocator_init();
// Initialize LodePNG custom bump allocator
lodepng_allocator_init();
// Allocate draw buffers in PSRAM
// Using LV_COLOR_FORMAT_L8 (1 byte per pixel)
size_t buf_size = LV_DRAW_BUF_SIZE(width, height, LV_COLOR_FORMAT_L8);
// Fallback to MALLOC_CAP_DEFAULT if we can't get SPIRAM (for debugging without it)
void *buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM);
if (!buf1)
{
ESP_LOGW(kTagLvgl, "Failed to allocate LVGL draw buffer in PSRAM, falling back to internal RAM");
buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_DEFAULT);
}
if (!buf1)
{
ESP_LOGE(kTagLvgl, "Failed to allocate LVGL draw buffer entirely.");
return;
}
// Allocate draw buffers in PSRAM
// Using LV_COLOR_FORMAT_RGB565 (2 bytes per pixel)
size_t buf_size = LV_DRAW_BUF_SIZE(width, height, LV_COLOR_FORMAT_RGB565);
g_LvglDrawBuffer = (uint8_t *)buf1;
// Fallback to MALLOC_CAP_DEFAULT if we can't get SPIRAM (for debugging
// without it)
void *buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM);
if (!buf1)
{
ESP_LOGW(kTagLvgl, "Failed to allocate LVGL draw buffer in PSRAM, falling "
"back to internal RAM");
buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_DEFAULT);
}
lv_display_set_buffers(g_LvglDisplay, buf1, nullptr, buf_size, LV_DISPLAY_RENDER_MODE_FULL);
// Explicitly set the color format of the display if it's set in sdkconfig/driver
lv_display_set_color_format(g_LvglDisplay, LV_COLOR_FORMAT_L8);
if (!buf1)
{
ESP_LOGE(kTagLvgl, "Failed to allocate LVGL draw buffer entirely.");
return;
}
// Create the background task for the LVGL timer
xTaskCreate(lvgl_tick_task, "LVGL Tick", 4096, nullptr, 5, nullptr);
g_LvglDrawBuffer = (uint8_t *)buf1;
// Draw the sample UI
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
{
lv_draw_sample_ui();
xSemaphoreGive(g_LvglMutex);
}
// Explicitly set the color format of the display FIRST
// so that stride and byte-per-pixel calculations align with our buffer.
lv_display_set_color_format(g_LvglDisplay, LV_COLOR_FORMAT_RGB565);
ESP_LOGI(kTagLvgl, "LVGL fully initialized. Display %lux%lu created.", width, height);
lv_display_set_buffers(g_LvglDisplay, buf1, nullptr, buf_size,
LV_DISPLAY_RENDER_MODE_FULL);
// Create the background task for the LVGL timer
xTaskCreate(lvgl_tick_task, "LVGL Tick", 4096, nullptr, 5, nullptr);
// Draw the sample UI
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
{
lv_draw_sample_ui();
xSemaphoreGive(g_LvglMutex);
}
ESP_LOGI(kTagLvgl, "LVGL fully initialized. Display %lux%lu created.", width,
height);
}