diff --git a/docs/overview/img.rst b/docs/overview/img.rst index 0df85b6337..ede9b76d9f 100644 --- a/docs/overview/img.rst +++ b/docs/overview/img.rst @@ -258,7 +258,7 @@ open/close the PNG files. It should look like this: } /** - * Open a PNG image and decode it into dsc.img_data + * Open a PNG image and decode it into dsc.decoded * @param decoder pointer to the decoder where this function belongs * @param dsc image descriptor * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image @@ -270,11 +270,11 @@ open/close the PNG files. It should look like this: /*Check whether the type `src` is known by the decoder*/ if(is_png(dsc->src) == false) return LV_RESULT_INVALID; - /*Decode and store the image. If `dsc->img_data` is `NULL`, the `read_line` function will be called to get the image data line-by-line*/ - dsc->img_data = my_png_decoder(dsc->src); + /*Decode and store the image. If `dsc->decoded` is `NULL`, the `read_line` function will be called to get the image data line-by-line*/ + dsc->decoded = my_png_decoder(dsc->src); - /*Change the color format if required. For PNG usually 'Raw' is fine*/ - dsc->header.cf = LV_COLOR_FORMAT_... + /*Change the color format if decoded image format is different than original format. For PNG it's usually decoded to ARGB8888 format*/ + dsc->decoded.header.cf = LV_COLOR_FORMAT_... /*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); @@ -316,13 +316,13 @@ So in summary: - In ``decoder_open``, you should try to open the image source pointed by ``dsc->src``. Its type is already in ``dsc->src_type == LV_IMG_SRC_FILE/VARIABLE``. If this format/type is not supported by the decoder, return :cpp:enumerator:`LV_RESULT_INVALID`. - However, if you can open the image, a pointer to the decoded *True color* image should be - set in ``dsc->img_data``. If the format is known, but you don't want to - decode the entire image (e.g. no memory for it), set ``dsc->img_data = NULL`` and + However, if you can open the image, a pointer to the decoded image should be + set in ``dsc->decoded``. If the format is known, but you don't want to + decode the entire image (e.g. no memory for it), set ``dsc->decoded = NULL`` and use ``decoder_get_area`` to get the image area pixels. - In ``decoder_close`` you should free all allocated resources. - ``decoder_get_area`` is optional. In this case you should decode the whole image In - ``decoder_open`` function and store image data in ``dsc->img_data``. + ``decoder_open`` function and store image data in ``dsc->decoded``. Decoding the whole image requires extra memory and some computational overhead. @@ -346,7 +346,7 @@ images to tell color of the image. res = lv_image_decoder_open(&dsc, &my_img_dsc, &args); if(res == LV_RESULT_OK) { - /*Do something with `dsc->img_data`*/ + /*Do something with `dsc->decoded`. You can copy out the decoded image by `lv_draw_buf_dup(dsc.decoded)`*/ lv_image_decoder_close(&dsc); } @@ -385,61 +385,26 @@ See the detailed code below: lv_cache_entry_t * entry = dsc->cache_entry; if(!(entry->process_state & IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA)) { - lv_color32_t * image = (lv_color32_t *)dsc->img_data; - uint32_t px_cnt = dsc->header.w * dsc->header.h; - - /* premultiply alpha */ - while(px_cnt--) { - image->red = LV_UDIV255(image->red * image->alpha); - image->green = LV_UDIV255(image->green * image->alpha); - image->blue = LV_UDIV255(image->blue * image->alpha); - image++; - } - + lv_draw_buf_premultiply(dsc->decoded); LV_LOG_USER("premultiplied alpha OK"); entry->process_state |= IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA; } if(!(entry->process_state & IMAGE_PROCESS_STATE_STRIDE_ALIGNED)) { - int32_t image_w = dsc->header.w; - int32_t image_h = dsc->header.h; - uint32_t width_byte = image_w * lv_color_format_get_size(color_format); - uint32_t stride = lv_draw_buf_width_to_stride(image_w, color_format); + uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf); + if(decoded->header.stride != stride_expect) { + LV_LOG_WARN("Stride mismatch"); + lv_draw_buf_t * aligned = lv_draw_buf_adjust_stride(decoded, stride_expect); + if(aligned == NULL) { + LV_LOG_ERROR("No memory for Stride adjust."); + return NULL; + } - /* Check stride alignment requirements */ - if(stride != width_byte) { - LV_LOG_USER("need to realign stride: %" LV_PRIu32 " -> %" LV_PRIu32, width_byte, stride); + decoded = aligned; + } - const uint8_t * ori_image = lv_cache_get_data(entry); - size_t size_bytes = stride * dsc->header.h; - uint8_t * new_image = lv_draw_buf_malloc(size_bytes, color_format); - if(!new_image) { - LV_LOG_ERROR("alloc failed"); - res = LV_RESULT_INVALID; - goto alloc_failed; - } - - /* Replace the image data pointer */ - entry->data = new_image; - dsc->img_data = new_image; - - /* Copy image data */ - const uint8_t * cur = ori_image; - for(int32_t y = 0; y < image_h; y++) { - lv_memcpy(new_image, cur, width_byte); - new_image += stride; - cur += width_byte; - } - - /* free memory for old image */ - lv_draw_buf_free((void *)ori_image); - } - else { - LV_LOG_USER("no need to realign stride: %" LV_PRIu32, stride); - } - - entry->process_state |= IMAGE_PROCESS_STATE_STRIDE_ALIGNED; + entry->process_state |= IMAGE_PROCESS_STATE_STRIDE_ALIGNED; } alloc_failed: @@ -482,7 +447,7 @@ inefficient and detrimental to the user experience. Therefore, LVGL caches image data. Caching means some images will be left open, hence LVGL can quickly access them from -``dsc->img_data`` instead of needing to decode them again. +``dsc->decoded`` instead of needing to decode them again. Of course, caching images is resource intensive as it uses more RAM to store the decoded image. LVGL tries to optimize the process as much as diff --git a/src/draw/lv_draw_image.c b/src/draw/lv_draw_image.c index e3ce76687c..e64aaf1775 100644 --- a/src/draw/lv_draw_image.c +++ b/src/draw/lv_draw_image.c @@ -210,7 +210,7 @@ static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_ sup.palette_size = decoder_dsc->palette_size; /*The whole image is available, just draw it*/ - if(decoder_dsc->decoded || decoder_dsc->img_data) { + if(decoder_dsc->decoded) { draw_core_cb(draw_unit, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area); } /*Draw in smaller pieces*/ diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index ebea3a6570..e24c2e7e86 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -171,7 +171,6 @@ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src lv_memzero(&dsc->header, sizeof(lv_image_header_t)); dsc->error_msg = NULL; - dsc->img_data = NULL; dsc->decoded = NULL; dsc->cache_entry = NULL; dsc->user_data = NULL; diff --git a/src/draw/lv_image_decoder.h b/src/draw/lv_image_decoder.h index 288cfeddf8..b5f77e77a3 100644 --- a/src/draw/lv_image_decoder.h +++ b/src/draw/lv_image_decoder.h @@ -145,10 +145,8 @@ typedef struct _lv_image_decoder_dsc_t { /**Info about the opened image: color format, size, etc. MUST be set in `open` function*/ lv_image_header_t header; - /** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format. - * MUST be set in `open` function*/ - const uint8_t * img_data; - + /** Pointer to a draw buffer where the image's data (pixels) are stored in a decoded, plain format. + * MUST be set in `open` or `get_area_cb`function*/ const lv_draw_buf_t * decoded; /*A draw buffer to described decoded image.*/ const lv_color32_t * palette; @@ -175,14 +173,6 @@ typedef struct _lv_image_decoder_dsc_t { * GLOBAL PROTOTYPES **********************/ -/** - * @todo remove it when all decoder migrates to new draw buf interface. - */ -static inline const void * _lv_image_decoder_get_data(const lv_image_decoder_dsc_t * dsc) -{ - return dsc->decoded ? dsc->decoded->data : dsc->img_data; -} - /** * Initialize the image decoder module */ @@ -215,7 +205,7 @@ lv_result_t lv_image_decoder_get_info(const void * src, lv_image_header_t * head * 3) Symbol: E.g. `LV_SYMBOL_OK` * @param color The color of the image with `LV_COLOR_FORMAT_ALPHA_...` * @param args args about how the image should be opened. - * @return LV_RESULT_OK: opened the image. `dsc->img_data` and `dsc->header` are set. + * @return LV_RESULT_OK: opened the image. `dsc->decoded` and `dsc->header` are set. * LV_RESULT_INVALID: none of the registered image decoders were able to open the image. */ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args); diff --git a/src/draw/renesas/dave2d/lv_draw_dave2d_image.c b/src/draw/renesas/dave2d/lv_draw_dave2d_image.c index 217dccca6d..dd9499e11a 100644 --- a/src/draw/renesas/dave2d/lv_draw_dave2d_image.c +++ b/src/draw/renesas/dave2d/lv_draw_dave2d_image.c @@ -55,14 +55,16 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup, const lv_area_t * img_coords, const lv_area_t * clipped_img_area) { + if(decoder_dsc->decoded == NULL) return; lv_draw_dave2d_unit_t * u = (lv_draw_dave2d_unit_t *)u_base; bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE ? true : false; - const uint8_t * src_buf = decoder_dsc->img_data; - const lv_image_header_t * header = &decoder_dsc->header; + lv_draw_buf_t decoded = decoder_dsc->decoded; + const uint8_t * src_buf = decoded->data; + const lv_image_header_t * header = &decoder_dsc->decoded.header; lv_area_t buffer_area; lv_area_t draw_area; lv_area_t clipped_area; @@ -78,14 +80,6 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d uint32_t img_stride = header->stride; lv_color_format_t cf = header->cf; - cf = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf; - - if(decoder_dsc->decoded) { - src_buf = decoder_dsc->decoded->data; - img_stride = decoder_dsc->decoded->header.stride; - cf = decoder_dsc->decoded->header.cf; - } - #if LV_USE_OS lv_result_t status; status = lv_mutex_lock(u->pd2Mutex); @@ -131,7 +125,7 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d #if defined(RENESAS_CORTEX_M85) #if (BSP_CFG_DCACHE_ENABLED) d1_cacheblockflush(u->d2_handle, 0, src_buf, - img_stride * decoder_dsc->header.h); //Stride is in bytes, not pixels/texels + img_stride * header->h); //Stride is in bytes, not pixels/texels #endif #endif @@ -162,14 +156,14 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d lv_point_t p[4] = { //Points in clockwise order {0, 0}, - {decoder_dsc->header.w - 1, 0}, - {decoder_dsc->header.w - 1, decoder_dsc->header.h - 1}, - {0, decoder_dsc->header.h - 1}, + {header->w - 1, 0}, + {header->w - 1, header->h - 1}, + {0, header->h - 1}, }; d2_settexture(u->d2_handle, (void *)src_buf, (d2_s32)(img_stride / lv_color_format_get_size(header->cf)), - decoder_dsc->header.w, decoder_dsc->header.h, lv_draw_dave2d_lv_colour_fmt_to_d2_fmt(header->cf)); + header->w, header->h, lv_draw_dave2d_lv_colour_fmt_to_d2_fmt(header->cf)); d2_settexturemode(u->d2_handle, d2_tm_filter); d2_setfillmode(u->d2_handle, d2_fm_texture); diff --git a/src/draw/sw/lv_draw_sw_arc.c b/src/draw/sw/lv_draw_sw_arc.c index 6030e70577..c425610b32 100644 --- a/src/draw/sw/lv_draw_sw_arc.c +++ b/src/draw/sw/lv_draw_sw_arc.c @@ -131,7 +131,7 @@ void lv_draw_sw_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, c int32_t ofs = decoder_dsc.header.w / 2; lv_area_move(&img_area, dsc->center.x - ofs, dsc->center.y - ofs); blend_dsc.src_area = &img_area; - blend_dsc.src_buf = _lv_image_decoder_get_data(&decoder_dsc); + blend_dsc.src_buf = decoder_dsc.decoded->data; blend_dsc.src_color_format = decoder_dsc.decoded->header.cf; blend_dsc.src_stride = decoder_dsc.decoded->header.stride; } diff --git a/src/draw/sw/lv_draw_sw_img.c b/src/draw/sw/lv_draw_sw_img.c index 6283cb4774..ae46abcde0 100644 --- a/src/draw/sw/lv_draw_sw_img.c +++ b/src/draw/sw/lv_draw_sw_img.c @@ -173,18 +173,11 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t draw_dsc->scale_y != LV_SCALE_NONE ? true : false; lv_draw_sw_blend_dsc_t blend_dsc; - const uint8_t * src_buf = decoder_dsc->img_data; - const lv_image_header_t * header = &decoder_dsc->header; - uint32_t img_stride = header->stride; - lv_color_format_t cf = header->cf; - - cf = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf; - - if(decoder_dsc->decoded) { - src_buf = decoder_dsc->decoded->data; - img_stride = decoder_dsc->decoded->header.stride; - cf = decoder_dsc->decoded->header.cf; - } + const lv_draw_buf_t * decoded = decoder_dsc->decoded; + const uint8_t * src_buf = decoded->data; + const lv_image_header_t * header = &decoded->header; + uint32_t img_stride = decoded->header.stride; + lv_color_format_t cf = decoded->header.cf; lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); blend_dsc.opa = draw_dsc->opa; diff --git a/src/draw/sw/lv_draw_sw_vector.c b/src/draw/sw/lv_draw_sw_vector.c index c709b2d1aa..2cd14a8db4 100644 --- a/src/draw/sw/lv_draw_sw_vector.c +++ b/src/draw/sw/lv_draw_sw_vector.c @@ -298,13 +298,13 @@ static void _set_paint_fill_pattern(Tvg_Paint * obj, Tvg_Canvas * canvas, const return; } - if(!decoder_dsc.decoded && !decoder_dsc.img_data) { + if(!decoder_dsc.decoded) { lv_image_decoder_close(&decoder_dsc); LV_LOG_ERROR("Image not ready"); return; } - const uint8_t * src_buf = _lv_image_decoder_get_data(&decoder_dsc); + const uint8_t * src_buf = decoder_dsc.decoded->data; const lv_image_header_t * header = &decoder_dsc.header; lv_color_format_t cf = header->cf; diff --git a/src/libs/ffmpeg/lv_ffmpeg.c b/src/libs/ffmpeg/lv_ffmpeg.c index d7e2b31c0c..453bacac14 100644 --- a/src/libs/ffmpeg/lv_ffmpeg.c +++ b/src/libs/ffmpeg/lv_ffmpeg.c @@ -50,6 +50,7 @@ struct ffmpeg_context_s { int video_dst_linesize[4]; enum AVPixelFormat video_dst_pix_fmt; bool has_alpha; + lv_draw_buf_t draw_buf; }; #pragma pack(1) @@ -300,7 +301,13 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d uint8_t * img_data = ffmpeg_get_image_data(ffmpeg_ctx); dsc->user_data = ffmpeg_ctx; - dsc->img_data = img_data; + lv_draw_buf_t * decoded = &ffmpeg_ctx->draw_buf; + decoded->header = dsc->header; + decoded->header.flags |= LV_IMAGE_FLAGS_MODIFIABLE; + decoded->data = img_data; + decoded->data_size = (uint32_t)dsc->header.stride * dsc->header.h; + decoded->unaligned_data = NULL; + dsc->decoded = decoded; /* The image is fully decoded. Return with its pointer */ return LV_RESULT_OK; @@ -575,6 +582,7 @@ static int ffmpeg_get_image_header(const char * filepath, header->w = video_dec_ctx->width; header->h = video_dec_ctx->height; header->cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE; + header->stride = header->w * lv_color_format_get_size(header->cf); ret = 0; } diff --git a/src/libs/lodepng/lv_lodepng.c b/src/libs/lodepng/lv_lodepng.c index 0a058ae888..df77001706 100644 --- a/src/libs/lodepng/lv_lodepng.c +++ b/src/libs/lodepng/lv_lodepng.c @@ -251,13 +251,12 @@ static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_si { unsigned png_width; /*Not used, just required by the decoder*/ unsigned png_height; /*Not used, just required by the decoder*/ - uint8_t * img_data = NULL; + lv_draw_buf_t * decoded = NULL; - lv_draw_buf_t * decoded; /*Decode the image in ARGB8888 */ unsigned error = lodepng_decode32((unsigned char **)&decoded, &png_width, &png_height, png_data, png_data_size); if(error) { - if(img_data != NULL) lv_draw_buf_destroy(decoded); + if(decoded != NULL) lv_draw_buf_destroy(decoded); return NULL; }