test(canvas): add canvas test case and fix the null pointer access crash in the draw buffer (#9478)

Signed-off-by: jialuxiao <jialuxiao@xiaomi.com>
Co-authored-by: jialuxiao <jialuxiao@xiaomi.com>
Co-authored-by: chengchen10 <chengchen10@xiaomi.com>
This commit is contained in:
vela-mib666
2026-01-05 10:46:00 +08:00
committed by GitHub
parent 702891aa98
commit 3433d62bf8
5 changed files with 392 additions and 9 deletions
+11 -5
View File
@@ -115,8 +115,11 @@ void lv_canvas_set_px(lv_obj_t * obj, int32_t x, int32_t y, lv_color_t color, lv
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_draw_buf_t * draw_buf = canvas->draw_buf;
if(draw_buf == NULL) return;
lv_color_format_t cf = draw_buf->header.cf;
uint8_t * data = lv_draw_buf_goto_xy(draw_buf, x, y);
if(data == NULL) return;
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
uint8_t shift;
@@ -188,6 +191,8 @@ void lv_canvas_set_palette(lv_obj_t * obj, uint8_t index, lv_color32_t color)
lv_canvas_t * canvas = (lv_canvas_t *)obj;
if(canvas->draw_buf == NULL) return;
lv_draw_buf_set_palette(canvas->draw_buf, index, color);
lv_obj_invalidate(obj);
}
@@ -214,6 +219,7 @@ lv_color32_t lv_canvas_get_px(lv_obj_t * obj, int32_t x, int32_t y)
lv_image_header_t * header = &canvas->draw_buf->header;
const uint8_t * px = lv_draw_buf_goto_xy(canvas->draw_buf, x, y);
if(px == NULL) return ret;
switch(header->cf) {
case LV_COLOR_FORMAT_ARGB8888:
@@ -280,18 +286,18 @@ const void * lv_canvas_get_buf(lv_obj_t * obj)
* Other functions
*====================*/
void lv_canvas_copy_buf(lv_obj_t * obj, const lv_area_t * canvas_area, lv_draw_buf_t * dest_buf,
const lv_area_t * dest_area)
void lv_canvas_copy_buf(lv_obj_t * obj, const lv_area_t * canvas_area, lv_draw_buf_t * src_buf,
const lv_area_t * src_area)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(canvas_area && dest_buf);
LV_ASSERT_NULL(src_buf);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
if(canvas->draw_buf == NULL) return;
LV_ASSERT_MSG(canvas->draw_buf->header.cf == dest_buf->header.cf, "Color formats must be the same");
LV_ASSERT_MSG(canvas->draw_buf->header.cf == src_buf->header.cf, "Color formats must be the same");
lv_draw_buf_copy(canvas->draw_buf, canvas_area, dest_buf, dest_area);
lv_draw_buf_copy(canvas->draw_buf, canvas_area, src_buf, src_area);
}
void lv_canvas_fill_bg(lv_obj_t * obj, lv_color_t color, lv_opa_t opa)
+4 -4
View File
@@ -135,11 +135,11 @@ const void * lv_canvas_get_buf(lv_obj_t * canvas);
* Copy a buffer to the canvas
* @param obj pointer to a canvas object
* @param canvas_area the area of the canvas to copy
* @param dest_buf pointer to a buffer to store the copied data
* @param dest_area the area of the destination buffer to copy to. If omitted NULL, copy to the whole `dest_buf`
* @param src_buf pointer to a buffer to copy data from
* @param src_area the area of the source buffer to copy from. If NULL, copy the whole `src_buf`
*/
void lv_canvas_copy_buf(lv_obj_t * obj, const lv_area_t * canvas_area, lv_draw_buf_t * dest_buf,
const lv_area_t * dest_area);
void lv_canvas_copy_buf(lv_obj_t * obj, const lv_area_t * canvas_area, lv_draw_buf_t * src_buf,
const lv_area_t * src_area);
/**
* Fill the canvas with color
Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

+377
View File
@@ -358,4 +358,381 @@ void test_canvas_triangles(void)
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/canvas_2.png");
}
void test_canvas_buffer_operations(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
/* Test uninitialized buffer */
static LV_ATTRIBUTE_MEM_ALIGN uint8_t buf[LV_DRAW_BUF_SIZE(100, 50, LV_COLOR_FORMAT_RGB565)];
lv_canvas_set_buffer(canvas, buf, 100, 50, LV_COLOR_FORMAT_RGB565);
TEST_ASSERT_NOT_NULL(lv_canvas_get_draw_buf(canvas));
TEST_ASSERT_NOT_NULL(lv_canvas_get_image(canvas));
TEST_ASSERT_NOT_NULL(lv_canvas_get_buf(canvas));
/* Test lv_canvas_fill_bg to ensure no out-of-bounds access */
lv_canvas_fill_bg(canvas, lv_color_black(), LV_OPA_COVER);
/* Test draw buffer with handlers */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 100, 50, LV_COLOR_FORMAT_RGB565);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
TEST_ASSERT_EQUAL_PTR(&draw_buf, lv_canvas_get_draw_buf(canvas));
}
void test_canvas_layer_operations(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
int draw_counter = 0;
lv_obj_add_event_cb(canvas, draw_event_cb, LV_EVENT_DRAW_MAIN, &draw_counter);
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 100, 100, LV_COLOR_FORMAT_RGB565);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
/* Test layer initialization */
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
TEST_ASSERT_EQUAL_PTR(&draw_buf, layer.draw_buf);
TEST_ASSERT_EQUAL(LV_COLOR_FORMAT_RGB565, layer.color_format);
/* Test drawing on layer */
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_black();
lv_area_t rect_area = {10, 10, 50, 50};
lv_draw_rect(&layer, &rect_dsc, &rect_area);
/* Test layer finalization */
lv_canvas_finish_layer(canvas, &layer);
lv_refr_now(NULL);
TEST_ASSERT_EQUAL_INT(1, draw_counter);
/* Test for secondary redrawing */
lv_canvas_finish_layer(canvas, &layer);
lv_refr_now(NULL);
TEST_ASSERT_EQUAL_INT(1, draw_counter);
}
void test_canvas_image_operations(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 10, 10, LV_COLOR_FORMAT_RGB565);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
/* Test get_image */
lv_image_dsc_t * img = lv_canvas_get_image(canvas);
TEST_ASSERT_NOT_NULL(img);
TEST_ASSERT_EQUAL_UINT32(10, img->header.w);
TEST_ASSERT_EQUAL_UINT32(10, img->header.h);
/* Test get_buf */
const void * buf = lv_canvas_get_buf(canvas);
TEST_ASSERT_NOT_NULL(buf);
TEST_ASSERT_EQUAL_PTR(draw_buf.unaligned_data, buf);
}
static void verify_canvas_px(lv_obj_t * canvas, lv_color_t expected_color, lv_opa_t expected_opa)
{
for(int32_t y = 0; y < 10; y++) {
for(int32_t x = 0; x < 10; x++) {
lv_color32_t px = lv_canvas_get_px(canvas, x, y);
TEST_ASSERT_EQUAL_UINT8(expected_color.red, px.red);
TEST_ASSERT_EQUAL_UINT8(expected_color.green, px.green);
TEST_ASSERT_EQUAL_UINT8(expected_color.blue, px.blue);
TEST_ASSERT_EQUAL_UINT8(expected_opa, px.alpha);
}
}
}
void test_canvas_fill_background_formats(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
/* Test ARGB8888 format */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_argb, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf_argb);
canvas_draw_buf_reshape(&draw_buf_argb);
lv_canvas_set_draw_buf(canvas, &draw_buf_argb);
lv_color_t fill_color_argb = lv_color_hex(0xABCDEF);
lv_canvas_fill_bg(canvas, fill_color_argb, 0x80);
verify_canvas_px(canvas, fill_color_argb, 0x80);
/* Test A8 format */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_a8, 10, 10, LV_COLOR_FORMAT_A8);
LV_DRAW_BUF_INIT_STATIC(draw_buf_a8);
canvas_draw_buf_reshape(&draw_buf_a8);
lv_canvas_set_draw_buf(canvas, &draw_buf_a8);
lv_canvas_fill_bg(canvas, lv_color_black(), 0x80);
verify_canvas_px(canvas, lv_obj_get_style_image_recolor(canvas, LV_PART_MAIN), 0x80);
/* Test RGB888 format */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_rgb888, 10, 10, LV_COLOR_FORMAT_RGB888);
LV_DRAW_BUF_INIT_STATIC(draw_buf_rgb888);
canvas_draw_buf_reshape(&draw_buf_rgb888);
lv_canvas_set_draw_buf(canvas, &draw_buf_rgb888);
lv_color_t fill_color_rgb888 = lv_color_hex(0x123456);
lv_canvas_fill_bg(canvas, fill_color_rgb888, LV_OPA_COVER);
verify_canvas_px(canvas, fill_color_rgb888, LV_OPA_COVER);
/* Test unsupported format (default case) */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_unsupported, 10, 10, LV_COLOR_FORMAT_ARGB8565);
LV_DRAW_BUF_INIT_STATIC(draw_buf_unsupported);
canvas_draw_buf_reshape(&draw_buf_unsupported);
lv_canvas_set_draw_buf(canvas, &draw_buf_unsupported);
verify_canvas_px(canvas, lv_color_black(), 0);
}
void test_canvas_layer_complex_drawing(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
int draw_counter = 0;
lv_obj_add_event_cb(canvas, draw_event_cb, LV_EVENT_DRAW_MAIN, &draw_counter);
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 100, 100, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
/* Initialize layer */
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
/* Draw multiple shapes */
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_hex(0xFF0000);
lv_area_t rect_area = {10, 10, 50, 50};
lv_draw_rect(&layer, &rect_dsc, &rect_area);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.color = lv_color_white();
lv_point_t label_pos = {20, 20};
lv_area_t label_area = {label_pos.x, label_pos.y, label_pos.x + 100, label_pos.y + 50};
/* label_dsc already declared above, just initialize and use */
lv_draw_label_dsc_init(&label_dsc);
label_dsc.text = "Test";
lv_draw_label(&layer, &label_dsc, &label_area);
/* Finish layer and verify */
lv_canvas_finish_layer(canvas, &layer);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/canvas_layer_complex.png");
TEST_ASSERT_EQUAL_INT(1, draw_counter);
/* Verify pixel content */
lv_color32_t px = lv_canvas_get_px(canvas, 30, 30);
TEST_ASSERT_EQUAL_UINT8(0xFF, px.red);
TEST_ASSERT_EQUAL_UINT8(0x00, px.green);
TEST_ASSERT_EQUAL_UINT8(0x00, px.blue);
}
void test_canvas_pixel_operations(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
/* Test RGB565 format */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_rgb565, 10, 10, LV_COLOR_FORMAT_RGB565);
LV_DRAW_BUF_INIT_STATIC(draw_buf_rgb565);
canvas_draw_buf_reshape(&draw_buf_rgb565);
lv_canvas_set_draw_buf(canvas, &draw_buf_rgb565);
/* Test basic color conversion */
lv_canvas_set_px(canvas, 0, 0, lv_color_hex(0x0000FF), 0);
lv_color32_t px_blue = lv_canvas_get_px(canvas, 0, 0);
TEST_ASSERT_EQUAL_UINT8(0xFF, px_blue.blue);
TEST_ASSERT_EQUAL_UINT8(0x00, px_blue.green);
TEST_ASSERT_EQUAL_UINT8(0x00, px_blue.red);
TEST_ASSERT_EQUAL_UINT8(0xFF, px_blue.alpha);
/* Test ARGB8888 format */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf_argb, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf_argb);
canvas_draw_buf_reshape(&draw_buf_argb);
lv_canvas_set_draw_buf(canvas, &draw_buf_argb);
lv_color_t test_color = lv_color_hex(0x1234);
lv_canvas_set_px(canvas, 3, 3, test_color, 0x80);
lv_color32_t px = lv_canvas_get_px(canvas, 3, 3);
TEST_ASSERT_EQUAL_UINT8(test_color.red, px.red);
TEST_ASSERT_EQUAL_UINT8(test_color.green, px.green);
TEST_ASSERT_EQUAL_UINT8(test_color.blue, px.blue);
TEST_ASSERT_EQUAL_UINT8(0x80, px.alpha);
}
void test_canvas_fill_background(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_color_t fill_color = lv_color_hex(0xABCD);
lv_canvas_fill_bg(canvas, fill_color, LV_OPA_COVER);
for(int32_t y = 0; y < 10; y++) {
for(int32_t x = 0; x < 10; x++) {
lv_color32_t px = lv_canvas_get_px(canvas, x, y);
TEST_ASSERT_EQUAL_UINT8(fill_color.red, px.red);
TEST_ASSERT_EQUAL_UINT8(fill_color.green, px.green);
TEST_ASSERT_EQUAL_UINT8(fill_color.blue, px.blue);
}
}
}
void test_canvas_copy_buffer(void)
{
lv_obj_t * src_canvas = lv_canvas_create(g_screen_active);
lv_obj_t * dst_canvas = lv_canvas_create(g_screen_active);
LV_DRAW_BUF_DEFINE_STATIC(src_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_DEFINE_STATIC(dst_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(src_buf);
LV_DRAW_BUF_INIT_STATIC(dst_buf);
canvas_draw_buf_reshape(&src_buf);
canvas_draw_buf_reshape(&dst_buf);
lv_canvas_set_draw_buf(src_canvas, &src_buf);
lv_canvas_set_draw_buf(dst_canvas, &dst_buf);
/* Fill source with pattern */
for(int32_t y = 0; y < 10; y++) {
for(int32_t x = 0; x < 10; x++) {
lv_color_t c = lv_color_make(x * 25, y * 25, (x + y) * 12);
lv_canvas_set_px(src_canvas, x, y, c, LV_OPA_COVER);
}
}
/* Copy area */
lv_area_t dst_area = {2, 2, 7, 7};
lv_area_t src_area = {0, 0, 5, 5};
lv_canvas_copy_buf(dst_canvas, &dst_area, &src_buf, &src_area);
/* Verify copied pixels */
for(int32_t y = 0; y < 6; y++) {
for(int32_t x = 0; x < 6; x++) {
lv_color32_t src_px = lv_canvas_get_px(src_canvas, x, y);
lv_color32_t dst_px = lv_canvas_get_px(dst_canvas, x + 2, y + 2);
TEST_ASSERT_EQUAL_UINT8(src_px.red, dst_px.red);
TEST_ASSERT_EQUAL_UINT8(src_px.green, dst_px.green);
TEST_ASSERT_EQUAL_UINT8(src_px.blue, dst_px.blue);
TEST_ASSERT_EQUAL_UINT8(src_px.alpha, dst_px.alpha);
}
}
}
void test_canvas_palette_operations(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
/* Test with supported color formats */
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
/* Test directly setting and getting pixel colors */
lv_color32_t test_color = {.red = 0x12, .green = 0x34, .blue = 0x56, .alpha = 0xFF};
lv_canvas_set_px(canvas, 0, 0, lv_color_hex(0x123456), LV_OPA_COVER);
lv_color32_t px = lv_canvas_get_px(canvas, 0, 0);
TEST_ASSERT_EQUAL_UINT8(test_color.red, px.red);
TEST_ASSERT_EQUAL_UINT8(test_color.green, px.green);
TEST_ASSERT_EQUAL_UINT8(test_color.blue, px.blue);
TEST_ASSERT_EQUAL_UINT8(test_color.alpha, px.alpha);
}
void test_canvas_copy_buffer_partial(void)
{
lv_obj_t * src_canvas = lv_canvas_create(g_screen_active);
lv_obj_t * dst_canvas = lv_canvas_create(g_screen_active);
LV_DRAW_BUF_DEFINE_STATIC(src_buf, 20, 20, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_DEFINE_STATIC(dst_buf, 20, 20, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(src_buf);
LV_DRAW_BUF_INIT_STATIC(dst_buf);
canvas_draw_buf_reshape(&src_buf);
canvas_draw_buf_reshape(&dst_buf);
lv_canvas_set_draw_buf(src_canvas, &src_buf);
lv_canvas_set_draw_buf(dst_canvas, &dst_buf);
/* Fill source with pattern */
for(int32_t y = 0; y < 20; y++) {
for(int32_t x = 0; x < 20; x++) {
lv_color_t c = lv_color_make(x * 12, y * 12, (x + y) * 6);
lv_canvas_set_px(src_canvas, x, y, c, LV_OPA_COVER);
}
}
/* Copy partial area with offset */
lv_area_t src_area = {5, 5, 15, 15};
lv_area_t dst_area = {0, 0, 10, 10};
lv_canvas_copy_buf(src_canvas, &src_area, &dst_buf, &dst_area);
/* Verify copied pixels */
for(int32_t y = 0; y < 11; y++) {
for(int32_t x = 0; x < 11; x++) {
lv_color32_t src_px = lv_canvas_get_px(src_canvas, x + 5, y + 5);
lv_color32_t dst_px = lv_canvas_get_px(dst_canvas, x, y);
TEST_ASSERT_TRUE(lv_color32_eq(src_px, dst_px));
}
}
}
void test_canvas_empty_draw_buf(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
TEST_ASSERT_NULL(lv_canvas_get_draw_buf(canvas));
TEST_ASSERT_NULL(lv_canvas_get_image(canvas));
TEST_ASSERT_NULL(lv_canvas_get_buf(canvas));
lv_layer_t layer;
LV_DRAW_BUF_DEFINE_STATIC(src_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(src_buf);
canvas_draw_buf_reshape(&src_buf);
lv_canvas_copy_buf(canvas, NULL, &src_buf, NULL);
lv_canvas_fill_bg(canvas, lv_color_hex(0xFFFFFF), LV_OPA_COVER);
lv_canvas_init_layer(canvas, &layer);
lv_canvas_set_px(canvas, 0, 0, lv_color_hex(0x000000), LV_OPA_COVER);
lv_color32_t src_px = lv_color_to_32(lv_color_hex(0x000000), LV_OPA_0);
lv_color32_t dst_px = lv_canvas_get_px(canvas, 0, 0);
TEST_ASSERT_TRUE(lv_color32_eq(src_px, dst_px));
lv_canvas_set_palette(canvas, 0, src_px);
lv_canvas_finish_layer(canvas, &layer);
}
void test_canvas_out_of_area(void)
{
lv_obj_t * canvas = lv_canvas_create(g_screen_active);
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, 10, 10, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
canvas_draw_buf_reshape(&draw_buf);
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_color_t test_color = lv_color_hex(0x1234);
lv_canvas_set_px(canvas, -1, -1, test_color, LV_OPA_0);
lv_color32_t px = lv_canvas_get_px(canvas, -1, -1);
TEST_ASSERT_EQUAL_UINT8(0x00, px.red);
TEST_ASSERT_EQUAL_UINT8(0x00, px.green);
TEST_ASSERT_EQUAL_UINT8(0x00, px.blue);
TEST_ASSERT_EQUAL_UINT8(0x00, px.alpha);
}
#endif