perf(span): optimize span render performance up by 50% (#7290)
@ -67,6 +67,7 @@ void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
|
||||
lv_memzero(dsc, sizeof(lv_draw_label_dsc_t));
|
||||
dsc->opa = LV_OPA_COVER;
|
||||
dsc->color = lv_color_black();
|
||||
dsc->text_length = LV_TEXT_LEN_MAX;
|
||||
dsc->font = LV_FONT_DEFAULT;
|
||||
dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
|
||||
dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
|
||||
@ -214,14 +215,16 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
|
||||
pos.y += dsc->hint->y;
|
||||
}
|
||||
|
||||
uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL,
|
||||
dsc->flag);
|
||||
uint32_t remaining_len = dsc->text_length;
|
||||
|
||||
uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space,
|
||||
w, NULL, dsc->flag);
|
||||
|
||||
/*Go the first visible line*/
|
||||
while(pos.y + line_height_font < draw_unit->clip_area->y1) {
|
||||
/*Go to next line*/
|
||||
line_start = line_end;
|
||||
line_end += lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
|
||||
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
|
||||
pos.y += line_height;
|
||||
|
||||
/*Save at the threshold coordinate*/
|
||||
@ -277,7 +280,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
|
||||
uint8_t is_first_space_after_cmd = 0;
|
||||
|
||||
/*Write out all lines*/
|
||||
while(dsc->text[line_start] != '\0') {
|
||||
while(remaining_len && dsc->text[line_start] != '\0') {
|
||||
pos.x += x_ofs;
|
||||
line_start_x = pos.x;
|
||||
|
||||
@ -292,7 +295,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
|
||||
const char * bidi_txt = dsc->text + line_start;
|
||||
#endif
|
||||
|
||||
while(next_char_offset < line_end - line_start) {
|
||||
while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) {
|
||||
uint32_t logical_char_pos = 0;
|
||||
|
||||
/* Check if the text selection is enabled */
|
||||
@ -440,8 +443,11 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
|
||||
bidi_txt = NULL;
|
||||
#endif
|
||||
/*Go to next line*/
|
||||
remaining_len -= line_end - line_start;
|
||||
line_start = line_end;
|
||||
line_end += lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
|
||||
if(remaining_len) {
|
||||
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
|
||||
}
|
||||
|
||||
pos.x = coords->x1;
|
||||
/*Align to middle*/
|
||||
|
@ -33,6 +33,7 @@ typedef struct {
|
||||
lv_draw_dsc_base_t base;
|
||||
|
||||
const char * text;
|
||||
uint32_t text_length;
|
||||
const lv_font_t * font;
|
||||
uint32_t sel_start;
|
||||
uint32_t sel_end;
|
||||
|
@ -106,7 +106,7 @@ void lv_text_get_size(lv_point_t * size_res, const char * text, const lv_font_t
|
||||
|
||||
/*Calc. the height and longest line*/
|
||||
while(text[line_start] != '\0') {
|
||||
new_line_start += lv_text_get_next_line(&text[line_start], font, letter_space, max_width, NULL, flag);
|
||||
new_line_start += lv_text_get_next_line(&text[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_width, NULL, flag);
|
||||
|
||||
if((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(int32_t)) {
|
||||
LV_LOG_WARN("integer overflow while calculating text height");
|
||||
@ -246,6 +246,9 @@ static uint32_t lv_text_get_next_word(const char * txt, const lv_font_t * font,
|
||||
if(break_index == NO_BREAK_FOUND && (cur_w - letter_space) > max_width) {
|
||||
break_index = i;
|
||||
break_letter_count = word_len - 1;
|
||||
if(flag & LV_TEXT_FLAG_BREAK_ALL) {
|
||||
break;
|
||||
}
|
||||
/*break_index is now pointing at the character that doesn't fit*/
|
||||
}
|
||||
|
||||
@ -315,9 +318,9 @@ static uint32_t lv_text_get_next_word(const char * txt, const lv_font_t * font,
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font,
|
||||
int32_t letter_space, int32_t max_width,
|
||||
int32_t * used_width, lv_text_flag_t flag)
|
||||
uint32_t lv_text_get_next_line(const char * txt, uint32_t len,
|
||||
const lv_font_t * font, int32_t letter_space,
|
||||
int32_t max_width, int32_t * used_width, lv_text_flag_t flag)
|
||||
{
|
||||
if(used_width) *used_width = 0;
|
||||
|
||||
@ -331,10 +334,10 @@ uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font,
|
||||
*without thinking about word wrapping*/
|
||||
if((flag & LV_TEXT_FLAG_EXPAND) || (flag & LV_TEXT_FLAG_FIT)) {
|
||||
uint32_t i;
|
||||
for(i = 0; txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) {
|
||||
for(i = 0; i < len && txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) {
|
||||
/*Just find the new line chars or string ends by incrementing `i`*/
|
||||
}
|
||||
if(txt[i] != '\0') i++; /*To go beyond `\n`*/
|
||||
if(i < len && txt[i] != '\0') i++; /*To go beyond `\n`*/
|
||||
if(used_width) *used_width = -1;
|
||||
return i;
|
||||
}
|
||||
@ -344,7 +347,7 @@ uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font,
|
||||
|
||||
uint32_t i = 0; /*Iterating index into txt*/
|
||||
|
||||
while(txt[i] != '\0' && max_width > 0) {
|
||||
while(i < len && txt[i] != '\0' && max_width > 0) {
|
||||
lv_text_flag_t word_flag = flag;
|
||||
if(i == 0) word_flag |= LV_TEXT_FLAG_BREAK_ALL;
|
||||
|
||||
|
@ -30,6 +30,8 @@ extern "C" {
|
||||
#define LV_TXT_ENC_UTF8 1
|
||||
#define LV_TXT_ENC_ASCII 2
|
||||
|
||||
#define LV_TEXT_LEN_MAX UINT32_MAX
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
@ -31,6 +31,7 @@ extern "C" {
|
||||
/**
|
||||
* Get the next line of text. Check line length and break chars too.
|
||||
* @param txt a '\0' terminated string
|
||||
* @param len length of 'txt' in bytes
|
||||
* @param font pointer to a font
|
||||
* @param letter_space letter space
|
||||
* @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid
|
||||
@ -41,7 +42,7 @@ extern "C" {
|
||||
* @return the index of the first char of the new line (in byte index not letter index. With UTF-8
|
||||
* they are different)
|
||||
*/
|
||||
uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font, int32_t letter_space,
|
||||
uint32_t lv_text_get_next_line(const char * txt, uint32_t len, const lv_font_t * font, int32_t letter_space,
|
||||
int32_t max_width, int32_t * used_width, lv_text_flag_t flag);
|
||||
|
||||
/**
|
||||
|
@ -346,7 +346,7 @@ void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t
|
||||
bool last_line = y + letter_height + line_space + letter_height > max_h;
|
||||
if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL;
|
||||
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag);
|
||||
if(byte_id < new_line_start || txt[new_line_start] == '\0')
|
||||
break; /*The line of 'index' letter begins at 'line_start'*/
|
||||
|
||||
@ -437,7 +437,7 @@ uint32_t lv_label_get_letter_on(const lv_obj_t * obj, lv_point_t * pos_in, bool
|
||||
bool last_line = y + letter_height + line_space + letter_height > max_h;
|
||||
if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL;
|
||||
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag);
|
||||
|
||||
if(pos.y <= y + letter_height) {
|
||||
/*The line is found (stored in 'line_start')*/
|
||||
@ -558,7 +558,7 @@ bool lv_label_is_char_under_pos(const lv_obj_t * obj, lv_point_t * pos)
|
||||
bool last_line = y + letter_height + line_space + letter_height > max_h;
|
||||
if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL;
|
||||
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
|
||||
new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag);
|
||||
|
||||
if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
|
||||
y += letter_height + line_space;
|
||||
|
@ -738,7 +738,7 @@ static bool lv_text_get_snippet(const char * txt, const lv_font_t * font,
|
||||
real_max_width++;
|
||||
#endif
|
||||
|
||||
uint32_t ofs = lv_text_get_next_line(txt, font, letter_space, real_max_width, use_width, flag);
|
||||
uint32_t ofs = lv_text_get_next_line(txt, LV_TEXT_LEN_MAX, font, letter_space, real_max_width, use_width, flag);
|
||||
*end_ofs = ofs;
|
||||
|
||||
if(txt[ofs] == '\0' && *use_width < max_width && !(ofs && (txt[ofs - 1] == '\n' || txt[ofs - 1] == '\r'))) {
|
||||
@ -1018,16 +1018,11 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
|
||||
if(last_snippet->txt[last_snippet->bytes] == '\0') {
|
||||
next_line_h = 0;
|
||||
lv_span_t * next_span = lv_ll_get_next(&spans->child_ll, last_snippet->span);
|
||||
if(next_span) { /* have the next line */
|
||||
if(next_span && next_span->txt && next_span->txt[0]) { /* have the next line */
|
||||
next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space;
|
||||
}
|
||||
}
|
||||
if(txt_pos.y + max_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */
|
||||
if(last_snippet->txt[last_snippet->bytes] != '\0') {
|
||||
last_snippet->bytes = lv_strlen(last_snippet->txt);
|
||||
last_snippet->txt_w = lv_text_get_width_with_flags(last_snippet->txt, last_snippet->bytes, last_snippet->font,
|
||||
last_snippet->letter_space, label_draw_dsc.flag);
|
||||
}
|
||||
ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS;
|
||||
is_end_line = true;
|
||||
}
|
||||
@ -1076,76 +1071,45 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
|
||||
}
|
||||
uint32_t txt_bytes = pinfo->bytes;
|
||||
|
||||
/* overflow */
|
||||
uint32_t dot_letter_w = 0;
|
||||
uint32_t dot_width = 0;
|
||||
if(ellipsis_valid) {
|
||||
dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.');
|
||||
dot_width = dot_letter_w * 3;
|
||||
}
|
||||
int32_t ellipsis_width = coords.x1 + max_width - dot_width;
|
||||
|
||||
uint32_t j = 0;
|
||||
while(j < txt_bytes) {
|
||||
/* skip invalid fields */
|
||||
if(pos.x > clip_area.x2) {
|
||||
break;
|
||||
}
|
||||
uint32_t letter = lv_text_encoded_next(bidi_txt, &j);
|
||||
uint32_t letter_next = lv_text_encoded_next(&bidi_txt[j], NULL);
|
||||
int32_t letter_w = lv_font_get_glyph_width(pinfo->font, letter, letter_next);
|
||||
|
||||
/* skip invalid fields */
|
||||
if(pos.x + letter_w + pinfo->letter_space < clip_area.x1) {
|
||||
if(letter_w > 0) {
|
||||
pos.x = pos.x + letter_w + pinfo->letter_space;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ellipsis_valid && pos.x + letter_w + pinfo->letter_space > ellipsis_width) {
|
||||
for(int ell = 0; ell < 3; ell++) {
|
||||
lv_draw_character(layer, &label_draw_dsc, &pos, '.');
|
||||
pos.x = pos.x + dot_letter_w + pinfo->letter_space;
|
||||
}
|
||||
if(pos.x <= ellipsis_width) {
|
||||
pos.x = ellipsis_width + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
lv_draw_character(layer, &label_draw_dsc, &pos, letter);
|
||||
if(letter_w > 0) {
|
||||
pos.x = pos.x + letter_w + pinfo->letter_space;
|
||||
}
|
||||
}
|
||||
label_draw_dsc.text = bidi_txt;
|
||||
label_draw_dsc.text_length = txt_bytes;
|
||||
label_draw_dsc.letter_space = pinfo->letter_space;
|
||||
label_draw_dsc.decor = lv_span_get_style_text_decor(obj, pinfo->span);
|
||||
lv_area_t a;
|
||||
a.x1 = pos.x;
|
||||
a.y1 = pos.y;
|
||||
a.x2 = a.x1 + pinfo->txt_w;
|
||||
a.y2 = a.y1 + pinfo->line_h;
|
||||
|
||||
bool need_draw_ellipsis = false;
|
||||
uint32_t dot_width = 0;
|
||||
/* deal overflow */
|
||||
if(ellipsis_valid) {
|
||||
uint32_t dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.');
|
||||
dot_width = dot_letter_w * 3;
|
||||
|
||||
label_draw_dsc.flag = LV_TEXT_FLAG_BREAK_ALL;
|
||||
uint32_t next_ofs;
|
||||
need_draw_ellipsis = lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, coords.x2 - a.x1 - dot_width,
|
||||
label_draw_dsc.flag, &pinfo->txt_w, &next_ofs);
|
||||
a.x2 = a.x1 + pinfo->txt_w;
|
||||
label_draw_dsc.text_length = next_ofs + 1;
|
||||
}
|
||||
|
||||
/* draw decor */
|
||||
lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span);
|
||||
if(decor != LV_TEXT_DECOR_NONE) {
|
||||
lv_draw_line_dsc_t line_dsc;
|
||||
lv_draw_line_dsc_init(&line_dsc);
|
||||
line_dsc.color = label_draw_dsc.color;
|
||||
line_dsc.width = label_draw_dsc.font->underline_thickness ? pinfo->font->underline_thickness : 1;
|
||||
line_dsc.opa = label_draw_dsc.opa;
|
||||
line_dsc.blend_mode = label_draw_dsc.blend_mode;
|
||||
lv_draw_label(layer, &label_draw_dsc, &a);
|
||||
|
||||
if(decor & LV_TEXT_DECOR_STRIKETHROUGH) {
|
||||
int32_t y = pos.y + ((pinfo->line_h - line_space) >> 1) + (line_dsc.width >> 1);
|
||||
lv_point_precise_set(&line_dsc.p1, txt_pos.x, y);
|
||||
lv_point_precise_set(&line_dsc.p2, pos.x, y);
|
||||
lv_draw_line(layer, &line_dsc);
|
||||
if(need_draw_ellipsis) {
|
||||
label_draw_dsc.text = "...";
|
||||
a.x1 = a.x2;
|
||||
a.x2 = a.x1 + dot_width;
|
||||
lv_draw_label(layer, &label_draw_dsc, &a);
|
||||
}
|
||||
|
||||
if(decor & LV_TEXT_DECOR_UNDERLINE) {
|
||||
int32_t y = pos.y + pinfo->line_h - line_space - pinfo->font->base_line - pinfo->font->underline_position;
|
||||
lv_point_precise_set(&line_dsc.p1, txt_pos.x, y);
|
||||
lv_point_precise_set(&line_dsc.p2, pos.x, y);
|
||||
lv_draw_line(layer, &line_dsc);
|
||||
}
|
||||
}
|
||||
txt_pos.x = pos.x;
|
||||
txt_pos.x = a.x2;
|
||||
}
|
||||
|
||||
Next_line_init:
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -134,7 +134,7 @@ void test_txt_next_line_should_handle_empty_string(void)
|
||||
int32_t max_width = 0;
|
||||
lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
|
||||
|
||||
uint32_t next_line = lv_text_get_next_line("", font_ptr, letter_space, max_width, NULL, flag);
|
||||
uint32_t next_line = lv_text_get_next_line("", 0, font_ptr, letter_space, max_width, NULL, flag);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(0, next_line);
|
||||
}
|
||||
|