From 59c72243cc0df514dee82847b986b4f78abd29b0 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 18 Oct 2024 10:51:49 +0200 Subject: [PATCH] perf(label): simplify handling of bytes overwritten with dots (#7001) --- src/widgets/label/lv_label.c | 155 ++++++--------------------- src/widgets/label/lv_label_private.h | 8 +- 2 files changed, 34 insertions(+), 129 deletions(-) diff --git a/src/widgets/label/lv_label.c b/src/widgets/label/lv_label.c index 661f813a2..0907b02b6 100644 --- a/src/widgets/label/lv_label.c +++ b/src/widgets/label/lv_label.c @@ -32,7 +32,7 @@ #define LV_LABEL_DEF_SCROLL_SPEED lv_anim_speed_clamped(40, 300, 10000) #define LV_LABEL_SCROLL_DELAY 300 -#define LV_LABEL_DOT_END_INV 0xFFFFFFFF +#define LV_LABEL_DOT_BEGIN_INV 0xFFFFFFFF #define LV_LABEL_HINT_HEIGHT_LIMIT 1024 /*Enable "hint" to buffer info about labels larger than this. (Speed up drawing)*/ /********************** @@ -49,10 +49,8 @@ static void draw_main(lv_event_t * e); static void lv_label_refr_text(lv_obj_t * obj); static void lv_label_revert_dots(lv_obj_t * label); +static void lv_label_set_dots(lv_obj_t * label, uint32_t dot_begin); -static bool lv_label_set_dot_tmp(lv_obj_t * label, char * data, uint32_t len); -static char * lv_label_get_dot_tmp(lv_obj_t * label); -static void lv_label_dot_tmp_free(lv_obj_t * label); static void set_ofs_x_anim(void * obj, int32_t v); static void set_ofs_y_anim(void * obj, int32_t v); static size_t get_text_length(const char * text); @@ -137,11 +135,10 @@ void lv_label_set_text(lv_obj_t * obj, const char * text) LV_ASSERT_OBJ(obj, MY_CLASS); lv_label_t * label = (lv_label_t *)obj; - lv_obj_invalidate(obj); - /*If text is NULL then just refresh with the current text*/ if(text == NULL) text = label->text; + lv_label_revert_dots(obj); /*In case text == label->text*/ const size_t text_len = get_text_length(text); /*If set its own text then reallocate it (maybe its size changed)*/ @@ -237,11 +234,6 @@ void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode) else label->expand = 0; - /*Restore the character under the dots*/ - if(label->long_mode == LV_LABEL_LONG_DOT && label->dot_end != LV_LABEL_DOT_END_INV) { - lv_label_revert_dots(obj); - } - label->long_mode = long_mode; lv_label_refr_text(obj); } @@ -722,7 +714,7 @@ static void lv_label_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) label->text = NULL; label->recolor = 0; label->static_txt = 0; - label->dot_end = LV_LABEL_DOT_END_INV; + label->dot_begin = LV_LABEL_DOT_BEGIN_INV; label->long_mode = LV_LABEL_LONG_WRAP; lv_point_set(&label->offset, 0, 0); @@ -736,8 +728,6 @@ static void lv_label_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) label->sel_start = LV_DRAW_LABEL_NO_TXT_SEL; label->sel_end = LV_DRAW_LABEL_NO_TXT_SEL; #endif - label->dot.tmp_ptr = NULL; - label->dot_tmp_alloc = 0; lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE); lv_label_set_long_mode(obj, LV_LABEL_LONG_WRAP); @@ -751,7 +741,6 @@ static void lv_label_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) LV_UNUSED(class_p); lv_label_t * label = (lv_label_t *)obj; - lv_label_dot_tmp_free(obj); if(!label->static_txt) lv_free(label->text); label->text = NULL; } @@ -768,8 +757,6 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_obj_t * obj = lv_event_get_current_target(e); if((code == LV_EVENT_STYLE_CHANGED) || (code == LV_EVENT_SIZE_CHANGED)) { - /*Revert dots for proper refresh*/ - lv_label_revert_dots(obj); lv_label_refr_text(obj); } else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { @@ -791,13 +778,16 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR; if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND; - int32_t w = lv_obj_get_content_width(obj); + int32_t w; if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) w = LV_COORD_MAX; else w = lv_obj_get_content_width(obj); - w = LV_MIN(w, lv_obj_get_style_max_width(obj, 0)); + uint32_t dot_begin = label->dot_begin; + lv_label_revert_dots(obj); lv_text_get_size(&label->size_cache, label->text, font, letter_space, line_space, w, flag); + lv_label_set_dots(obj, dot_begin); + label->invalid_size_cache = false; } @@ -955,6 +945,7 @@ static void lv_label_refr_text(lv_obj_t * obj) lv_point_t size; lv_text_flag_t flag = get_label_flags(label); + lv_label_revert_dots(obj); lv_text_get_size(&size, label->text, font, letter_space, line_space, max_w, flag); lv_obj_refresh_self_size(obj); @@ -1152,16 +1143,9 @@ static void lv_label_refr_text(lv_obj_t * obj) } } else if(label->long_mode == LV_LABEL_LONG_DOT) { - if(size.y <= lv_area_get_height(&txt_coords)) { /*No dots are required, the text is short enough*/ - label->dot_end = LV_LABEL_DOT_END_INV; - } - else if(size.y <= lv_font_get_line_height(font)) { /*No dots are required for one-line texts*/ - label->dot_end = LV_LABEL_DOT_END_INV; - } - else if(lv_text_get_encoded_length(label->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/ - label->dot_end = LV_LABEL_DOT_END_INV; - } - else { + if(size.y > lv_area_get_height(&txt_coords) && /*Text overflows available area*/ + size.y > lv_font_get_line_height(font) && /*No break requested, so no dots required*/ + lv_text_get_encoded_length(label->text) > LV_LABEL_DOT_NUM) { /*Do not turn all characters into dots*/ lv_point_t p; int32_t y_overed; p.x = lv_area_get_width(&txt_coords) - @@ -1190,27 +1174,10 @@ static void lv_label_refr_text(lv_obj_t * obj) } /*Save letters under the dots and replace them with dots*/ - uint32_t byte_id_ori = byte_id; - uint32_t i; - uint8_t len = 0; - for(i = 0; i <= LV_LABEL_DOT_NUM; i++) { - len += lv_text_encoded_size(&label->text[byte_id]); - lv_text_encoded_next(label->text, &byte_id); - if(len > LV_LABEL_DOT_NUM || byte_id > txt_len) { - break; - } - } - - if(lv_label_set_dot_tmp(obj, &label->text[byte_id_ori], len)) { - for(i = 0; i < LV_LABEL_DOT_NUM; i++) { - label->text[byte_id_ori + i] = '.'; - } - label->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0'; - label->dot_end = letter_id + LV_LABEL_DOT_NUM; - } + lv_label_set_dots(obj, byte_id); } } - else if(label->long_mode == LV_LABEL_LONG_CLIP) { + else if(label->long_mode == LV_LABEL_LONG_CLIP || label->long_mode == LV_LABEL_LONG_WRAP) { /*Do nothing*/ } @@ -1220,88 +1187,30 @@ static void lv_label_refr_text(lv_obj_t * obj) static void lv_label_revert_dots(lv_obj_t * obj) { lv_label_t * label = (lv_label_t *)obj; - - if(label->long_mode != LV_LABEL_LONG_DOT) return; - if(label->dot_end == LV_LABEL_DOT_END_INV) return; - - const uint32_t letter_i = label->dot_end - LV_LABEL_DOT_NUM; - const uint32_t byte_i = lv_text_encoded_get_byte_id(label->text, letter_i); - - /*Restore the characters*/ - uint8_t i = 0; - char * dot_tmp = lv_label_get_dot_tmp(obj); - while(label->text[byte_i + i] != '\0') { - label->text[byte_i + i] = dot_tmp[i]; - i++; - } - label->text[byte_i + i] = dot_tmp[i]; - - lv_label_dot_tmp_free(obj); - - label->dot_end = LV_LABEL_DOT_END_INV; -} - -/** - * Store `len` characters from `data`. Allocates space if necessary. - * - * @param label pointer to label object - * @param len Number of characters to store. - * @return true on success. - */ -static bool lv_label_set_dot_tmp(lv_obj_t * obj, char * data, uint32_t len) -{ - - lv_label_t * label = (lv_label_t *)obj; - lv_label_dot_tmp_free(obj); /*Deallocate any existing space*/ - if(len > sizeof(char *)) { - /*Memory needs to be allocated. Allocates an additional byte - *for a NULL-terminator so it can be copied.*/ - label->dot.tmp_ptr = lv_malloc(len + 1); - if(label->dot.tmp_ptr == NULL) { - LV_LOG_ERROR("Failed to allocate memory for dot_tmp_ptr"); - return false; + if(label->dot_begin != LV_LABEL_DOT_BEGIN_INV) { + for(int i = 0; i < LV_LABEL_DOT_NUM + 1 && label->dot[i]; i++) { + label->text[label->dot_begin + i] = label->dot[i]; } - lv_memcpy(label->dot.tmp_ptr, data, len); - label->dot.tmp_ptr[len] = '\0'; - label->dot_tmp_alloc = true; - } - else { - /*Characters can be directly stored in object*/ - label->dot_tmp_alloc = false; - lv_memcpy(label->dot.tmp, data, len); - } - return true; -} - -/** - * Get the stored dot_tmp characters - * @param label pointer to label object - * @return char pointer to a stored characters. Is *not* necessarily NULL-terminated. - */ -static char * lv_label_get_dot_tmp(lv_obj_t * obj) -{ - lv_label_t * label = (lv_label_t *)obj; - if(label->dot_tmp_alloc) { - return label->dot.tmp_ptr; - } - else { - return label->dot.tmp; + label->dot_begin = LV_LABEL_DOT_BEGIN_INV; } } -/** - * Free the dot_tmp_ptr field if it was previously allocated. - * Always clears the field - * @param label pointer to label object. - */ -static void lv_label_dot_tmp_free(lv_obj_t * obj) +static void lv_label_set_dots(lv_obj_t * obj, uint32_t dot_begin) { lv_label_t * label = (lv_label_t *)obj; - if(label->dot_tmp_alloc && label->dot.tmp_ptr) { - lv_free(label->dot.tmp_ptr); + LV_ASSERT_MSG(label->dot_begin == LV_LABEL_DOT_BEGIN_INV, "Label dots already set"); + if(dot_begin != LV_LABEL_DOT_BEGIN_INV) { + /*Save characters*/ + lv_strncpy(label->dot, &label->text[dot_begin], LV_LABEL_DOT_NUM + 1); + label->dot_begin = dot_begin; + + /*Overwrite up to LV_LABEL_DOT_NUM + 1 characters with dots and null terminator*/ + int i = 0; + for(; i < LV_LABEL_DOT_NUM && label->text[dot_begin + i]; i++) { + label->text[dot_begin + i] = '.'; + } + label->text[dot_begin + i] = '\0'; } - label->dot_tmp_alloc = false; - label->dot.tmp_ptr = NULL; } static void set_ofs_x_anim(void * obj, int32_t v) diff --git a/src/widgets/label/lv_label_private.h b/src/widgets/label/lv_label_private.h index d3c3cc889..996767b6a 100644 --- a/src/widgets/label/lv_label_private.h +++ b/src/widgets/label/lv_label_private.h @@ -31,11 +31,8 @@ extern "C" { struct _lv_label_t { lv_obj_t obj; char * text; - union { - char * tmp_ptr; /**< Pointer to the allocated memory containing the character replaced by dots */ - char tmp[LV_LABEL_DOT_NUM + 1]; /**< Directly store the characters if <=4 characters */ - } dot; - uint32_t dot_end; /**< The real text length, used in dot mode */ + char dot[LV_LABEL_DOT_NUM + 1]; /**< Bytes that have been replaced with dots */ + uint32_t dot_begin; /**< Offset where bytes have been replaced with dots */ #if LV_LABEL_LONG_TXT_HINT lv_draw_label_hint_t hint; @@ -52,7 +49,6 @@ struct _lv_label_t { uint8_t static_txt : 1; /**< Flag to indicate the text is static */ uint8_t recolor : 1; /**< Enable in-line letter re-coloring*/ uint8_t expand : 1; /**< Ignore real width (used by the library with LV_LABEL_LONG_SCROLL) */ - uint8_t dot_tmp_alloc : 1; /**< 1: dot is allocated, 0: dot directly holds up to 4 chars */ uint8_t invalid_size_cache : 1; /**< 1: Recalculate size and update cache */ };