diff --git a/src/widgets/canvas/lv_canvas.c b/src/widgets/canvas/lv_canvas.c index 351dc1a320..4d37b6938d 100644 --- a/src/widgets/canvas/lv_canvas.c +++ b/src/widgets/canvas/lv_canvas.c @@ -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) diff --git a/src/widgets/canvas/lv_canvas.h b/src/widgets/canvas/lv_canvas.h index d669b17738..7b5820c27c 100644 --- a/src/widgets/canvas/lv_canvas.h +++ b/src/widgets/canvas/lv_canvas.h @@ -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 diff --git a/tests/ref_imgs/widgets/canvas_layer_complex.png b/tests/ref_imgs/widgets/canvas_layer_complex.png new file mode 100644 index 0000000000..506dd921fc Binary files /dev/null and b/tests/ref_imgs/widgets/canvas_layer_complex.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/canvas_layer_complex.png b/tests/ref_imgs_vg_lite/widgets/canvas_layer_complex.png new file mode 100644 index 0000000000..506dd921fc Binary files /dev/null and b/tests/ref_imgs_vg_lite/widgets/canvas_layer_complex.png differ diff --git a/tests/src/test_cases/widgets/test_canvas.c b/tests/src/test_cases/widgets/test_canvas.c index f0d4123953..0363d228f1 100644 --- a/tests/src/test_cases/widgets/test_canvas.c +++ b/tests/src/test_cases/widgets/test_canvas.c @@ -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