diff --git a/docs/overview/img.rst b/docs/overview/img.rst index dfeb634d5..d249a0ca6 100644 --- a/docs/overview/img.rst +++ b/docs/overview/img.rst @@ -276,8 +276,8 @@ open/close the PNG files. It should look like this: /*Change the color format if required. For PNG usually 'Raw' is fine*/ dsc->header.cf = LV_COLOR_FORMAT_... - /*Call a built in decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/ - lv_result_t res = lv_image_decoder_built_in_open(decoder, dsc); + /*Call a binary image decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/ + lv_result_t res = lv_bin_decoder_open(decoder, dsc); return res; } @@ -306,7 +306,7 @@ open/close the PNG files. It should look like this: /*Free all allocated data*/ /*Call the built-in close function if the built-in open/read_line was used*/ - lv_image_decoder_built_in_close(decoder, dsc); + lv_bin_decoder_close(decoder, dsc); } diff --git a/lvgl.h b/lvgl.h index 28cc73a90..64097f86a 100644 --- a/lvgl.h +++ b/lvgl.h @@ -90,6 +90,7 @@ extern "C" { #include "src/others/file_explorer/lv_file_explorer.h" #include "src/libs/barcode/lv_barcode.h" +#include "src/libs/bin_decoder/lv_bin_decoder.h" #include "src/libs/bmp/lv_bmp.h" #include "src/libs/rle/lv_rle_decoder.h" #include "src/libs/fsdrv/lv_fsdrv.h" diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index 61cd26c39..261a33763 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -22,29 +22,11 @@ * TYPEDEFS **********************/ -typedef struct { - lv_fs_file_t * f; - lv_color32_t * palette; - uint8_t * img_data; - lv_opa_t * opa; -} lv_image_decoder_built_in_data_t; - /********************** * STATIC PROTOTYPES **********************/ -static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc); -static void free_decoder_data(lv_image_decoder_dsc_t * dsc); -static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); -#if LV_BIN_DECODER_RAM_LOAD - static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); -#endif -static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); -static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x, - int32_t y, - int32_t w_px, const uint8_t * in, lv_color32_t * out); static uint32_t img_width_to_stride(lv_image_header_t * header); -static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br); /********************** * STATIC VARIABLES @@ -64,21 +46,6 @@ static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buf void _lv_image_decoder_init(void) { _lv_ll_init(img_decoder_ll_p, sizeof(lv_image_decoder_t)); - - lv_image_decoder_t * decoder; - - /*Create a decoder for the built in color format*/ - decoder = lv_image_decoder_create(); - LV_ASSERT_MALLOC(decoder); - if(decoder == NULL) { - LV_LOG_WARN("Out of memory"); - return; - } - - lv_image_decoder_set_info_cb(decoder, lv_image_decoder_built_in_info); - lv_image_decoder_set_open_cb(decoder, lv_image_decoder_built_in_open); - lv_image_decoder_set_get_area_cb(decoder, lv_image_decoder_built_in_get_area); - lv_image_decoder_set_close_cb(decoder, lv_image_decoder_built_in_close); } /** @@ -298,603 +265,6 @@ void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decode decoder->close_cb = close_cb; } -/** - * Get info about a built-in image - * @param decoder the decoder where this function belongs - * @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol - * @param header store the image data here - * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. - */ -lv_result_t lv_image_decoder_built_in_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header) -{ - LV_UNUSED(decoder); /*Unused*/ - - lv_image_src_t src_type = lv_image_src_get_type(src); - if(src_type == LV_IMAGE_SRC_VARIABLE) { - header->w = ((lv_image_dsc_t *)src)->header.w; - header->h = ((lv_image_dsc_t *)src)->header.h; - header->cf = ((lv_image_dsc_t *)src)->header.cf; - header->stride = ((lv_image_dsc_t *)src)->header.stride; - } - else if(src_type == LV_IMAGE_SRC_FILE) { - /*Support only "*.bin" files*/ - if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RESULT_INVALID; - - lv_fs_file_t f; - lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD); - if(res == LV_FS_RES_OK) { - uint32_t rn; - res = lv_fs_read(&f, header, sizeof(lv_image_header_t), &rn); - lv_fs_close(&f); - if(res != LV_FS_RES_OK || rn != sizeof(lv_image_header_t)) { - LV_LOG_WARN("Read file header failed: %d", res); - return LV_RESULT_INVALID; - } - } - } - else if(src_type == LV_IMAGE_SRC_SYMBOL) { - /*The size depend on the font but it is unknown here. It should be handled outside of the - *function*/ - header->w = 1; - header->h = 1; - /*Symbols always have transparent parts. Important because of cover check in the draw - *function. The actual value doesn't matter because lv_draw_label will draw it*/ - header->cf = LV_COLOR_FORMAT_A8; - } - else { - LV_LOG_WARN("Image get info found unknown src type"); - return LV_RESULT_INVALID; - } - - return LV_RESULT_OK; -} - -/** - * Open a built in image - * @param decoder the decoder where this function belongs - * @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it. - * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. - */ -lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) -{ - LV_UNUSED(decoder); - lv_fs_res_t res = LV_RESULT_INVALID; - - /*Open the file if it's a file*/ - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - /*Support only "*.bin" files*/ - if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RESULT_INVALID; - - /*If the file was open successfully save the file descriptor*/ - lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc); - if(decoder_data == NULL) { - return LV_RESULT_INVALID; - } - - dsc->user_data = decoder_data; - lv_fs_file_t * f = lv_malloc(sizeof(*f)); - if(f == NULL) { - free_decoder_data(dsc); - return LV_RESULT_INVALID; - } - - res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD); - if(res != LV_FS_RES_OK) { - LV_LOG_WARN("Open file failed: %d", res); - lv_free(f); - free_decoder_data(dsc); - return LV_RESULT_INVALID; - } - - decoder_data->f = f; - - lv_color_format_t cf = dsc->header.cf; - - /*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(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) { - 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 - } - - else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { - /*The variables should have valid data*/ - lv_image_dsc_t * img_dsc = (lv_image_dsc_t *)dsc->src; - if(img_dsc->data == NULL) { - return LV_RESULT_INVALID; - } - - lv_color_format_t cf = img_dsc->header.cf; - if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { - /*Need decoder data to store converted image*/ - lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc); - if(decoder_data == NULL) { - return LV_RESULT_INVALID; - } - - res = decode_indexed(decoder, dsc); - } - else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) { - /*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/ - lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc); - if(decoder_data == NULL) { - return LV_RESULT_INVALID; - } - - res = decode_alpha_only(decoder, dsc); - } - else { - /*In case of uncompressed formats the image stored in the ROM/RAM. - *So simply give its pointer*/ - dsc->img_data = ((lv_image_dsc_t *)dsc->src)->data; - res = LV_RESULT_OK; - } - } - - if(res != LV_RESULT_OK) { - free_decoder_data(dsc); - } - - return res; -} - -/** - * Close the pending decoding. Free resources etc. - * @param decoder pointer to the decoder the function associated with - * @param dsc pointer to decoder descriptor - */ -void lv_image_decoder_built_in_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) -{ - LV_UNUSED(decoder); /*Unused*/ - free_decoder_data(dsc); -} - -lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, - const lv_area_t * full_area, lv_area_t * decoded_area) -{ - LV_UNUSED(decoder); /*Unused*/ - - lv_color_format_t cf = dsc->header.cf; - /*Check if cf is supported*/ - - bool supported = LV_COLOR_FORMAT_IS_INDEXED(cf) - || cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888 - || cf == LV_COLOR_FORMAT_RGB565 || cf == LV_COLOR_FORMAT_RGB565A8; - if(!supported) { - LV_LOG_WARN("CF: %d is not supported", cf); - return LV_RESULT_INVALID; - } - - lv_result_t res = LV_RESULT_INVALID; - lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data; - lv_fs_file_t * f = decoder_data->f; - uint32_t bpp = lv_color_format_get_bpp(cf); - int32_t w_px = lv_area_get_width(full_area); - uint8_t * img_data = NULL; - uint32_t offset = sizeof(lv_image_header_t); /*All image starts with image header*/ - - /*We only support read line by line for now*/ - if(decoded_area->y1 == LV_COORD_MIN) { - /*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_draw_buf_malloc(len, cf); - LV_ASSERT_NULL(img_data); - if(img_data == NULL) - return LV_RESULT_INVALID; - - *decoded_area = *full_area; - decoded_area->y2 = decoded_area->y1; - decoder_data->img_data = img_data; /*Free on decoder close*/ - } - else { - decoded_area->y1++; - decoded_area->y2++; - img_data = decoder_data->img_data; - } - - if(decoded_area->y1 > full_area->y2) { - return LV_RESULT_INVALID; - } - - if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { - int32_t x_fraction = decoded_area->x1 % (8 / bpp); - uint32_t len = (w_px * bpp + 7) / 8 + 1; /*10px for 1bpp may across 3bytes*/ - uint8_t * buf = NULL; - offset += dsc->palette_size * 4; /*Skip palette*/ - offset += decoded_area->y1 * dsc->header.stride; - offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - buf = lv_malloc(len); - LV_ASSERT_NULL(buf); - if(buf == NULL) - return LV_RESULT_INVALID; - - res = fs_read_file_at(f, offset, buf, len, NULL); - if(res != LV_FS_RES_OK) { - lv_free(buf); - return LV_RESULT_INVALID; - } - } - else { - const lv_image_dsc_t * image = dsc->src; - buf = (void *)(image->data + offset); - } - - decode_indexed_line(cf, dsc->palette, x_fraction, 0, w_px, buf, (lv_color32_t *)img_data); - - if(dsc->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)buf); - - dsc->img_data = img_data; /*Return decoded image*/ - return LV_RESULT_OK; - } - - if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888 - || cf == LV_COLOR_FORMAT_RGB565) { - uint32_t len = (w_px * bpp) / 8; - offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/ - offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ - res = fs_read_file_at(f, offset, img_data, len, NULL); - if(res != LV_FS_RES_OK) { - return LV_RESULT_INVALID; - } - - dsc->img_data = img_data; /*Return decoded image*/ - return LV_RESULT_OK; - } - - if(cf == LV_COLOR_FORMAT_RGB565A8) { - bpp = 16; /* RGB565 + A8 mask*/ - uint32_t len = (w_px * bpp) / 8; /*map comes firstly*/ - offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/ - offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ - res = fs_read_file_at(f, offset, img_data, len, NULL); - if(res != LV_FS_RES_OK) { - return LV_RESULT_INVALID; - } - - /*Now the A8 mask*/ - offset = sizeof(lv_image_header_t); - offset += dsc->header.h * dsc->header.w * bpp / 8; /*Move to A8 map*/ - offset += decoded_area->y1 * dsc->header.w * 1; /*Move to y1*/ - offset += decoded_area->x1 * 1; /*Move to x1*/ - res = fs_read_file_at(f, offset, img_data + len, w_px * 1, NULL); - if(res != LV_FS_RES_OK) { - return LV_RESULT_INVALID; - } - - dsc->img_data = img_data; /*Return decoded image*/ - return LV_RESULT_OK; - } - - return LV_RESULT_INVALID; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc) -{ - lv_image_decoder_built_in_data_t * data = dsc->user_data; - if(data == NULL) { - data = lv_malloc_zeroed(sizeof(lv_image_decoder_built_in_data_t)); - LV_ASSERT_MALLOC(data); - if(data == NULL) { - LV_LOG_ERROR("Out of memory"); - return NULL; - } - - dsc->user_data = data; - } - - return data; -} - -static void free_decoder_data(lv_image_decoder_dsc_t * dsc) -{ - lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data; - if(decoder_data) { - if(decoder_data->f) { - lv_fs_close(decoder_data->f); - lv_free(decoder_data->f); - } - - lv_draw_buf_free(decoder_data->img_data); - lv_free(decoder_data->palette); - lv_free(decoder_data); - dsc->user_data = NULL; - } -} - -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; - uint32_t palette_len = sizeof(lv_color32_t) * LV_COLOR_INDEXED_PALETTE_SIZE(cf); - const lv_color32_t * palette; - const uint8_t * indexed_data = NULL; - uint32_t stride = dsc->header.stride; - - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - /*read palette for indexed image*/ - 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: %d", res); - lv_free((void *)palette); - return LV_RESULT_INVALID; - } - -#if LV_BIN_DECODER_RAM_LOAD - indexed_data = lv_draw_buf_malloc(stride * dsc->header.h, cf); - LV_ASSERT_MALLOC(indexed_data); - if(indexed_data == NULL) { - LV_LOG_ERROR("Draw buffer alloc failed"); - goto exit_with_buf; - } - - 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 *)indexed_data, data_len, &rn); - if(res != LV_FS_RES_OK || rn != data_len) { - LV_LOG_WARN("Read indexed image failed: %d", res); - goto exit_with_buf; - } -#endif - } - else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { - lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src; - palette = (lv_color32_t *)image->data; - indexed_data = image->data + palette_len; - } - else { - return LV_RESULT_INVALID; - } - - dsc->palette = palette; - dsc->palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); - -#if LV_BIN_DECODER_RAM_LOAD - /*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 = indexed_data; - 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*/ - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - decoder_data->palette = (void *)palette; /*Free decoder data on close*/ - lv_draw_buf_free((void *)indexed_data); - } - - return LV_RESULT_OK; -exit_with_buf: - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - lv_free((void *)palette); - lv_draw_buf_free((void *)indexed_data); - } - return LV_RESULT_INVALID; -#else - LV_UNUSED(stride); - LV_UNUSED(indexed_data); - /*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: %d", res); - 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; - uint8_t bpp = lv_color_format_get_bpp(dsc->header.cf); - uint32_t w = (dsc->header.stride * 8) / bpp; - uint32_t buf_stride = (w * 8 + 7) >> 3; /*stride for img_data*/ - uint32_t buf_len = w * dsc->header.h; /*always decode to A8 format*/ - uint8_t * img_data = lv_draw_buf_malloc(buf_len, dsc->header.cf); - uint32_t file_len = (uint32_t)dsc->header.stride * dsc->header.h; - - LV_ASSERT_MALLOC(img_data); - if(img_data == NULL) { - LV_LOG_ERROR("Out of memory"); - return LV_RESULT_INVALID; - } - - if(dsc->src_type == LV_IMAGE_SRC_FILE) { - res = fs_read_file_at(decoder_data->f, sizeof(lv_image_header_t), img_data, file_len, &rn); - if(res != LV_FS_RES_OK || rn != file_len) { - LV_LOG_WARN("Read header failed: %d", res); - lv_draw_buf_free(img_data); - return LV_RESULT_INVALID; - } - } - else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { - /*Copy from image data*/ - lv_memcpy(img_data, ((lv_image_dsc_t *)dsc->src)->data, file_len); - } - - if(dsc->header.cf != LV_COLOR_FORMAT_A8) { - /*Convert A1/2/4 to A8 from last pixel to first pixel*/ - uint8_t * in = img_data + file_len - 1; - uint8_t * out = img_data + buf_len - 1; - uint8_t mask = (1 << bpp) - 1; - uint8_t shift = 0; - for(uint32_t i = 0; i < buf_len; i++) { - /** - * Rounding error: - * Take bpp = 4 as example, alpha value of 0x0 to 0x0F should be - * mapped to 0x00 to 0xFF, thus the equation should be below Equation 3. - * - * But it involves division and multiplication, which is slow. So, if - * we ignore the rounding errors, Equation1, 2 could be faster. But it - * will either has error when alpha is 0xff or 0x00. - * - * We use Equation 3 here for maximum accuracy. - * - * Equation 1: *out = ((*in >> shift) & mask) << (8 - bpp); - * Equation 2: *out = ((((*in >> shift) & mask) + 1) << (8 - bpp)) - 1; - * Equation 3: *out = ((*in >> shift) & mask) * 255 / ((1L << bpp) - 1) ; - */ - *out = ((*in >> shift) & mask) * 255L / ((1L << bpp) - 1) ; - shift += bpp; - if(shift >= 8) { - shift = 0; - in--; - } - out--; - } - } - - decoder_data->img_data = img_data; - dsc->img_data = img_data; - dsc->header.stride = buf_stride; - dsc->header.cf = LV_COLOR_FORMAT_A8; - return LV_RESULT_OK; -} - -static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x, - int32_t y, - int32_t w_px, const uint8_t * in, lv_color32_t * out) -{ - uint8_t px_size; - uint16_t mask; - - out += w_px * y; - - int32_t w_byte = 0; - int8_t shift = 0; - switch(color_format) { - case LV_COLOR_FORMAT_I1: - px_size = 1; - w_byte = (w_px + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/ - in += w_byte * y; /*First pixel*/ - in += x / 8; /*8pixel per byte*/ - shift = 7 - (x & 0x7); - break; - case LV_COLOR_FORMAT_I2: - px_size = 2; - w_byte = (w_px + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/ - in += w_byte * y; /*First pixel*/ - in += x / 4; /*4pixel per byte*/ - shift = 6 - 2 * (x & 0x3); - break; - case LV_COLOR_FORMAT_I4: - px_size = 4; - w_byte = (w_px + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/ - in += w_byte * y; /*First pixel*/ - in += x / 2; /*2pixel per byte*/ - shift = 4 - 4 * (x & 0x1); - break; - case LV_COLOR_FORMAT_I8: - px_size = 8; - w_byte = w_px; - in += w_byte * y; /*First pixel*/ - in += x; - shift = 0; - break; - default: - return LV_RESULT_INVALID; - } - - mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/ - - int32_t i; - for(i = 0; i < w_px; i++) { - uint8_t val_act = (*in >> shift) & mask; - out[i] = palette[val_act]; - - shift -= px_size; - if(shift < 0) { - shift = 8 - px_size; - in++; - } - } - return LV_RESULT_OK; -} - static uint32_t img_width_to_stride(lv_image_header_t * header) { if(header->cf == LV_COLOR_FORMAT_RGB565A8) { @@ -905,20 +275,3 @@ static uint32_t img_width_to_stride(lv_image_header_t * header) } } -static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br) -{ - lv_fs_res_t res; - if(br) *br = 0; - - res = lv_fs_seek(f, pos, LV_FS_SEEK_SET); - if(res != LV_FS_RES_OK) { - return res; - } - - res |= lv_fs_read(f, buff, btr, br); - if(res != LV_FS_RES_OK) { - return res; - } - - return LV_FS_RES_OK; -} diff --git a/src/draw/lv_image_decoder.h b/src/draw/lv_image_decoder.h index 1a5bb40b1..2047dda6c 100644 --- a/src/draw/lv_image_decoder.h +++ b/src/draw/lv_image_decoder.h @@ -241,33 +241,6 @@ void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_dec */ void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb); -/** - * Get info about a built-in image - * @param decoder the decoder where this function belongs - * @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol - * @param header store the image data here - * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. - */ -lv_result_t lv_image_decoder_built_in_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header); - -lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, - const lv_area_t * full_area, lv_area_t * decoded_area); - -/** - * Open a built in image - * @param decoder the decoder where this function belongs - * @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it. - * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. - */ -lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); - -/** - * Close the pending decoding. Free resources etc. - * @param decoder pointer to the decoder the function associated with - * @param dsc pointer to decoder descriptor - */ -void lv_image_decoder_built_in_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); - /********************** * MACROS **********************/ diff --git a/src/libs/bin_decoder/lv_bin_decoder.c b/src/libs/bin_decoder/lv_bin_decoder.c new file mode 100644 index 000000000..297b20f7d --- /dev/null +++ b/src/libs/bin_decoder/lv_bin_decoder.c @@ -0,0 +1,680 @@ +/** + * @file lv_image_decoder.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_bin_decoder.h" +#include "../../draw/lv_draw_image.h" +#include "../../draw/lv_draw_buf.h" +#include "../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_fs_file_t * f; + lv_color32_t * palette; + uint8_t * img_data; + lv_opa_t * opa; +} decoder_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static decoder_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc); +static void free_decoder_data(lv_image_decoder_dsc_t * dsc); +static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +#if LV_BIN_DECODER_RAM_LOAD + static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +#endif +static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x, + int32_t w_px, const uint8_t * in, lv_color32_t * out); + +static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the lvgl binary image decoder module + */ +void lv_bin_decoder_init(void) +{ + lv_image_decoder_t * decoder; + + decoder = lv_image_decoder_create(); + LV_ASSERT_MALLOC(decoder); + if(decoder == NULL) { + LV_LOG_WARN("Out of memory"); + return; + } + + lv_image_decoder_set_info_cb(decoder, lv_bin_decoder_info); + lv_image_decoder_set_open_cb(decoder, lv_bin_decoder_open); + lv_image_decoder_set_get_area_cb(decoder, lv_bin_decoder_get_area); + lv_image_decoder_set_close_cb(decoder, lv_bin_decoder_close); +} + +/** + * Get info about a lvgl binary image + * @param decoder the decoder where this function belongs + * @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol + * @param header store the image data here + * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. + */ +lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_image_src_t src_type = lv_image_src_get_type(src); + if(src_type == LV_IMAGE_SRC_VARIABLE) { + header->w = ((lv_image_dsc_t *)src)->header.w; + header->h = ((lv_image_dsc_t *)src)->header.h; + header->cf = ((lv_image_dsc_t *)src)->header.cf; + header->stride = ((lv_image_dsc_t *)src)->header.stride; + } + else if(src_type == LV_IMAGE_SRC_FILE) { + /*Support only "*.bin" files*/ + if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RESULT_INVALID; + + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD); + if(res == LV_FS_RES_OK) { + uint32_t rn; + res = lv_fs_read(&f, header, sizeof(lv_image_header_t), &rn); + lv_fs_close(&f); + if(res != LV_FS_RES_OK || rn != sizeof(lv_image_header_t)) { + LV_LOG_WARN("Read file header failed: %d", res); + return LV_RESULT_INVALID; + } + } + } + else if(src_type == LV_IMAGE_SRC_SYMBOL) { + /*The size depend on the font but it is unknown here. It should be handled outside of the + *function*/ + header->w = 1; + header->h = 1; + /*Symbols always have transparent parts. Important because of cover check in the draw + *function. The actual value doesn't matter because lv_draw_label will draw it*/ + header->cf = LV_COLOR_FORMAT_A8; + } + else { + LV_LOG_WARN("Image get info found unknown src type"); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +/** + * Open a lvgl binary image + * @param decoder the decoder where this function belongs + * @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it. + * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. + */ +lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + lv_fs_res_t res = LV_RESULT_INVALID; + + /*Open the file if it's a file*/ + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + /*Support only "*.bin" files*/ + if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RESULT_INVALID; + + /*If the file was open successfully save the file descriptor*/ + decoder_data_t * decoder_data = get_decoder_data(dsc); + if(decoder_data == NULL) { + return LV_RESULT_INVALID; + } + + dsc->user_data = decoder_data; + lv_fs_file_t * f = lv_malloc(sizeof(*f)); + if(f == NULL) { + free_decoder_data(dsc); + return LV_RESULT_INVALID; + } + + res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + LV_LOG_WARN("Open file failed: %d", res); + lv_free(f); + free_decoder_data(dsc); + return LV_RESULT_INVALID; + } + + decoder_data->f = f; + + lv_color_format_t cf = dsc->header.cf; + + /*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(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) { + 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 + } + + else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { + /*The variables should have valid data*/ + lv_image_dsc_t * img_dsc = (lv_image_dsc_t *)dsc->src; + if(img_dsc->data == NULL) { + return LV_RESULT_INVALID; + } + + lv_color_format_t cf = img_dsc->header.cf; + if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { + /*Need decoder data to store converted image*/ + decoder_data_t * decoder_data = get_decoder_data(dsc); + if(decoder_data == NULL) { + return LV_RESULT_INVALID; + } + + res = decode_indexed(decoder, dsc); + } + else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) { + /*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/ + decoder_data_t * decoder_data = get_decoder_data(dsc); + if(decoder_data == NULL) { + return LV_RESULT_INVALID; + } + + res = decode_alpha_only(decoder, dsc); + } + else { + /*In case of uncompressed formats the image stored in the ROM/RAM. + *So simply give its pointer*/ + dsc->img_data = ((lv_image_dsc_t *)dsc->src)->data; + res = LV_RESULT_OK; + } + } + + if(res != LV_RESULT_OK) { + free_decoder_data(dsc); + } + + return res; +} + +/** + * Close the pending decoding. Free resources etc. + * @param decoder pointer to the decoder the function associated with + * @param dsc pointer to decoder descriptor + */ +void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + free_decoder_data(dsc); +} + +lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, + const lv_area_t * full_area, lv_area_t * decoded_area) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_color_format_t cf = dsc->header.cf; + /*Check if cf is supported*/ + + bool supported = LV_COLOR_FORMAT_IS_INDEXED(cf) + || cf == LV_COLOR_FORMAT_ARGB8888 \ + || cf == LV_COLOR_FORMAT_XRGB8888 \ + || cf == LV_COLOR_FORMAT_RGB888 \ + || cf == LV_COLOR_FORMAT_RGB565 \ + || cf == LV_COLOR_FORMAT_RGB565A8; + if(!supported) { + LV_LOG_WARN("CF: %d is not supported", cf); + return LV_RESULT_INVALID; + } + + lv_result_t res = LV_RESULT_INVALID; + decoder_data_t * decoder_data = dsc->user_data; + lv_fs_file_t * f = decoder_data->f; + uint32_t bpp = lv_color_format_get_bpp(cf); + int32_t w_px = lv_area_get_width(full_area); + uint8_t * img_data = NULL; + uint32_t offset = sizeof(lv_image_header_t); /*All image starts with image header*/ + + /*We only support read line by line for now*/ + if(decoded_area->y1 == LV_COORD_MIN) { + /*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_draw_buf_malloc(len, cf); + LV_ASSERT_NULL(img_data); + if(img_data == NULL) + return LV_RESULT_INVALID; + + *decoded_area = *full_area; + decoded_area->y2 = decoded_area->y1; + decoder_data->img_data = img_data; /*Free on decoder close*/ + } + else { + decoded_area->y1++; + decoded_area->y2++; + img_data = decoder_data->img_data; + } + + if(decoded_area->y1 > full_area->y2) { + return LV_RESULT_INVALID; + } + + if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { + int32_t x_fraction = decoded_area->x1 % (8 / bpp); + uint32_t len = (w_px * bpp + 7) / 8 + 1; /*10px for 1bpp may across 3bytes*/ + uint8_t * buf = NULL; + offset += dsc->palette_size * 4; /*Skip palette*/ + offset += decoded_area->y1 * dsc->header.stride; + offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + buf = lv_malloc(len); + LV_ASSERT_NULL(buf); + if(buf == NULL) + return LV_RESULT_INVALID; + + res = fs_read_file_at(f, offset, buf, len, NULL); + if(res != LV_FS_RES_OK) { + lv_free(buf); + return LV_RESULT_INVALID; + } + } + else { + const lv_image_dsc_t * image = dsc->src; + buf = (void *)(image->data + offset); + } + + decode_indexed_line(cf, dsc->palette, x_fraction, w_px, buf, (lv_color32_t *)img_data); + + if(dsc->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)buf); + + dsc->img_data = img_data; /*Return decoded image*/ + return LV_RESULT_OK; + } + + if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888 + || cf == LV_COLOR_FORMAT_RGB565) { + uint32_t len = (w_px * bpp) / 8; + offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/ + offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ + res = fs_read_file_at(f, offset, img_data, len, NULL); + if(res != LV_FS_RES_OK) { + return LV_RESULT_INVALID; + } + + dsc->img_data = img_data; /*Return decoded image*/ + return LV_RESULT_OK; + } + + if(cf == LV_COLOR_FORMAT_RGB565A8) { + bpp = 16; /* RGB565 + A8 mask*/ + uint32_t len = (w_px * bpp) / 8; /*map comes firstly*/ + offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/ + offset += decoded_area->x1 * bpp / 8; /*Move to x1*/ + res = fs_read_file_at(f, offset, img_data, len, NULL); + if(res != LV_FS_RES_OK) { + return LV_RESULT_INVALID; + } + + /*Now the A8 mask*/ + offset = sizeof(lv_image_header_t); + offset += dsc->header.h * dsc->header.w * bpp / 8; /*Move to A8 map*/ + offset += decoded_area->y1 * dsc->header.w * 1; /*Move to y1*/ + offset += decoded_area->x1 * 1; /*Move to x1*/ + res = fs_read_file_at(f, offset, img_data + len, w_px * 1, NULL); + if(res != LV_FS_RES_OK) { + return LV_RESULT_INVALID; + } + + dsc->img_data = img_data; /*Return decoded image*/ + return LV_RESULT_OK; + } + + return LV_RESULT_INVALID; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static decoder_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc) +{ + decoder_data_t * data = dsc->user_data; + if(data == NULL) { + data = lv_malloc_zeroed(sizeof(decoder_data_t)); + LV_ASSERT_MALLOC(data); + if(data == NULL) { + LV_LOG_ERROR("Out of memory"); + return NULL; + } + + dsc->user_data = data; + } + + return data; +} + +static void free_decoder_data(lv_image_decoder_dsc_t * dsc) +{ + decoder_data_t * decoder_data = dsc->user_data; + if(decoder_data) { + if(decoder_data->f) { + lv_fs_close(decoder_data->f); + lv_free(decoder_data->f); + } + + lv_draw_buf_free(decoder_data->img_data); + lv_free(decoder_data->palette); + lv_free(decoder_data); + dsc->user_data = NULL; + } +} + +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; + decoder_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 palette_len = sizeof(lv_color32_t) * LV_COLOR_INDEXED_PALETTE_SIZE(cf); + const lv_color32_t * palette; + const uint8_t * indexed_data = NULL; + uint32_t stride = dsc->header.stride; + + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + /*read palette for indexed image*/ + 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: %d", res); + lv_free((void *)palette); + return LV_RESULT_INVALID; + } + +#if LV_BIN_DECODER_RAM_LOAD + indexed_data = lv_draw_buf_malloc(stride * dsc->header.h, cf); + LV_ASSERT_MALLOC(indexed_data); + if(indexed_data == NULL) { + LV_LOG_ERROR("Draw buffer alloc failed"); + goto exit_with_buf; + } + + 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 *)indexed_data, data_len, &rn); + if(res != LV_FS_RES_OK || rn != data_len) { + LV_LOG_WARN("Read indexed image failed: %d", res); + goto exit_with_buf; + } +#endif + } + else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { + lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src; + palette = (lv_color32_t *)image->data; + indexed_data = image->data + palette_len; + } + else { + return LV_RESULT_INVALID; + } + + dsc->palette = palette; + dsc->palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); + +#if LV_BIN_DECODER_RAM_LOAD + /*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 = indexed_data; + uint8_t * out = img_data; + for(uint32_t y = 0; y < dsc->header.h; y++) { + decode_indexed_line(cf, dsc->palette, 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*/ + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + decoder_data->palette = (void *)palette; /*Free decoder data on close*/ + lv_draw_buf_free((void *)indexed_data); + } + + return LV_RESULT_OK; +exit_with_buf: + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + lv_free((void *)palette); + lv_draw_buf_free((void *)indexed_data); + } + return LV_RESULT_INVALID; +#else + LV_UNUSED(stride); + LV_UNUSED(indexed_data); + /*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; + decoder_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: %d", res); + 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; + decoder_data_t * decoder_data = dsc->user_data; + uint8_t bpp = lv_color_format_get_bpp(dsc->header.cf); + uint32_t w = (dsc->header.stride * 8) / bpp; + uint32_t buf_stride = (w * 8 + 7) >> 3; /*stride for img_data*/ + uint32_t buf_len = w * dsc->header.h; /*always decode to A8 format*/ + uint8_t * img_data = lv_draw_buf_malloc(buf_len, dsc->header.cf); + uint32_t file_len = (uint32_t)dsc->header.stride * dsc->header.h; + + LV_ASSERT_MALLOC(img_data); + if(img_data == NULL) { + LV_LOG_ERROR("Out of memory"); + return LV_RESULT_INVALID; + } + + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + res = fs_read_file_at(decoder_data->f, sizeof(lv_image_header_t), img_data, file_len, &rn); + if(res != LV_FS_RES_OK || rn != file_len) { + LV_LOG_WARN("Read header failed: %d", res); + lv_draw_buf_free(img_data); + return LV_RESULT_INVALID; + } + } + else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { + /*Copy from image data*/ + lv_memcpy(img_data, ((lv_image_dsc_t *)dsc->src)->data, file_len); + } + + if(dsc->header.cf != LV_COLOR_FORMAT_A8) { + /*Convert A1/2/4 to A8 from last pixel to first pixel*/ + uint8_t * in = img_data + file_len - 1; + uint8_t * out = img_data + buf_len - 1; + uint8_t mask = (1 << bpp) - 1; + uint8_t shift = 0; + for(uint32_t i = 0; i < buf_len; i++) { + /** + * Rounding error: + * Take bpp = 4 as example, alpha value of 0x0 to 0x0F should be + * mapped to 0x00 to 0xFF, thus the equation should be below Equation 3. + * + * But it involves division and multiplication, which is slow. So, if + * we ignore the rounding errors, Equation1, 2 could be faster. But it + * will either has error when alpha is 0xff or 0x00. + * + * We use Equation 3 here for maximum accuracy. + * + * Equation 1: *out = ((*in >> shift) & mask) << (8 - bpp); + * Equation 2: *out = ((((*in >> shift) & mask) + 1) << (8 - bpp)) - 1; + * Equation 3: *out = ((*in >> shift) & mask) * 255 / ((1L << bpp) - 1) ; + */ + *out = ((*in >> shift) & mask) * 255L / ((1L << bpp) - 1) ; + shift += bpp; + if(shift >= 8) { + shift = 0; + in--; + } + out--; + } + } + + decoder_data->img_data = img_data; + dsc->img_data = img_data; + dsc->header.stride = buf_stride; + dsc->header.cf = LV_COLOR_FORMAT_A8; + return LV_RESULT_OK; +} + +static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x, + int32_t w_px, const uint8_t * in, lv_color32_t * out) +{ + uint8_t px_size; + uint16_t mask; + + int8_t shift = 0; + switch(color_format) { + case LV_COLOR_FORMAT_I1: + px_size = 1; + in += x / 8; /*8pixel per byte*/ + shift = 7 - (x & 0x7); + break; + case LV_COLOR_FORMAT_I2: + px_size = 2; + in += x / 4; /*4pixel per byte*/ + shift = 6 - 2 * (x & 0x3); + break; + case LV_COLOR_FORMAT_I4: + px_size = 4; + in += x / 2; /*2pixel per byte*/ + shift = 4 - 4 * (x & 0x1); + break; + case LV_COLOR_FORMAT_I8: + px_size = 8; + in += x; + shift = 0; + break; + default: + return LV_RESULT_INVALID; + } + + mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/ + + int32_t i; + for(i = 0; i < w_px; i++) { + uint8_t val_act = (*in >> shift) & mask; + out[i] = palette[val_act]; + + shift -= px_size; + if(shift < 0) { + shift = 8 - px_size; + in++; + } + } + return LV_RESULT_OK; +} + +static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br) +{ + lv_fs_res_t res; + if(br) *br = 0; + + res = lv_fs_seek(f, pos, LV_FS_SEEK_SET); + if(res != LV_FS_RES_OK) { + return res; + } + + res |= lv_fs_read(f, buff, btr, br); + if(res != LV_FS_RES_OK) { + return res; + } + + return LV_FS_RES_OK; +} diff --git a/src/libs/bin_decoder/lv_bin_decoder.h b/src/libs/bin_decoder/lv_bin_decoder.h new file mode 100644 index 000000000..2966dac39 --- /dev/null +++ b/src/libs/bin_decoder/lv_bin_decoder.h @@ -0,0 +1,70 @@ +/** + * @file lv_bin_decoder.h + * + */ + +#ifndef LV_BIN_DECODER_H +#define LV_BIN_DECODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../draw/lv_image_decoder.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the binary image decoder module + */ +void lv_bin_decoder_init(void); + +/** + * Get info about a lvgl binary image + * @param decoder the decoder where this function belongs + * @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol + * @param header store the image data here + * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. + */ +lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header); + +lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, + const lv_area_t * full_area, lv_area_t * decoded_area); + +/** + * Open a lvgl binary image + * @param decoder the decoder where this function belongs + * @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it. + * @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error. + */ +lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); + +/** + * Close the pending decoding. Free resources etc. + * @param decoder pointer to the decoder the function associated with + * @param dsc pointer to decoder descriptor + */ +void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_BIN_DECODER_H*/ diff --git a/src/libs/rle/lv_rle_decoder.c b/src/libs/rle/lv_rle_decoder.c index 4a187e0eb..ac08ddce6 100644 --- a/src/libs/rle/lv_rle_decoder.c +++ b/src/libs/rle/lv_rle_decoder.c @@ -15,6 +15,7 @@ *********************/ #include #include "lv_rle_decoder.h" +#include "../bin_decoder/lv_bin_decoder.h" #if LV_USE_RLE @@ -490,7 +491,7 @@ static lv_res_t decoder_open(lv_image_decoder_t * decoder, data->decoder_dsc.src_type = LV_IMAGE_SRC_VARIABLE; data->decoder_dsc.src = &data->img_dsc; - res = lv_image_decoder_built_in_open(decoder, &data->decoder_dsc); + res = lv_bin_decoder_open(decoder, &data->decoder_dsc); if(res != LV_RES_OK) { lv_free(img_data); lv_free(data); @@ -507,7 +508,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * { if(dsc->user_data) { lv_rle_decoder_data_t * decoder_data = dsc->user_data; - lv_image_decoder_built_in_close(decoder, &decoder_data->decoder_dsc); + lv_bin_decoder_close(decoder, &decoder_data->decoder_dsc); if(decoder_data->img_dsc.data) lv_free((void *)decoder_data->img_dsc.data); lv_free(dsc->user_data); diff --git a/src/lv_api_map.h b/src/lv_api_map.h index 330514043..ebe448d4f 100644 --- a/src/lv_api_map.h +++ b/src/lv_api_map.h @@ -248,6 +248,9 @@ static inline void lv_obj_move_background(lv_obj_t * obj) #define LV_ZOOM_NONE LV_SCALE_NONE +#define lv_image_decoder_built_in_open lv_bin_decoder_open +#define lv_image_decoder_built_in_close lv_bin_decoder_close + /********************** * MACROS **********************/ diff --git a/src/lv_init.c b/src/lv_init.c index 1e458c826..1cc8439dd 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -11,6 +11,7 @@ #include "display/lv_display_private.h" #include "indev/lv_indev_private.h" #include "layouts/lv_layout.h" +#include "libs/bin_decoder/lv_bin_decoder.h" #include "libs/bmp/lv_bmp.h" #include "libs/ffmpeg/lv_ffmpeg.h" #include "libs/freetype/lv_freetype.h" @@ -178,6 +179,7 @@ void lv_init(void) #endif _lv_image_decoder_init(); + lv_bin_decoder_init(); /*LVGL built-in binary image decoder*/ _lv_cache_init(); _lv_cache_builtin_init();