From b536adda63159e83c48e4a548e5b789f5e4e1219 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 29 Jun 2026 10:20:40 +0200 Subject: [PATCH] perf(arc): optimize angle invalidation (esp. for spinner, or when invisible) (#10307) --- src/widgets/arc/lv_arc.c | 172 ++++++++++++++++--------------- src/widgets/spinner/lv_spinner.c | 31 +++--- 2 files changed, 105 insertions(+), 98 deletions(-) diff --git a/src/widgets/arc/lv_arc.c b/src/widgets/arc/lv_arc.c index 39c4cb2a78..094e9d0f91 100644 --- a/src/widgets/arc/lv_arc.c +++ b/src/widgets/arc/lv_arc.c @@ -42,6 +42,7 @@ static void lv_arc_draw(lv_event_t * e); static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e); static void inv_arc_area(lv_obj_t * arc, lv_value_precise_t start_angle, lv_value_precise_t end_angle, lv_part_t part); static void inv_knob_area(lv_obj_t * obj); +static void get_knob_inv_area(lv_obj_t * obj, lv_area_t * area); static void get_center(const lv_obj_t * obj, lv_point_t * center, int32_t * arc_r); static lv_value_precise_t get_angle(const lv_obj_t * obj); static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, int32_t r, lv_area_t * knob_area); @@ -162,52 +163,59 @@ void lv_arc_set_start_angle(lv_obj_t * obj, lv_value_precise_t start) LV_CHECK_OBJ(obj, MY_CLASS, return); lv_arc_t * arc = (lv_arc_t *)obj; - if(start > 360) start -= 360; - - lv_value_precise_t old_delta = arc->indic_angle_end - arc->indic_angle_start; - lv_value_precise_t new_delta = arc->indic_angle_end - start; - - if(old_delta < 0) old_delta = 360 + old_delta; - if(new_delta < 0) new_delta = 360 + new_delta; - - if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj); - else if(new_delta < old_delta) inv_arc_area(obj, arc->indic_angle_start, start, LV_PART_INDICATOR); - else if(old_delta < new_delta) inv_arc_area(obj, start, arc->indic_angle_start, LV_PART_INDICATOR); - - inv_knob_area(obj); - - arc->indic_angle_start = start; - - inv_knob_area(obj); + lv_arc_set_angles(obj, start, arc->indic_angle_end); } void lv_arc_set_end_angle(lv_obj_t * obj, lv_value_precise_t end) { LV_CHECK_OBJ(obj, MY_CLASS, return); lv_arc_t * arc = (lv_arc_t *)obj; - if(end > 360) end -= 360; - lv_value_precise_t old_delta = arc->indic_angle_end - arc->indic_angle_start; - lv_value_precise_t new_delta = end - arc->indic_angle_start; - - if(old_delta < 0) old_delta = 360 + old_delta; - if(new_delta < 0) new_delta = 360 + new_delta; - - if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj); - else if(new_delta < old_delta) inv_arc_area(obj, end, arc->indic_angle_end, LV_PART_INDICATOR); - else if(old_delta < new_delta) inv_arc_area(obj, arc->indic_angle_end, end, LV_PART_INDICATOR); - - inv_knob_area(obj); - - arc->indic_angle_end = end; - - inv_knob_area(obj); + lv_arc_set_angles(obj, arc->indic_angle_start, end); } void lv_arc_set_angles(lv_obj_t * obj, lv_value_precise_t start, lv_value_precise_t end) { - lv_arc_set_end_angle(obj, end); - lv_arc_set_start_angle(obj, start); + LV_CHECK_OBJ(obj, MY_CLASS, return); + lv_arc_t * arc = (lv_arc_t *)obj; + + if(start > 360) start -= 360; + if(end > 360) end -= 360; + + /*Snapshot for invalidation*/ + lv_value_precise_t old_start = arc->indic_angle_start; + lv_value_precise_t old_end = arc->indic_angle_end; + lv_area_t old_knob_area; + get_knob_inv_area(obj, &old_knob_area); + + /*Apply the change*/ + arc->indic_angle_start = start; + arc->indic_angle_end = end; + + /*Invalidation*/ + if(lv_obj_is_visible(obj)) { + /*Area swept by the start angle*/ + lv_value_precise_t start_old_delta = end - old_start; + lv_value_precise_t start_new_delta = end - start; + if(start_old_delta < 0) start_old_delta = 360 + start_old_delta; + if(start_new_delta < 0) start_new_delta = 360 + start_new_delta; + if(LV_ABS(start_new_delta - start_old_delta) > 180) lv_obj_invalidate(obj); + else if(start_new_delta < start_old_delta) inv_arc_area(obj, old_start, start, LV_PART_INDICATOR); + else if(start_old_delta < start_new_delta) inv_arc_area(obj, start, old_start, LV_PART_INDICATOR); + + /*Area swept by the end angle*/ + lv_value_precise_t end_old_delta = old_end - old_start; + lv_value_precise_t end_new_delta = end - old_start; + if(end_old_delta < 0) end_old_delta = 360 + end_old_delta; + if(end_new_delta < 0) end_new_delta = 360 + end_new_delta; + if(LV_ABS(end_new_delta - end_old_delta) > 180) lv_obj_invalidate(obj); + else if(end_new_delta < end_old_delta) inv_arc_area(obj, end, old_end, LV_PART_INDICATOR); + else if(end_old_delta < end_new_delta) inv_arc_area(obj, old_end, end, LV_PART_INDICATOR); + + /*Knob*/ + lv_obj_invalidate_area(obj, &old_knob_area); + inv_knob_area(obj); + } } void lv_arc_set_bg_start_angle(lv_obj_t * obj, lv_value_precise_t start) @@ -215,21 +223,7 @@ void lv_arc_set_bg_start_angle(lv_obj_t * obj, lv_value_precise_t start) LV_CHECK_OBJ(obj, MY_CLASS, return); lv_arc_t * arc = (lv_arc_t *)obj; - if(start > 360) start -= 360; - - lv_value_precise_t old_delta = arc->bg_angle_end - arc->bg_angle_start; - lv_value_precise_t new_delta = arc->bg_angle_end - start; - - if(old_delta < 0) old_delta = 360 + old_delta; - if(new_delta < 0) new_delta = 360 + new_delta; - - if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj); - else if(new_delta < old_delta) inv_arc_area(obj, arc->bg_angle_start, start, LV_PART_MAIN); - else if(old_delta < new_delta) inv_arc_area(obj, start, arc->bg_angle_start, LV_PART_MAIN); - - arc->bg_angle_start = start; - - value_update(obj); + lv_arc_set_bg_angles(obj, start, arc->bg_angle_end); } void lv_arc_set_bg_end_angle(lv_obj_t * obj, lv_value_precise_t end) @@ -237,27 +231,46 @@ void lv_arc_set_bg_end_angle(lv_obj_t * obj, lv_value_precise_t end) LV_CHECK_OBJ(obj, MY_CLASS, return); lv_arc_t * arc = (lv_arc_t *)obj; - if(end > 360) end -= 360; - - lv_value_precise_t old_delta = arc->bg_angle_end - arc->bg_angle_start; - lv_value_precise_t new_delta = end - arc->bg_angle_start; - - if(old_delta < 0) old_delta = 360 + old_delta; - if(new_delta < 0) new_delta = 360 + new_delta; - - if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj); - else if(new_delta < old_delta) inv_arc_area(obj, end, arc->bg_angle_end, LV_PART_MAIN); - else if(old_delta < new_delta) inv_arc_area(obj, arc->bg_angle_end, end, LV_PART_MAIN); - - arc->bg_angle_end = end; - - value_update(obj); + lv_arc_set_bg_angles(obj, arc->bg_angle_start, end); } void lv_arc_set_bg_angles(lv_obj_t * obj, lv_value_precise_t start, lv_value_precise_t end) { - lv_arc_set_bg_end_angle(obj, end); - lv_arc_set_bg_start_angle(obj, start); + LV_CHECK_OBJ(obj, MY_CLASS, return); + lv_arc_t * arc = (lv_arc_t *)obj; + + if(start > 360) start -= 360; + if(end > 360) end -= 360; + + /*Snapshot for invalidation*/ + lv_value_precise_t old_start = arc->bg_angle_start; + lv_value_precise_t old_end = arc->bg_angle_end; + + /*Apply the change*/ + arc->bg_angle_start = start; + arc->bg_angle_end = end; + value_update(obj); + + /*Invalidation*/ + if(lv_obj_is_visible(obj)) { + /*Area swept by the start angle*/ + lv_value_precise_t start_old_delta = end - old_start; + lv_value_precise_t start_new_delta = end - start; + if(start_old_delta < 0) start_old_delta = 360 + start_old_delta; + if(start_new_delta < 0) start_new_delta = 360 + start_new_delta; + if(LV_ABS(start_new_delta - start_old_delta) > 180) lv_obj_invalidate(obj); + else if(start_new_delta < start_old_delta) inv_arc_area(obj, old_start, start, LV_PART_MAIN); + else if(start_old_delta < start_new_delta) inv_arc_area(obj, start, old_start, LV_PART_MAIN); + + /*Area swept by the end angle*/ + lv_value_precise_t end_old_delta = old_end - old_start; + lv_value_precise_t end_new_delta = end - old_start; + if(end_old_delta < 0) end_old_delta = 360 + end_old_delta; + if(end_new_delta < 0) end_new_delta = 360 + end_new_delta; + if(LV_ABS(end_new_delta - end_old_delta) > 180) lv_obj_invalidate(obj); + else if(end_new_delta < end_old_delta) inv_arc_area(obj, end, old_end, LV_PART_MAIN); + else if(end_old_delta < end_new_delta) inv_arc_area(obj, old_end, end, LV_PART_MAIN); + } } void lv_arc_set_rotation(lv_obj_t * obj, int32_t rotation) @@ -289,8 +302,7 @@ void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type) switch(arc->type) { case LV_ARC_MODE_SYMMETRICAL: bg_midpoint = (arc->bg_angle_start + bg_end) / 2; - lv_arc_set_start_angle(obj, bg_midpoint); - lv_arc_set_end_angle(obj, bg_midpoint); + lv_arc_set_angles(obj, bg_midpoint, bg_midpoint); break; case LV_ARC_MODE_REVERSE: lv_arc_set_end_angle(obj, arc->bg_angle_end); @@ -864,10 +876,6 @@ static void lv_arc_draw(lv_event_t * e) static void inv_arc_area(lv_obj_t * obj, lv_value_precise_t start_angle, lv_value_precise_t end_angle, lv_part_t part) { LV_CHECK_OBJ(obj, MY_CLASS, return); - - /*Skip this complicated invalidation if the arc is not visible*/ - if(lv_obj_is_visible(obj) == false) return; - lv_arc_t * arc = (lv_arc_t *)obj; if(start_angle == end_angle) return; @@ -898,21 +906,25 @@ static void inv_arc_area(lv_obj_t * obj, lv_value_precise_t start_angle, lv_valu lv_obj_invalidate_area(obj, &inv_area); } -static void inv_knob_area(lv_obj_t * obj) +static void get_knob_inv_area(lv_obj_t * obj, lv_area_t * area) { lv_point_t c; int32_t r; get_center(obj, &c, &r); - lv_area_t a; - get_knob_area(obj, &c, r, &a); + get_knob_area(obj, &c, r, area); int32_t knob_extra_size = knob_get_extra_size(obj); if(knob_extra_size > 0) { - lv_area_increase(&a, knob_extra_size, knob_extra_size); + lv_area_increase(area, knob_extra_size, knob_extra_size); } +} +static void inv_knob_area(lv_obj_t * obj) +{ + lv_area_t a; + get_knob_inv_area(obj, &a); lv_obj_invalidate_area(obj, &a); } @@ -1005,13 +1017,11 @@ static void value_update(lv_obj_t * obj) if(arc->value < range_midpoint) { angle = lv_map(arc->value, arc->min_value, range_midpoint, (int32_t)arc->bg_angle_start, (int32_t)bg_midpoint); - lv_arc_set_start_angle(obj, angle); - lv_arc_set_end_angle(obj, bg_midpoint); + lv_arc_set_angles(obj, angle, bg_midpoint); } else { angle = lv_map(arc->value, range_midpoint, arc->max_value, (int32_t)bg_midpoint, (int32_t)bg_end); - lv_arc_set_start_angle(obj, bg_midpoint); - lv_arc_set_end_angle(obj, angle); + lv_arc_set_angles(obj, bg_midpoint, angle); } break; case LV_ARC_MODE_REVERSE: diff --git a/src/widgets/spinner/lv_spinner.c b/src/widgets/spinner/lv_spinner.c index 791d204fc5..e68bc6d352 100644 --- a/src/widgets/spinner/lv_spinner.c +++ b/src/widgets/spinner/lv_spinner.c @@ -39,8 +39,7 @@ * STATIC PROTOTYPES **********************/ static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); -static void arc_anim_start_angle(void * obj, int32_t v); -static void arc_anim_end_angle(void * obj, int32_t v); +static void arc_anim_angles(void * obj, int32_t v); /********************** * STATIC VARIABLES @@ -98,17 +97,10 @@ void lv_spinner_set_anim_params(lv_obj_t * obj, uint32_t t, uint32_t angle) lv_anim_t a; lv_anim_init(&a); lv_anim_set_var(&a, obj); - lv_anim_set_exec_cb(&a, arc_anim_end_angle); + lv_anim_set_exec_cb(&a, arc_anim_angles); lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); lv_anim_set_duration(&a, t); - lv_anim_set_values(&a, angle, 360 + angle); - lv_anim_start(&a); - - lv_anim_set_path_cb(&a, lv_anim_path_custom_bezier3); - lv_anim_set_bezier3_param(&a, LV_BEZIER_VAL_FLOAT(0.42), LV_BEZIER_VAL_FLOAT(0.58), - LV_BEZIER_VAL_FLOAT(0), LV_BEZIER_VAL_FLOAT(1)); - lv_anim_set_values(&a, 0, 360); - lv_anim_set_exec_cb(&a, arc_anim_start_angle); + lv_anim_set_values(&a, 0, LV_BEZIER_VAL_MAX); lv_anim_start(&a); lv_arc_set_bg_angles(obj, 0, 360); @@ -160,14 +152,19 @@ static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * ob lv_spinner_set_anim_params(obj, DEF_TIME, DEF_ARC_ANGLE); } -static void arc_anim_start_angle(void * obj, int32_t v) +static void arc_anim_angles(void * obj, int32_t v) { - lv_arc_set_start_angle(obj, (uint32_t) v); -} + lv_spinner_t * spinner = (lv_spinner_t *)obj; -static void arc_anim_end_angle(void * obj, int32_t v) -{ - lv_arc_set_end_angle(obj, (uint32_t) v); + /*Start angle (bezier)*/ + int32_t step = lv_cubic_bezier(v, LV_BEZIER_VAL_FLOAT(0.42f), LV_BEZIER_VAL_FLOAT(0.58f), + LV_BEZIER_VAL_FLOAT(0.0f), LV_BEZIER_VAL_FLOAT(1.0f)); + lv_value_precise_t start = (lv_value_precise_t)((step * 360) >> LV_BEZIER_VAL_SHIFT); + + /*End angle (linear)*/ + lv_value_precise_t end = spinner->angle + ((v * 360) >> LV_BEZIER_VAL_SHIFT); + + lv_arc_set_angles(obj, start, end); } #endif /*LV_USE_SPINNER*/