fix(bin_decoder): fix memory leak (#5990)
@@ -244,6 +244,7 @@ open/close the PNG files. It should look like this:
|
|||||||
lv_image_decoder_t * dec = lv_image_decoder_create();
|
lv_image_decoder_t * dec = lv_image_decoder_create();
|
||||||
lv_image_decoder_set_info_cb(dec, decoder_info);
|
lv_image_decoder_set_info_cb(dec, decoder_info);
|
||||||
lv_image_decoder_set_open_cb(dec, decoder_open);
|
lv_image_decoder_set_open_cb(dec, decoder_open);
|
||||||
|
lv_image_decoder_set_get_area_cb(dec, decoder_get_area);
|
||||||
lv_image_decoder_set_close_cb(dec, decoder_close);
|
lv_image_decoder_set_close_cb(dec, decoder_close);
|
||||||
|
|
||||||
|
|
||||||
@@ -280,7 +281,7 @@ open/close the PNG files. It should look like this:
|
|||||||
/*Check whether the type `src` is known by the decoder*/
|
/*Check whether the type `src` is known by the decoder*/
|
||||||
if(is_png(dsc->src) == false) return LV_RESULT_INVALID;
|
if(is_png(dsc->src) == false) return LV_RESULT_INVALID;
|
||||||
|
|
||||||
/*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*/
|
/*Decode and store the image. If `dsc->decoded` is `NULL`, the `decoder_get_area` function will be called to get the image data line-by-line*/
|
||||||
dsc->decoded = my_png_decoder(dsc->src);
|
dsc->decoded = my_png_decoder(dsc->src);
|
||||||
|
|
||||||
/*Change the color format if decoded image format is different than original format. For PNG it's usually decoded to ARGB8888 format*/
|
/*Change the color format if decoded image format is different than original format. For PNG it's usually decoded to ARGB8888 format*/
|
||||||
@@ -296,13 +297,58 @@ open/close the PNG files. It should look like this:
|
|||||||
* Decode an area of image
|
* Decode an area of image
|
||||||
* @param decoder pointer to the decoder where this function belongs
|
* @param decoder pointer to the decoder where this function belongs
|
||||||
* @param dsc image decoder descriptor
|
* @param dsc image decoder descriptor
|
||||||
* @param full_area full image area information
|
* @param full_area input parameter. the full area to decode after enough subsequent calls
|
||||||
* @param decoded_area area information to decode (x1, y1, x2, y2)
|
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
|
||||||
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't decode image area
|
* the decoded area is stored here after each call.
|
||||||
|
* @return LV_RESULT_OK: ok; LV_RESULT_INVALID: failed or there is nothing left to decode
|
||||||
*/
|
*/
|
||||||
static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
static lv_result_t 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)
|
const lv_area_t * full_area, lv_area_t * decoded_area)
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* If `dsc->decoded` is always set in `decoder_open` then `decoder_get_area` does not need to be implemented.
|
||||||
|
* If `dsc->decoded` is only sometimes set or never set in `decoder_open` then `decoder_get_area` is used to
|
||||||
|
* incrementally decode the image by calling it repeatedly until it returns `LV_RESULT_INVALID`.
|
||||||
|
* In the example below the image is decoded line-by-line but the decoded area can have any shape and size
|
||||||
|
* depending on the requirements and capabilities of the image decoder.
|
||||||
|
*/
|
||||||
|
|
||||||
|
my_decoder_data_t * my_decoder_data = dsc->user_data;
|
||||||
|
|
||||||
|
/* if `decoded_area` has a field set to `LV_COORD_MIN` then reset decoding */
|
||||||
|
if(decoded_area->y1 == LV_COORD_MIN) {
|
||||||
|
decoded_area->x1 = full_area->x1;
|
||||||
|
decoded_area->x2 = full_area->x2;
|
||||||
|
decoded_area->y1 = full_area->y1;
|
||||||
|
decoded_area->y2 = decoded_area->y1; /* decode line-by-line, starting with the first line */
|
||||||
|
|
||||||
|
/* create a draw buf the size of one line */
|
||||||
|
bool reshape_success = NULL != lv_draw_buf_reshape(my_decoder_data->partial,
|
||||||
|
dsc->decoded.header.cf,
|
||||||
|
lv_area_get_width(full_area),
|
||||||
|
1,
|
||||||
|
LV_STRIDE_AUTO);
|
||||||
|
if(!reshape_success) {
|
||||||
|
lv_draw_buf_destroy(my_decoder_data->partial);
|
||||||
|
my_decoder_data->partial = lv_draw_buf_create(lv_area_get_width(full_area),
|
||||||
|
1,
|
||||||
|
dsc->decoded.header.cf,
|
||||||
|
LV_STRIDE_AUTO);
|
||||||
|
|
||||||
|
my_png_decode_line_reset(full_area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* otherwise decoding is already in progress. decode the next line */
|
||||||
|
else {
|
||||||
|
/* all lines have already been decoded. indicate completion by returning `LV_RESULT_INVALID` */
|
||||||
|
if (decoded_area->y1 >= full_area->y2) return LV_RESULT_INVALID;
|
||||||
|
decoded_area->y1++;
|
||||||
|
decoded_area->y2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_png_decode_line(my_decoder_data->partial);
|
||||||
|
|
||||||
|
return LV_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,8 +360,12 @@ open/close the PNG files. It should look like this:
|
|||||||
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
{
|
{
|
||||||
/*Free all allocated data*/
|
/*Free all allocated data*/
|
||||||
|
my_png_cleanup();
|
||||||
|
|
||||||
/*Call the built-in close function if the built-in open/read_line was used*/
|
my_decoder_data_t * my_decoder_data = dsc->user_data;
|
||||||
|
lv_draw_buf_destroy(my_decoder_data->partial);
|
||||||
|
|
||||||
|
/*Call the built-in close function if the built-in open/get_area was used*/
|
||||||
lv_bin_decoder_close(decoder, dsc);
|
lv_bin_decoder_close(decoder, dsc);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ void lv_example_barcode_1(void)
|
|||||||
|
|
||||||
/*Add a border with bg_color*/
|
/*Add a border with bg_color*/
|
||||||
lv_obj_set_style_border_color(barcode, bg_color, 0);
|
lv_obj_set_style_border_color(barcode, bg_color, 0);
|
||||||
lv_obj_set_style_border_width(barcode, 5, 0);
|
|
||||||
|
|
||||||
/*Set data*/
|
/*Set data*/
|
||||||
lv_barcode_update(barcode, "https://lvgl.io");
|
lv_barcode_update(barcode, "https://lvgl.io");
|
||||||
|
|||||||
@@ -82,15 +82,14 @@ typedef lv_result_t (*lv_image_decoder_info_f_t)(lv_image_decoder_t * decoder, c
|
|||||||
typedef lv_result_t (*lv_image_decoder_open_f_t)(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
typedef lv_result_t (*lv_image_decoder_open_f_t)(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
|
* Decode `full_area` pixels incrementally by calling in a loop. Set `decoded_area` values to `LV_COORD_MIN` on first call.
|
||||||
* Required only if the "open" function can't return with the whole decoded pixel array.
|
* Required only if the "open" function can't return with the whole decoded pixel array.
|
||||||
* @param decoder pointer to the decoder the function associated with
|
* @param decoder pointer to the decoder the function associated with
|
||||||
* @param dsc pointer to decoder descriptor
|
* @param dsc pointer to decoder descriptor
|
||||||
* @param x start x coordinate
|
* @param full_area input parameter. the full area to decode after enough subsequent calls
|
||||||
* @param y start y coordinate
|
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
|
||||||
* @param len number of pixels to decode
|
* the decoded area is stored here after each call.
|
||||||
* @param buf a buffer to store the decoded pixels
|
* @return LV_RESULT_OK: ok; LV_RESULT_INVALID: failed or there is nothing left to decode
|
||||||
* @return LV_RESULT_OK: ok; LV_RESULT_INVALID: failed
|
|
||||||
*/
|
*/
|
||||||
typedef lv_result_t (*lv_image_decoder_get_area_cb_t)(lv_image_decoder_t * decoder,
|
typedef lv_result_t (*lv_image_decoder_get_area_cb_t)(lv_image_decoder_t * decoder,
|
||||||
lv_image_decoder_dsc_t * dsc,
|
lv_image_decoder_dsc_t * dsc,
|
||||||
@@ -217,12 +216,13 @@ lv_result_t lv_image_decoder_get_info(const void * src, lv_image_header_t * head
|
|||||||
*/
|
*/
|
||||||
lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args);
|
lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args);
|
||||||
|
|
||||||
/**
|
/***
|
||||||
* Decode an area of the opened image
|
* Decode `full_area` pixels incrementally by calling in a loop. Set `decoded_area` to `LV_COORD_MIN` on first call.
|
||||||
* @param dsc image decoder descriptor
|
* @param dsc image decoder descriptor
|
||||||
* @param full_area start X coordinate (from left)
|
* @param full_area input parameter. the full area to decode after enough subsequent calls
|
||||||
* @param decoded_area start Y coordinate (from top)
|
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
|
||||||
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: an error occurred
|
* the decoded area is stored here after each call.
|
||||||
|
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: an error occurred or there is nothing left to decode
|
||||||
*/
|
*/
|
||||||
lv_result_t lv_image_decoder_get_area(lv_image_decoder_dsc_t * dsc, const lv_area_t * full_area,
|
lv_result_t lv_image_decoder_get_area(lv_image_decoder_dsc_t * dsc, const lv_area_t * full_area,
|
||||||
lv_area_t * decoded_area);
|
lv_area_t * decoded_area);
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
|
|||||||
|
|
||||||
free_decoder_data(dsc);
|
free_decoder_data(dsc);
|
||||||
|
|
||||||
if(dsc->cache_entry) {
|
if(dsc->cache && dsc->cache_entry) {
|
||||||
/*Decoded data is in cache, release it from cache's callback*/
|
/*Decoded data is in cache, release it from cache's callback*/
|
||||||
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
|
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
|
||||||
}
|
}
|
||||||
@@ -407,23 +407,20 @@ lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod
|
|||||||
/*We only support read line by line for now*/
|
/*We only support read line by line for now*/
|
||||||
if(decoded_area->y1 == LV_COORD_MIN) {
|
if(decoded_area->y1 == LV_COORD_MIN) {
|
||||||
/*Indexed image is converted to ARGB888*/
|
/*Indexed image is converted to ARGB888*/
|
||||||
uint32_t len = LV_COLOR_FORMAT_IS_INDEXED(cf) ? sizeof(lv_color32_t) * 8 : bpp;
|
|
||||||
lv_color_format_t cf_decoded = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf;
|
lv_color_format_t cf_decoded = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf;
|
||||||
|
|
||||||
len = (len * w_px) / 8;
|
decoded = lv_draw_buf_reshape(decoder_data->decoded_partial, cf_decoded, w_px, 1, LV_STRIDE_AUTO);
|
||||||
decoded = decoder_data->decoded_partial;
|
if(decoded == NULL) {
|
||||||
if(decoded && decoded->header.w == w_px) {
|
if(decoder_data->decoded_partial != NULL) {
|
||||||
/*Use existing one directly*/
|
lv_draw_buf_destroy(decoder_data->decoded_partial);
|
||||||
|
decoder_data->decoded_partial = NULL;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
decoded = lv_draw_buf_create(w_px, 1, cf_decoded, LV_STRIDE_AUTO);
|
decoded = lv_draw_buf_create(w_px, 1, cf_decoded, LV_STRIDE_AUTO);
|
||||||
if(decoded == NULL)
|
if(decoded == NULL) return LV_RESULT_INVALID;
|
||||||
return LV_RESULT_INVALID;
|
decoder_data->decoded_partial = decoded; /*Free on decoder close*/
|
||||||
}
|
}
|
||||||
|
|
||||||
*decoded_area = *full_area;
|
*decoded_area = *full_area;
|
||||||
decoded_area->y2 = decoded_area->y1;
|
decoded_area->y2 = decoded_area->y1;
|
||||||
decoder_data->decoded_partial = decoded; /*Free on decoder close*/
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
decoded_area->y1++;
|
decoded_area->y1++;
|
||||||
|
|||||||
@@ -200,7 +200,20 @@ static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod
|
|||||||
if(decoded_area->y1 == LV_COORD_MIN) {
|
if(decoded_area->y1 == LV_COORD_MIN) {
|
||||||
*decoded_area = *full_area;
|
*decoded_area = *full_area;
|
||||||
decoded_area->y2 = decoded_area->y1;
|
decoded_area->y2 = decoded_area->y1;
|
||||||
if(decoded == NULL) decoded = lv_draw_buf_create(lv_area_get_width(full_area), 1, dsc->header.cf, LV_STRIDE_AUTO);
|
int32_t w_px = lv_area_get_width(full_area);
|
||||||
|
lv_draw_buf_t * reshaped = lv_draw_buf_reshape(decoded, dsc->header.cf, w_px, 1, LV_STRIDE_AUTO);
|
||||||
|
if(reshaped == NULL) {
|
||||||
|
if(decoded != NULL) {
|
||||||
|
lv_draw_buf_destroy(decoded);
|
||||||
|
decoded = NULL;
|
||||||
|
dsc->decoded = NULL;
|
||||||
|
}
|
||||||
|
decoded = lv_draw_buf_create(w_px, 1, dsc->header.cf, LV_STRIDE_AUTO);
|
||||||
|
if(decoded == NULL) return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decoded = reshaped;
|
||||||
|
}
|
||||||
dsc->decoded = decoded;
|
dsc->decoded = decoded;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -216,8 +216,6 @@ static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod
|
|||||||
|
|
||||||
JDEC * jd = dsc->user_data;
|
JDEC * jd = dsc->user_data;
|
||||||
lv_draw_buf_t * decoded = (void *)dsc->decoded;
|
lv_draw_buf_t * decoded = (void *)dsc->decoded;
|
||||||
if(decoded == NULL) decoded = lv_malloc_zeroed(sizeof(lv_draw_buf_t));
|
|
||||||
dsc->decoded = decoded;
|
|
||||||
|
|
||||||
uint32_t mx, my;
|
uint32_t mx, my;
|
||||||
mx = jd->msx * 8;
|
mx = jd->msx * 8;
|
||||||
@@ -231,6 +229,15 @@ static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod
|
|||||||
jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */
|
jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */
|
||||||
jd->rst = 0;
|
jd->rst = 0;
|
||||||
jd->rsc = 0;
|
jd->rsc = 0;
|
||||||
|
if(decoded == NULL) {
|
||||||
|
decoded = lv_malloc_zeroed(sizeof(lv_draw_buf_t));
|
||||||
|
dsc->decoded = decoded;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lv_fs_seek(jd->device, 0, LV_FS_SEEK_SET);
|
||||||
|
JRESULT rc = jd_prepare(jd, input_func, jd->pool_original, (size_t)TJPGD_WORKBUFF_SIZE, jd->device);
|
||||||
|
if(rc) return rc;
|
||||||
|
}
|
||||||
decoded->data = jd->workbuf;
|
decoded->data = jd->workbuf;
|
||||||
decoded->header = dsc->header;
|
decoded->header = dsc->header;
|
||||||
decoded->header.stride = mx * 3;
|
decoded->header.stride = mx * 3;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,91 @@
|
|||||||
|
#if LV_BUILD_TEST
|
||||||
|
#include "../lvgl.h"
|
||||||
|
|
||||||
|
#include "unity/unity.h"
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
/* Function run before every test */
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_image(const void * src)
|
||||||
|
{
|
||||||
|
lv_obj_t * img = lv_image_create(lv_screen_active());
|
||||||
|
lv_image_set_src(img, src);
|
||||||
|
lv_obj_center(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bin_decoder(const void * src, const char * screenshot)
|
||||||
|
{
|
||||||
|
create_image(src);
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT(screenshot);
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
|
||||||
|
size_t mem_before = lv_test_get_free_mem();
|
||||||
|
for(uint32_t i = 0; i < 20; i++) {
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
create_image(src);
|
||||||
|
|
||||||
|
lv_obj_invalidate(lv_screen_active());
|
||||||
|
lv_refr_now(NULL);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT(screenshot);
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_image_tile(const void * src)
|
||||||
|
{
|
||||||
|
lv_obj_t * img = lv_image_create(lv_screen_active());
|
||||||
|
lv_image_set_src(img, src);
|
||||||
|
lv_obj_center(img);
|
||||||
|
lv_obj_set_size(img, 275, 175);
|
||||||
|
lv_image_set_inner_align(img, LV_IMAGE_ALIGN_TILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bin_decoder_tile(const void * src, const char * screenshot)
|
||||||
|
{
|
||||||
|
create_image_tile(src);
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT(screenshot);
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
|
||||||
|
size_t mem_before = lv_test_get_free_mem();
|
||||||
|
for(uint32_t i = 0; i < 20; i++) {
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
create_image_tile(src);
|
||||||
|
|
||||||
|
lv_obj_invalidate(lv_screen_active());
|
||||||
|
lv_refr_now(NULL);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT(screenshot);
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_bin_decoder_i4(void)
|
||||||
|
{
|
||||||
|
LV_IMAGE_DECLARE(test_image_cogwheel_i4);
|
||||||
|
bin_decoder(&test_image_cogwheel_i4, "libs/bin_decoder_1.png");
|
||||||
|
}
|
||||||
|
void test_bin_decoder_i4_tile(void)
|
||||||
|
{
|
||||||
|
LV_IMAGE_DECLARE(test_image_cogwheel_i4);
|
||||||
|
bin_decoder_tile(&test_image_cogwheel_i4, "libs/bin_decoder_2.png");
|
||||||
|
}
|
||||||
|
void test_bin_decoder_argb8888(void)
|
||||||
|
{
|
||||||
|
LV_IMAGE_DECLARE(test_image_cogwheel_argb8888);
|
||||||
|
bin_decoder(&test_image_cogwheel_argb8888, "libs/bin_decoder_3.png");
|
||||||
|
}
|
||||||
|
void test_bin_decoder_argb8888_tile(void)
|
||||||
|
{
|
||||||
|
LV_IMAGE_DECLARE(test_image_cogwheel_argb8888);
|
||||||
|
bin_decoder_tile(&test_image_cogwheel_argb8888, "libs/bin_decoder_4.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#if LV_BUILD_TEST
|
||||||
|
#include "../lvgl.h"
|
||||||
|
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "lv_test_helpers.h"
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
/* Function run before every test */
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_image(void)
|
||||||
|
{
|
||||||
|
lv_obj_t * img = lv_image_create(lv_screen_active());
|
||||||
|
lv_image_set_src(img, "A:src/test_assets/test_img_lvgl_logo.bmp");
|
||||||
|
lv_obj_center(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_bmp(void)
|
||||||
|
{
|
||||||
|
create_image();
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/bmp_1.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
|
||||||
|
size_t mem_before = lv_test_get_free_mem();
|
||||||
|
for(uint32_t i = 0; i < 20; i++) {
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
create_image();
|
||||||
|
|
||||||
|
lv_obj_invalidate(lv_screen_active());
|
||||||
|
lv_refr_now(NULL);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/bmp_1.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_image_tile(void)
|
||||||
|
{
|
||||||
|
lv_obj_t * img = lv_image_create(lv_screen_active());
|
||||||
|
lv_image_set_src(img, "A:src/test_assets/test_img_lvgl_logo.bmp");
|
||||||
|
lv_obj_center(img);
|
||||||
|
lv_obj_set_size(img, 300, 200);
|
||||||
|
lv_image_set_inner_align(img, LV_IMAGE_ALIGN_TILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_bmp_align_tile(void)
|
||||||
|
{
|
||||||
|
create_image_tile();
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/bmp_2.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
|
||||||
|
size_t mem_before = lv_test_get_free_mem();
|
||||||
|
for(uint32_t i = 0; i < 20; i++) {
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
create_image_tile();
|
||||||
|
|
||||||
|
lv_obj_invalidate(lv_screen_active());
|
||||||
|
lv_refr_now(NULL);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/bmp_2.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -11,7 +11,7 @@ void setUp(void)
|
|||||||
|
|
||||||
void tearDown(void)
|
void tearDown(void)
|
||||||
{
|
{
|
||||||
/* Function run after every test */
|
lv_obj_clean(lv_screen_active());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_images(void)
|
static void create_images(void)
|
||||||
@@ -64,4 +64,39 @@ void test_tjpgd_1(void)
|
|||||||
lv_libjpeg_turbo_init();
|
lv_libjpeg_turbo_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_image_2(void)
|
||||||
|
{
|
||||||
|
LV_IMG_DECLARE(test_img_lvgl_logo_jpg);
|
||||||
|
lv_obj_t * img = lv_image_create(lv_screen_active());
|
||||||
|
lv_image_set_src(img, &test_img_lvgl_logo_jpg);
|
||||||
|
lv_obj_center(img);
|
||||||
|
lv_obj_set_size(img, 300, 200);
|
||||||
|
lv_image_set_inner_align(img, LV_IMAGE_ALIGN_TILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_jdpgd_align_tile(void)
|
||||||
|
{
|
||||||
|
/* Temporarily remove libjpeg_turbo decoder */
|
||||||
|
lv_libjpeg_turbo_deinit();
|
||||||
|
|
||||||
|
create_image_2();
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/jpg_3.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
|
||||||
|
size_t mem_before = lv_test_get_free_mem();
|
||||||
|
for(uint32_t i = 0; i < 20; i++) {
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
create_image_2();
|
||||||
|
|
||||||
|
lv_obj_invalidate(lv_screen_active());
|
||||||
|
lv_refr_now(NULL);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("libs/jpg_3.png");
|
||||||
|
lv_obj_clean(lv_screen_active());
|
||||||
|
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
|
||||||
|
|
||||||
|
/* Re-add libjpeg_turbo decoder */
|
||||||
|
lv_libjpeg_turbo_init();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||