#include "display_lcd_decode_image.h" #include #include #include #include #include "esp_check.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "lvgl.h" #include "png.h" static const char *TAG = "lcd_png_dec"; extern const uint8_t lcd_demo_png_start[] asm("_binary_lcd_demo_png_start"); extern const uint8_t lcd_demo_png_end[] asm("_binary_lcd_demo_png_end"); static void lcd_fill_err(char *err, size_t err_len, const char *msg) { if (err != NULL && err_len > 0) { snprintf(err, err_len, "%s", msg != NULL ? msg : "unknown"); } } esp_err_t platform_lcd_decode_png_rgb565(const uint8_t *png, size_t png_len, uint16_t **out_pixels, uint16_t *out_width, uint16_t *out_height, char *err, size_t err_len) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(out_pixels != NULL && out_width != NULL && out_height != NULL, ESP_ERR_INVALID_ARG, TAG, "invalid output args"); ESP_RETURN_ON_FALSE(png != NULL && png_len > 0, ESP_ERR_INVALID_ARG, TAG, "invalid png data"); *out_pixels = NULL; *out_width = 0; *out_height = 0; png_image image; memset(&image, 0, sizeof(image)); image.version = PNG_IMAGE_VERSION; if (!png_image_begin_read_from_memory(&image, png, png_len)) { const char *png_err = image.message[0] != '\0' ? image.message : "png header decode failed"; lcd_fill_err(err, err_len, png_err); return ESP_FAIL; } ESP_GOTO_ON_FALSE(image.width > 0 && image.height > 0, ESP_ERR_INVALID_SIZE, fail, TAG, "invalid png size"); ESP_GOTO_ON_FALSE(image.width <= UINT16_MAX && image.height <= UINT16_MAX, ESP_ERR_INVALID_SIZE, fail, TAG, "png dimensions too large"); ESP_GOTO_ON_FALSE(image.width <= SIZE_MAX / image.height, ESP_ERR_INVALID_SIZE, fail, TAG, "png too large"); image.format = PNG_FORMAT_RGBA; png_alloc_size_t rgba_size = PNG_IMAGE_SIZE(image); ESP_GOTO_ON_FALSE(rgba_size > 0, ESP_ERR_INVALID_SIZE, fail, TAG, "png output size invalid"); uint8_t *rgba = (uint8_t *)heap_caps_malloc((size_t)rgba_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (rgba == NULL) { rgba = (uint8_t *)heap_caps_malloc((size_t)rgba_size, MALLOC_CAP_8BIT); } ESP_GOTO_ON_FALSE(rgba != NULL, ESP_ERR_NO_MEM, fail, TAG, "alloc rgba buffer failed"); png_alloc_size_t stride = PNG_IMAGE_ROW_STRIDE(image); if (!png_image_finish_read(&image, NULL, rgba, stride, NULL)) { const char *png_err = image.message[0] != '\0' ? image.message : "png decode failed"; lcd_fill_err(err, err_len, png_err); heap_caps_free(rgba); png_image_free(&image); return ESP_FAIL; } png_image_free(&image); size_t pixel_count = (size_t)image.width * (size_t)image.height; uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (pixels == NULL) { pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_8BIT); } if (pixels == NULL) { lcd_fill_err(err, err_len, "alloc rgb565 buffer failed"); heap_caps_free(rgba); return ESP_ERR_NO_MEM; } for (size_t i = 0; i < pixel_count; ++i) { const size_t base = i * 4u; uint8_t r = rgba[base]; uint8_t g = rgba[base + 1u]; uint8_t b = rgba[base + 2u]; uint8_t a = rgba[base + 3u]; if (a < 255u) { r = (uint8_t)((((uint32_t)r * (uint32_t)a) + (255u * (255u - (uint32_t)a)) + 127u) / 255u); g = (uint8_t)((((uint32_t)g * (uint32_t)a) + (255u * (255u - (uint32_t)a)) + 127u) / 255u); b = (uint8_t)((((uint32_t)b * (uint32_t)a) + (255u * (255u - (uint32_t)a)) + 127u) / 255u); } lv_color_t color = lv_color_make(r, g, b); pixels[i] = (uint16_t)color.full; } heap_caps_free(rgba); *out_pixels = pixels; *out_width = (uint16_t)image.width; *out_height = (uint16_t)image.height; return ESP_OK; fail: lcd_fill_err(err, err_len, "invalid png"); png_image_free(&image); if (ret == ESP_OK) { ret = ESP_FAIL; } return ret; } esp_err_t platform_lcd_decode_demo_png(uint16_t **out_pixels, uint16_t *out_width, uint16_t *out_height, char *err, size_t err_len) { const size_t png_len = (size_t)(lcd_demo_png_end - lcd_demo_png_start); ESP_RETURN_ON_FALSE(png_len > 0, ESP_ERR_INVALID_SIZE, TAG, "empty png data"); return platform_lcd_decode_png_rgb565(lcd_demo_png_start, png_len, out_pixels, out_width, out_height, err, err_len); }