From ad81f1f8cc3abb252c221d85d54f1b34ef6828c7 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Tue, 6 Feb 2024 17:53:14 +0800 Subject: [PATCH] Stride adjust in place (#5423) Signed-off-by: Xu Xingliang --- src/draw/lv_draw_buf.c | 50 +++++++++----- src/draw/lv_draw_buf.h | 9 ++- src/draw/lv_image_decoder.c | 16 +++-- tests/src/test_cases/test_draw_buf_stride.c | 74 +++++++++++++++++++++ 4 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 tests/src/test_cases/test_draw_buf_stride.c diff --git a/src/draw/lv_draw_buf.c b/src/draw/lv_draw_buf.c index 9a69e3c72..2505eb83e 100644 --- a/src/draw/lv_draw_buf.c +++ b/src/draw/lv_draw_buf.c @@ -270,12 +270,12 @@ void * lv_draw_buf_goto_xy(const lv_draw_buf_t * buf, uint32_t x, uint32_t y) return data + x * lv_color_format_get_size(buf->header.cf); } -lv_draw_buf_t * lv_draw_buf_adjust_stride(const lv_draw_buf_t * src, uint32_t stride) +lv_result_t lv_draw_buf_adjust_stride(lv_draw_buf_t * src, uint32_t stride) { LV_ASSERT_NULL(src); LV_ASSERT_NULL(src->data); - if(src == NULL) return NULL; - if(src->data == NULL) return NULL; + if(src == NULL) return LV_RESULT_INVALID; + if(src->data == NULL) return LV_RESULT_INVALID; const lv_image_header_t * header = &src->header; @@ -283,32 +283,48 @@ lv_draw_buf_t * lv_draw_buf_adjust_stride(const lv_draw_buf_t * src, uint32_t st if(stride == 0) stride = lv_draw_buf_width_to_stride(header->w, header->cf); /*Check if stride already match*/ - if(header->stride == stride) return NULL; + if(header->stride == stride) return LV_RESULT_OK; /*Calculate the minimal stride allowed from bpp*/ uint32_t bpp = lv_color_format_get_bpp(header->cf); uint32_t min_stride = (header->w * bpp + 7) >> 3; if(stride < min_stride) { LV_LOG_WARN("New stride is too small. min: %" LV_PRId32, min_stride); - return NULL; + return LV_RESULT_INVALID; } - lv_draw_buf_t * dst = lv_draw_buf_create(header->w, header->h, header->cf, stride); - if(dst == NULL) return NULL; + /*Check if buffer has enough space. */ + uint32_t new_size = _calculate_draw_buf_size(header->w, header->h, header->cf, stride); + if(new_size > src->data_size) { + return LV_RESULT_INVALID; + } uint32_t offset = LV_COLOR_INDEXED_PALETTE_SIZE(header->cf) * 4; - if(offset) lv_memcpy(dst->data, src->data, offset); - uint8_t * dst_data = dst->data; - dst_data += offset; - const uint8_t * src_data = src->data; - src_data += offset; - for(int32_t y = 0; y < src->header.h; y++) { - lv_memcpy(dst_data, src_data, min_stride); - src_data += src->header.stride; - dst_data += dst->header.stride; + + if(stride > header->stride) { + /*Copy from the last line to the first*/ + uint8_t * src_data = src->data + offset + header->stride * (header->h - 1); + uint8_t * dst_data = src->data + offset + stride * (header->h - 1); + for(; src_data != src->data;) { + lv_memmove(dst_data, src_data, min_stride); + src_data -= header->stride; + dst_data -= stride; + } + } + else { + /*Copy from the first line to the last*/ + uint8_t * src_data = src->data + offset; + uint8_t * dst_data = src->data + offset; + for(uint32_t y = 0; y < header->h; y++) { + lv_memmove(dst_data, src_data, min_stride); + src_data += header->stride; + dst_data += stride; + } } - return dst; + src->header.stride = stride; + + return LV_RESULT_OK; } lv_result_t lv_draw_buf_premultiply(lv_draw_buf_t * draw_buf) diff --git a/src/draw/lv_draw_buf.h b/src/draw/lv_draw_buf.h index 560a21dc5..2628412e2 100644 --- a/src/draw/lv_draw_buf.h +++ b/src/draw/lv_draw_buf.h @@ -32,7 +32,7 @@ LV_EXPORT_CONST_INT(LV_STRIDE_AUTO); typedef struct { lv_image_header_t header; uint32_t data_size; /*Total buf size in bytes*/ - void * data; + uint8_t * data; void * unaligned_data; /*Unaligned address of `data`, used internally by lvgl*/ } lv_draw_buf_t; @@ -206,9 +206,12 @@ void lv_draw_buf_destroy(lv_draw_buf_t * buf); void * lv_draw_buf_goto_xy(const lv_draw_buf_t * buf, uint32_t x, uint32_t y); /** - * Adjust the stride of a draw buf. + * Adjust the stride of a draw buf in place. + * @param src pointer to a draw buffer + * @param stride the new stride in bytes for image. Use LV_STRIDE_AUTO for automatic calculation. + * @return LV_RESULT_OK: success or LV_RESULT_INVALID: failed */ -lv_draw_buf_t * lv_draw_buf_adjust_stride(const lv_draw_buf_t * src, uint32_t stride); +lv_result_t lv_draw_buf_adjust_stride(lv_draw_buf_t * src, uint32_t stride); /** * Premultiply draw buffer color with alpha channel. diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index cfbe53c59..15be695ed 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -256,13 +256,17 @@ lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_d uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf); if(decoded->header.stride != stride_expect) { LV_LOG_TRACE("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; - } + lv_result_t res = lv_draw_buf_adjust_stride(decoded, stride_expect); + if(res != LV_RESULT_OK) { + lv_draw_buf_t * aligned = lv_draw_buf_create(decoded->header.w, decoded->header.h, decoded->header.cf, stride_expect); + if(aligned == NULL) { + LV_LOG_ERROR("No memory for Stride adjust."); + return NULL; + } - decoded = aligned; + lv_draw_buf_copy(aligned, NULL, decoded, NULL); + decoded = aligned; + } } } diff --git a/tests/src/test_cases/test_draw_buf_stride.c b/tests/src/test_cases/test_draw_buf_stride.c new file mode 100644 index 000000000..3df28dbe5 --- /dev/null +++ b/tests/src/test_cases/test_draw_buf_stride.c @@ -0,0 +1,74 @@ +#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) +{ + /* Function run after every test */ +} + +void test_draw_buf_stride_adjust(void) +{ +#if LV_BIN_DECODER_RAM_LOAD == 1 + const char * img_src = "A:test_images/stride_align64/UNCOMPRESSED/test_ARGB8888.bin"; + lv_obj_t * img = lv_image_create(lv_screen_active()); + lv_obj_center(img); + lv_image_set_src(img, img_src); + TEST_ASSERT_EQUAL_SCREENSHOT("draw/temp.o"); /*Generate the reference image, use .o so git ignore it*/ + + lv_image_decoder_args_t args = { + .no_cache = false, + .premultiply = false, + .stride_align = false, + .use_indexed = false, + }; + + lv_image_decoder_dsc_t decoder_dsc; + lv_result_t res = lv_image_decoder_open(&decoder_dsc, img_src, &args); + TEST_ASSERT_EQUAL(LV_RESULT_OK, res); + + const lv_image_header_t header = decoder_dsc.decoded->header; + /*The test image must have aligned stride different with width * bpp*/ + TEST_ASSERT_NOT_EQUAL(header.w * 4, header.stride); + + lv_draw_buf_t * dup = lv_draw_buf_dup(decoder_dsc.decoded); + TEST_ASSERT_NOT_NULL(dup); + + /*Close the decoder since we copied out the decoded draw buffer*/ + lv_image_decoder_close(&decoder_dsc); + + /* Shrink stride to below minimal stride(by -1 in code below) should fail */ + res = lv_draw_buf_adjust_stride(dup, header.w * 4 - 1); + TEST_ASSERT_EQUAL(LV_RESULT_INVALID, res); + + res = lv_draw_buf_adjust_stride(dup, header.stride + 1); + TEST_ASSERT_EQUAL(LV_RESULT_INVALID, res); + + /*Expand the stride should fail if stride is too large that buffer size overflow*/ + res = lv_draw_buf_adjust_stride(dup, header.stride + 1); + TEST_ASSERT_EQUAL(LV_RESULT_INVALID, res); + + /* Expand the stride should work, use a proper stride value should succeed*/ + res = lv_draw_buf_adjust_stride(dup, (header.stride + header.w * 4) / 2); + TEST_ASSERT_EQUAL(LV_RESULT_OK, res); + lv_image_set_src(img, dup); + TEST_ASSERT_EQUAL_SCREENSHOT("draw/temp.o"); /*The image should still looks same*/ + + /* Shrink stride to minimal stride should succeed */ + res = lv_draw_buf_adjust_stride(dup, header.w * 4); + TEST_ASSERT_EQUAL(LV_RESULT_OK, res); + lv_image_set_src(img, dup); + TEST_ASSERT_EQUAL_SCREENSHOT("draw/temp.o"); /*Test against with above reference image*/ + + lv_draw_buf_destroy(dup); +#endif +} + +#endif