diff --git a/Kconfig b/Kconfig index c3217ed84c..315fcf7bf0 100644 --- a/Kconfig +++ b/Kconfig @@ -550,6 +550,11 @@ menu "LVGL configuration" string "Header to include for the custom 'lv_global' function" depends on LV_ENABLE_GLOBAL_CUSTOM default "lv_global.h" + + config LV_BIN_DECODER_RAM_LOAD + bool "Decode whole image to RAM for bin decoder" + default n + endmenu menu "Compiler settings" diff --git a/lv_conf_template.h b/lv_conf_template.h index 34608a7024..677c93de9f 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -613,6 +613,9 @@ /*GIF decoder library*/ #define LV_USE_GIF 0 +/*Decode bin images to RAM*/ +#define LV_BIN_DECODER_RAM_LOAD 0 + /*QR code library*/ #define LV_USE_QRCODE 0 diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index adffda2514..9ff270ec37 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -330,6 +330,7 @@ lv_result_t lv_image_decoder_built_in_info(lv_image_decoder_t * decoder, const v LV_LOG_WARN("Image get info found unknown src type"); return LV_RESULT_INVALID; } + return LV_RESULT_OK; } @@ -351,6 +352,157 @@ static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_ return data; } +static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + lv_result_t res; + uint32_t rn; + lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data; + lv_fs_file_t * f = &decoder_data->f; + lv_color_format_t cf = dsc->header.cf; + + /*read palette for indexed image*/ + uint32_t palette_len = sizeof(lv_color32_t) * LV_COLOR_INDEXED_PALETTE_SIZE(cf); + lv_color32_t * palette = lv_malloc(palette_len); + LV_ASSERT_MALLOC(palette); + if(palette == NULL) { + LV_LOG_ERROR("out of memory"); + return LV_RESULT_INVALID; + } + + res = fs_read_file_at(f, sizeof(lv_image_header_t), (uint8_t *)palette, palette_len, &rn); + if(res != LV_FS_RES_OK || rn != palette_len) { + LV_LOG_WARN("read palette failed"); + lv_free(palette); + return LV_RESULT_INVALID; + } + + dsc->palette = palette; + dsc->palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); + + decoder_data->palette = palette; /*Free decoder data on close*/ + +#if LV_BIN_DECODER_RAM_LOAD + uint32_t stride = dsc->header.stride; + uint8_t * file_buf = lv_draw_buf_malloc(stride * dsc->header.h, cf); + LV_ASSERT_MALLOC(file_buf); + if(file_buf == NULL) { + LV_LOG_ERROR("draw buffer alloc failed"); + return LV_RESULT_INVALID; + } + + uint32_t data_len = 0; + if(lv_fs_seek(f, 0, LV_FS_SEEK_END) != LV_FS_RES_OK || + lv_fs_tell(f, &data_len) != LV_FS_RES_OK) { + LV_LOG_WARN("failed to get file to size"); + goto exit_with_buf; + } + + uint32_t data_offset = sizeof(lv_image_header_t) + palette_len; + data_len -= data_offset; + res = fs_read_file_at(f, data_offset, (uint8_t *)file_buf, data_len, &rn); + if(res != LV_FS_RES_OK || rn != data_len) { + LV_LOG_WARN("read palette failed"); + goto exit_with_buf; + } + + /*Convert to ARGB8888, since sw renderer cannot render it directly even it's in RAM*/ + stride = lv_draw_buf_width_to_stride(dsc->header.w, LV_COLOR_FORMAT_ARGB8888); + uint8_t * img_data = lv_draw_buf_malloc(stride * dsc->header.h, cf); + if(img_data == NULL) { + LV_LOG_ERROR("no memory for indexed image"); + goto exit_with_buf; + } + + const uint8_t * in = file_buf; + uint8_t * out = img_data; + for(uint32_t y = 0; y < dsc->header.h; y++) { + decode_indexed_line(cf, dsc->palette, 0, 0, dsc->header.w, in, (lv_color32_t *)out); + in += dsc->header.stride; + out += stride; + } + + dsc->header.stride = stride; + dsc->header.cf = LV_COLOR_FORMAT_ARGB8888; + dsc->img_data = img_data; + decoder_data->img_data = img_data; /*Free when decoder closes*/ + lv_draw_buf_free(file_buf); + + return LV_RESULT_OK; + +exit_with_buf: + lv_free(palette); + lv_draw_buf_free(file_buf); + return LV_RESULT_INVALID; +#else + /*It needs to be read by get_area_cb later*/ + return LV_RESULT_OK; +#endif +} + +#if LV_BIN_DECODER_RAM_LOAD +static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + lv_result_t res; + lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data; + lv_fs_file_t * f = &decoder_data->f; + lv_color_format_t cf = dsc->header.cf; + + uint32_t len = dsc->header.stride * dsc->header.h; + if(cf == LV_COLOR_FORMAT_RGB565A8) { + len += dsc->header.w * dsc->header.h * 1; + } + + uint8_t * img_data = lv_draw_buf_malloc(len, cf); + LV_ASSERT_MALLOC(img_data); + if(img_data == NULL) { + LV_LOG_ERROR("no memory for rgb file read"); + return LV_RESULT_INVALID; + } + + uint32_t rn; + res = fs_read_file_at(f, sizeof(lv_image_header_t), img_data, len, &rn); + if(res != LV_FS_RES_OK || rn != len) { + LV_LOG_WARN("read rgb file failed"); + lv_draw_buf_free(img_data); + return LV_RESULT_INVALID; + } + + dsc->img_data = img_data; + decoder_data->img_data = img_data; /*Free when decoder closes*/ + return LV_RESULT_OK; +} +#endif + +static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + lv_result_t res; + uint32_t rn; + lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data; + lv_fs_file_t * f = &decoder_data->f; + uint32_t len = (uint32_t)dsc->header.stride * dsc->header.h; + uint8_t * data = lv_draw_buf_malloc(len, dsc->header.cf); + + LV_ASSERT_MALLOC(data); + if(data == NULL) { + LV_LOG_ERROR("out of memory"); + return LV_RESULT_INVALID; + } + + res = fs_read_file_at(f, sizeof(lv_image_header_t), data, len, &rn); + if(res != LV_FS_RES_OK || rn != len) { + LV_LOG_WARN("Built-in image decoder can't read the palette"); + lv_draw_buf_free(data); + return LV_RESULT_INVALID; + } + + decoder_data->img_data = data; + dsc->img_data = data; + return LV_RESULT_OK; +} + /** * Open a built in image * @param decoder the decoder where this function belongs @@ -368,7 +520,7 @@ lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_imag lv_fs_file_t f; lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD); if(res != LV_FS_RES_OK) { - LV_LOG_WARN("Built-in image decoder can't open the file"); + LV_LOG_WARN("open file failed"); return LV_RESULT_INVALID; } @@ -381,62 +533,36 @@ lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_imag lv_memcpy(&decoder_data->f, &f, sizeof(f)); dsc->user_data = decoder_data; - /*read palette for indexed image*/ - if(LV_COLOR_FORMAT_IS_INDEXED(dsc->header.cf)) { - uint32_t size = LV_COLOR_INDEXED_PALETTE_SIZE(dsc->header.cf); - lv_color32_t * palette = lv_malloc(sizeof(lv_color32_t) * size); - LV_ASSERT_MALLOC(palette); - if(palette == NULL) { - LV_LOG_ERROR("out of memory"); - lv_fs_close(&f); - return LV_RESULT_INVALID; - } + lv_color_format_t cf = dsc->header.cf; - uint32_t rn; - res = fs_read_file_at(&f, sizeof(lv_image_header_t), (uint8_t *)palette, sizeof(lv_color32_t) * size, &rn); - if(res != LV_FS_RES_OK || rn != sizeof(lv_color32_t) * size) { - LV_LOG_WARN("Built-in image decoder can't read the palette"); - lv_free(palette); - lv_fs_close(&f); - return LV_RESULT_INVALID; - } + /*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/ + if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { + res = decode_indexed(decoder, dsc); + } + else if(cf == LV_COLOR_FORMAT_A8) { + res = decode_alpha_only(decoder, dsc); + } +#if LV_BIN_DECODER_RAM_LOAD + else if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 + || cf == LV_COLOR_FORMAT_RGB888 || cf == LV_COLOR_FORMAT_RGB565) { + res = decode_rgb(decoder, dsc); + } + else if(cf == LV_COLOR_FORMAT_RGB565A8) { + res = decode_rgb(decoder, dsc); + } +#else + else { + /* decode them in get_area_cb */ + res = LV_RESULT_OK; + } +#endif - dsc->palette = palette; - dsc->palette_size = size; - - decoder_data->palette = palette; /*Free decoder data on close*/ - - /*It needs to be read by get_area_cb later*/ - return LV_RESULT_OK; + if(res != LV_RESULT_OK) { + lv_fs_close(&f); + lv_memset(&decoder_data->f, 0, sizeof(decoder_data->f)); } - if(dsc->header.cf == LV_COLOR_FORMAT_A8) { - /*For A8, we read directly to RAM since it takes much less memory than ARGB*/ - uint32_t len = (uint32_t)dsc->header.w * dsc->header.h * 1; - uint8_t * data = lv_malloc(len); - LV_ASSERT_MALLOC(data); - if(data == NULL) { - LV_LOG_ERROR("out of memory"); - lv_fs_close(&f); - return LV_RESULT_INVALID; - } - - uint32_t rn; - res = fs_read_file_at(&f, sizeof(lv_image_header_t), data, len, &rn); - if(res != LV_FS_RES_OK || rn != len) { - LV_LOG_WARN("Built-in image decoder can't read the palette"); - lv_free(data); - lv_fs_close(&f); - return LV_RESULT_INVALID; - } - - decoder_data->img_data = data; - dsc->img_data = data; - return LV_RESULT_OK; - } - - /*It needs to be read by get_area_cb later*/ - return LV_RESULT_OK; + return res; } if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { @@ -454,7 +580,8 @@ lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_imag return LV_RESULT_INVALID; } - uint8_t * img_data = lv_malloc(sizeof(lv_color32_t) * img_dsc->header.w * img_dsc->header.h); + uint8_t * img_data = lv_draw_buf_malloc(sizeof(lv_color32_t) * img_dsc->header.w * img_dsc->header.h, + img_dsc->header.cf); LV_ASSERT_NULL(img_data); if(img_data == NULL) { return LV_RESULT_INVALID; @@ -503,7 +630,7 @@ void lv_image_decoder_built_in_close(lv_image_decoder_t * decoder, lv_image_deco lv_fs_close(&decoder_data->f); } - lv_free(decoder_data->img_data); + lv_draw_buf_free(decoder_data->img_data); lv_free(decoder_data->palette); lv_free(decoder_data); } @@ -538,7 +665,7 @@ lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_ /*Indexed image is converted to ARGB888*/ uint32_t len = LV_COLOR_FORMAT_IS_INDEXED(cf) ? sizeof(lv_color32_t) * 8 : bpp; len = (len * w_px) / 8; - img_data = lv_malloc(len); + img_data = lv_draw_buf_malloc(len, cf); LV_ASSERT_NULL(img_data); if(img_data == NULL) return LV_RESULT_INVALID; @@ -693,7 +820,7 @@ static uint32_t img_width_to_stride(lv_image_header_t * header) return header->w * 2; } else { - return header->w * lv_color_format_get_size(header->cf); + return ((uint32_t)header->w * lv_color_format_get_bpp(header->cf) + 7) >> 3; } } diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 57243f7755..9b485ba348 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -2043,6 +2043,15 @@ #endif #endif +/*Decode bin images to RAM*/ +#ifndef LV_BIN_DECODER_RAM_LOAD + #ifdef CONFIG_LV_BIN_DECODER_RAM_LOAD + #define LV_BIN_DECODER_RAM_LOAD CONFIG_LV_BIN_DECODER_RAM_LOAD + #else + #define LV_BIN_DECODER_RAM_LOAD 0 + #endif +#endif + /*QR code library*/ #ifndef LV_USE_QRCODE #ifdef CONFIG_LV_USE_QRCODE diff --git a/tests/ref_imgs/draw/image_format_rotate_and_recolor.png b/tests/ref_imgs/draw/image_format_rotate_and_recolor.png index 1397c94036..bf8d0af9c5 100644 Binary files a/tests/ref_imgs/draw/image_format_rotate_and_recolor.png and b/tests/ref_imgs/draw/image_format_rotate_and_recolor.png differ diff --git a/tests/ref_imgs/draw/image_format_rotated.png b/tests/ref_imgs/draw/image_format_rotated.png index 35d1987b3a..1bc5d1d9e7 100644 Binary files a/tests/ref_imgs/draw/image_format_rotated.png and b/tests/ref_imgs/draw/image_format_rotated.png differ diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 5d3aedd0cb..1d6bb2b3d8 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -90,3 +90,4 @@ #define LV_USE_OBJ_ID 1 #define LV_USE_OBJ_ID_BUILTIN 1 #define LV_USE_OBJ_PROPERTY 1 +#define LV_BIN_DECODER_RAM_LOAD 1 diff --git a/tests/src/test_cases/draw/test_image_formats.c b/tests/src/test_cases/draw/test_image_formats.c index 89bfe4da81..c6ca461e78 100644 --- a/tests/src/test_cases/draw/test_image_formats.c +++ b/tests/src/test_cases/draw/test_image_formats.c @@ -85,8 +85,16 @@ void test_image_built_in_decode_rotate(void) img_create("XRGB8888", &test_image_cogwheel_xrgb8888, true, false); img_create("ARGB8888", &test_image_cogwheel_argb8888, true, false); - /*Only A8 is read to ram thus can rotate for now*/ img_create("binA8", "A:src/test_files/binimages/cogwheel.A8.bin", true, false); + img_create("binI1", "A:src/test_files/binimages/cogwheel.I1.bin", true, false); + img_create("binI2", "A:src/test_files/binimages/cogwheel.I2.bin", true, false); + img_create("binI4", "A:src/test_files/binimages/cogwheel.I4.bin", true, false); + img_create("binI8", "A:src/test_files/binimages/cogwheel.I8.bin", true, false); + img_create("binRGB565A8", "A:src/test_files/binimages/cogwheel.RGB565A8.bin", true, false); + img_create("binRGB565", "A:src/test_files/binimages/cogwheel.RGB565.bin", true, false); + img_create("binRGB888", "A:src/test_files/binimages/cogwheel.RGB888.bin", true, false); + img_create("binXRGB8888", "A:src/test_files/binimages/cogwheel.XRGB8888.bin", true, false); + img_create("binARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.bin", true, false); TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rotated.png"); } @@ -138,8 +146,16 @@ void test_image_built_in_decode_rotate_and_recolor(void) img_create("XRGB8888", &test_image_cogwheel_xrgb8888, true, true); img_create("ARGB8888", &test_image_cogwheel_argb8888, true, true); - /*Only A8 is read to ram thus can rotate for now*/ img_create("binA8", "A:src/test_files/binimages/cogwheel.A8.bin", true, true); + img_create("binI1", "A:src/test_files/binimages/cogwheel.I1.bin", true, true); + img_create("binI2", "A:src/test_files/binimages/cogwheel.I2.bin", true, true); + img_create("binI4", "A:src/test_files/binimages/cogwheel.I4.bin", true, true); + img_create("binI8", "A:src/test_files/binimages/cogwheel.I8.bin", true, true); + img_create("binRGB565A8", "A:src/test_files/binimages/cogwheel.RGB565A8.bin", true, true); + img_create("binRGB565", "A:src/test_files/binimages/cogwheel.RGB565.bin", true, true); + img_create("binRGB888", "A:src/test_files/binimages/cogwheel.RGB888.bin", true, true); + img_create("binXRGB8888", "A:src/test_files/binimages/cogwheel.XRGB8888.bin", true, true); + img_create("binARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.bin", true, true); TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rotate_and_recolor.png");