fix(blur): fix incorrect invalidation if part has blur or dropshadow (#9930)

This commit is contained in:
Gabor Kiss-Vamosi
2026-04-24 07:54:35 +02:00
committed by GitHub
parent b1e55eabe9
commit 14a1e1fd47
6 changed files with 131 additions and 51 deletions
+43 -33
View File
@@ -38,6 +38,8 @@ static bool is_transformed(const lv_obj_t * obj);
static lv_result_t invalidate_area_core(const lv_obj_t * obj, lv_area_t * area_tmp);
static lv_result_t obj_invalidate_area_internal(const lv_display_t * disp, const lv_obj_t * obj,
const lv_area_t * area);
static bool has_blur(const lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
@@ -790,7 +792,7 @@ bool lv_obj_refresh_self_size(lv_obj_t * obj)
return false;
/**
* Refresh the parent's layout, because the childs size is in some way dependent on its contents we need to force a
* Refresh the parent's layout, because the children size is in some way dependent on its contents we need to force a
* recalculation of the parents layout
*/
lv_obj_t * parent = lv_obj_get_parent(obj);
@@ -1046,7 +1048,8 @@ typedef struct {
static lv_obj_tree_walk_res_t blur_walk_cb(lv_obj_t * obj, void * user_data)
{
blur_walk_data_t * blur_data = user_data;
if(obj == blur_data->requester_obj) return LV_OBJ_TREE_WALK_SKIP_CHILDREN;
/*The requester obj was checked already*/
if(blur_data->requester_obj == obj) return LV_OBJ_TREE_WALK_SKIP_CHILDREN;
/*Truncate the area to the object*/
lv_area_t obj_coords;
@@ -1060,48 +1063,23 @@ static lv_obj_tree_walk_res_t blur_walk_cb(lv_obj_t * obj, void * user_data)
/*If the widget has blur set, invalidate it*/
if(lv_area_is_on(blur_data->inv_area, &obj_coords)) {
const uint32_t group_blur = (uint32_t)1 << lv_style_get_prop_group(LV_STYLE_BLUR_RADIUS);
const uint32_t group_dropshadow = (uint32_t)1 << lv_style_get_prop_group(LV_STYLE_DROP_SHADOW_OPA);
const lv_state_t state = lv_obj_style_get_selector_state(lv_obj_get_state(obj));
const lv_state_t state_inv = ~state;
lv_style_value_t v;
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
lv_obj_style_t * obj_style = &obj->styles[i];
if(obj_style->is_disabled) continue;
lv_state_t state_style = lv_obj_style_get_selector_state(obj->styles[i].selector);
if((state_style & state_inv)) continue;
bool invalidation_needed = false;
if((obj_style->style->has_group & group_blur) &&
lv_style_get_prop(obj_style->style, LV_STYLE_BLUR_RADIUS, &v)) {
invalidation_needed = true;
}
if((obj_style->style->has_group & group_dropshadow) &&
lv_style_get_prop(obj_style->style, LV_STYLE_DROP_SHADOW_OPA, &v)) {
invalidation_needed = true;
}
if(invalidation_needed == false) continue;
/*Truncate the area to the object*/
if(has_blur(obj)) {
ext_size = lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
invalidate_area_core(obj, &obj_coords);
/*No need to check the children as the widget is already invalidated
*which will redraw the children too*/
return LV_OBJ_TREE_WALK_SKIP_CHILDREN;
}
/*Check the next child, maybe it's blurred*/
return LV_OBJ_TREE_WALK_NEXT;
else {
/*Check the next child, maybe it's blurred*/
return LV_OBJ_TREE_WALK_NEXT;
}
}
else {
/*Not on the area of interest, skip it*/
@@ -1117,7 +1095,10 @@ lv_result_t lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
lv_display_t * disp = lv_obj_get_display(obj);
if(!lv_display_is_invalidation_enabled(disp)) return LV_RESULT_INVALID;
return obj_invalidate_area_internal(disp, obj, area);
/*If there are blurred or drop-shadow parts the whole widget needs to be invalidated
*as these can't be calculated partially. */
if(has_blur(obj)) return lv_obj_invalidate(obj);
else return obj_invalidate_area_internal(disp, obj, area);
}
@@ -1667,3 +1648,32 @@ static lv_result_t invalidate_area_core(const lv_obj_t * obj, lv_area_t * area_t
lv_result_t res = lv_inv_area(lv_obj_get_display(obj), area_tmp);
return res;
}
static bool has_blur(const lv_obj_t * obj)
{
const uint32_t group_blur = (uint32_t)1 << lv_style_get_prop_group(LV_STYLE_BLUR_RADIUS);
const uint32_t group_dropshadow = (uint32_t)1 << lv_style_get_prop_group(LV_STYLE_DROP_SHADOW_OPA);
const lv_state_t state = lv_obj_style_get_selector_state(lv_obj_get_state(obj));
const lv_state_t state_inv = ~state;
lv_style_value_t v;
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
lv_obj_style_t * obj_style = &obj->styles[i];
if(obj_style->is_disabled) continue;
lv_state_t state_style = lv_obj_style_get_selector_state(obj->styles[i].selector);
if((state_style & state_inv)) continue;
if((obj_style->style->has_group & group_blur) &&
lv_style_get_prop(obj_style->style, LV_STYLE_BLUR_RADIUS, &v)) {
if(v.num > 0) return true;
}
if((obj_style->style->has_group & group_dropshadow) &&
lv_style_get_prop(obj_style->style, LV_STYLE_DROP_SHADOW_OPA, &v)) {
if(v.num > 0) return true;
}
}
return false;
}
+10 -16
View File
@@ -56,7 +56,6 @@
/**********************
* STATIC PROTOTYPES
**********************/
static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance);
static unsigned read_png_file(lv_draw_buf_t ** refr_draw_buf, unsigned * width, unsigned * height,
const char * file_name);
static unsigned write_png_file(void * raw_img, uint32_t width, uint32_t height, char * file_name);
@@ -83,20 +82,11 @@ lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref)
lv_refr_now(NULL);
lv_test_screenshot_result_t res;
res = screenshot_compare(fn_ref, REF_IMG_TOLERANCE);
res = lv_test_screenshot_compare_core(fn_ref);
return res;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Compare the content of the frame buffer with a reference image
* @param fn_ref reference image path
* @return An element of lv_test_screenshot_result_t
*/
static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance)
lv_test_screenshot_result_t lv_test_screenshot_compare_core(const char * fn_ref)
{
char fn_ref_full[256];
lv_snprintf(fn_ref_full, sizeof(fn_ref_full), "%s%s", REF_IMGS_PATH, fn_ref);
@@ -150,9 +140,9 @@ static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8
uint8_t * ptr_ref = &(ref_row[x * 4]);
uint8_t * ptr_act = &screen_buf_tmp[x * 4];
if(LV_ABS((int32_t) ptr_act[0] - (int32_t) ptr_ref[0]) > tolerance ||
LV_ABS((int32_t) ptr_act[1] - (int32_t) ptr_ref[1]) > tolerance ||
LV_ABS((int32_t) ptr_act[2] - (int32_t) ptr_ref[2]) > tolerance) {
if(LV_ABS((int32_t) ptr_act[0] - (int32_t) ptr_ref[0]) > REF_IMG_TOLERANCE ||
LV_ABS((int32_t) ptr_act[1] - (int32_t) ptr_ref[1]) > REF_IMG_TOLERANCE ||
LV_ABS((int32_t) ptr_act[2] - (int32_t) ptr_ref[2]) > REF_IMG_TOLERANCE) {
uint32_t act_px = (ptr_act[2] << 16) + (ptr_act[1] << 8) + (ptr_act[0] << 0);
uint32_t ref_px = 0;
memcpy(&ref_px, ptr_ref, 3);
@@ -162,7 +152,7 @@ static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8
" - Expected: %X\n"
" - Actual: %X\n"
" - Tolerance: %d\n",
fn_ref_full, x, y, ref_px, act_px, tolerance);
fn_ref_full, x, y, ref_px, act_px, REF_IMG_TOLERANCE);
err = true;
break;
}
@@ -188,6 +178,10 @@ static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8
}
/**********************
* STATIC FUNCTIONS
**********************/
static unsigned read_png_file(lv_draw_buf_t ** refr_draw_buf, unsigned * width, unsigned * height,
const char * file_name)
{
@@ -53,7 +53,7 @@ typedef enum {
**********************/
/**
* Compare the current content of the test screen with a reference PNG image
* Invalidate and redraw the current screen and compare its content with a reference PNG image
* - If the reference image is not found it will be created automatically from the rendered screen.
* - If the compare fails an `<image_name>_err.png` file will be created with the rendered content next to the reference image.
*
@@ -66,6 +66,14 @@ typedef enum {
*/
lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref);
/**
* Works the same way as `lv_test_screenshot_compare` but it doesn't invalidate and redraw the screen
* but uses its current content. (lv_refr_now(NULL) might be called earlier)
* @param fn_ref reference image path
*/
lv_test_screenshot_result_t lv_test_screenshot_compare_core(const char * fn_ref);
/**********************
* MACROS
**********************/
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

@@ -16,8 +16,14 @@ void tearDown(void)
lv_obj_clean(lv_screen_active());
}
void test_draw_drop_shadow(void)
void test_draw_drop_shadow_basic(void)
{
/*It doesn't work with VGLite*/
#if LV_USE_DRAW_VG_LITE
TEST_PASS();
#else
static lv_style_t style;
lv_style_init(&style);
lv_style_set_drop_shadow_opa(&style, 255);
@@ -70,6 +76,68 @@ void test_draw_drop_shadow(void)
lv_label_set_text(label, "Hello world");
TEST_ASSERT_EQUAL_SCREENSHOT("draw/draw_drop_shadow.png");
#endif
}
void test_draw_drop_shadow_invalidate(void)
{
/*It doesn't work with VGLite*/
#if LV_USE_DRAW_VG_LITE
TEST_PASS();
#else
static lv_style_t style;
lv_style_init(&style);
lv_style_set_drop_shadow_color(&style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_drop_shadow_radius(&style, 16);
lv_style_set_drop_shadow_opa(&style, 255);
lv_style_set_drop_shadow_offset_x(&style, 5);
lv_style_set_drop_shadow_offset_y(&style, 10);
/*Create an object with the new style*/
lv_obj_t * obj = lv_arc_create(lv_screen_active());
lv_obj_add_style(obj, &style, LV_PART_INDICATOR);
lv_obj_set_pos(obj, 10, 10);
lv_arc_set_value(obj, 50);
/*Don't use TEST_ASSERT_EQUAL_SCREENSHOT as it invalidates the screen removing all
*manual partial invalidations below*/
const char * path = "draw/draw_drop_shadow_invalidate.png";
lv_refr_now(NULL);
TEST_ASSERT_MESSAGE(lv_test_screenshot_compare_core(path), path);
lv_arc_set_value(obj, 10);
lv_refr_now(NULL);
lv_arc_set_value(obj, 20);
lv_refr_now(NULL);
lv_arc_set_value(obj, 30);
lv_refr_now(NULL);
lv_arc_set_value(obj, 40);
lv_refr_now(NULL);
lv_arc_set_value(obj, 50);
lv_refr_now(NULL);
lv_arc_set_value(obj, 60);
lv_refr_now(NULL);
lv_arc_set_value(obj, 70);
lv_refr_now(NULL);
lv_arc_set_value(obj, 80);
lv_refr_now(NULL);
lv_arc_set_value(obj, 90);
lv_refr_now(NULL);
lv_arc_set_value(obj, 100);
lv_refr_now(NULL);
lv_arc_set_value(obj, 50);
lv_refr_now(NULL);
TEST_ASSERT_MESSAGE(lv_test_screenshot_compare_core(path), path);
lv_area_t a = {20, 20, 200, 60};
lv_obj_invalidate_area(obj, &a);
lv_refr_now(NULL);
TEST_ASSERT_MESSAGE(lv_test_screenshot_compare_core(path), path);
#endif
}
#endif