1 /**
2  * @file lv_draw_label.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_label_private.h"
10 #include "lv_draw_private.h"
11 #include "../misc/lv_area_private.h"
12 #include "lv_draw_vector_private.h"
13 #include "lv_draw_rect_private.h"
14 #include "../core/lv_obj.h"
15 #include "../misc/lv_math.h"
16 #include "../core/lv_obj_event.h"
17 #include "../misc/lv_bidi_private.h"
18 #include "../misc/lv_text_private.h"
19 #include "../misc/lv_assert.h"
20 #include "../stdlib/lv_mem.h"
21 #include "../stdlib/lv_string.h"
22 #include "../core/lv_global.h"
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 #define LABEL_RECOLOR_PAR_LENGTH 6
28 #define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
29 
30 #define font_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->font_draw_buf_handlers)
31 
32 /**********************
33  *      TYPEDEFS
34  **********************/
35 enum {
36     RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER,
37     RECOLOR_CMD_STATE_PARAMETER,
38     RECOLOR_CMD_STATE_TEXT_INPUT,
39 };
40 typedef unsigned char cmd_state_t;
41 
42 /**********************
43  *  STATIC PROTOTYPES
44  **********************/
45 static uint8_t hex_char_to_num(char hex);
46 
47 /**********************
48  *  STATIC VARIABLES
49  **********************/
50 
51 /**********************
52  *  GLOBAL VARIABLES
53  **********************/
54 
55 /**********************
56  *      MACROS
57  **********************/
58 
59 /**********************
60  *   GLOBAL FUNCTIONS
61  **********************/
62 
lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc)63 void lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc)
64 {
65     lv_memzero(dsc, sizeof(lv_draw_letter_dsc_t));
66     dsc->opa = LV_OPA_COVER;
67     dsc->color = lv_color_black();
68     dsc->font = LV_FONT_DEFAULT;
69     dsc->rotation = 0;
70     dsc->scale_x = LV_SCALE_NONE;
71     dsc->scale_y = LV_SCALE_NONE;
72     dsc->base.dsc_size = sizeof(lv_draw_letter_dsc_t);
73 }
74 
lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)75 void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
76 {
77     lv_memzero(dsc, sizeof(lv_draw_label_dsc_t));
78     dsc->opa = LV_OPA_COVER;
79     dsc->color = lv_color_black();
80     dsc->text_length = LV_TEXT_LEN_MAX;
81     dsc->font = LV_FONT_DEFAULT;
82     dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
83     dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
84     dsc->sel_color = lv_color_black();
85     dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
86     dsc->bidi_dir = LV_BASE_DIR_LTR;
87     dsc->base.dsc_size = sizeof(lv_draw_label_dsc_t);
88 }
89 
lv_draw_task_get_label_dsc(lv_draw_task_t * task)90 lv_draw_label_dsc_t * lv_draw_task_get_label_dsc(lv_draw_task_t * task)
91 {
92     return task->type == LV_DRAW_TASK_TYPE_LABEL ? (lv_draw_label_dsc_t *)task->draw_dsc : NULL;
93 }
94 
lv_draw_glyph_dsc_init(lv_draw_glyph_dsc_t * dsc)95 void lv_draw_glyph_dsc_init(lv_draw_glyph_dsc_t * dsc)
96 {
97     lv_memzero(dsc, sizeof(lv_draw_glyph_dsc_t));
98 }
99 
lv_draw_label(lv_layer_t * layer,const lv_draw_label_dsc_t * dsc,const lv_area_t * coords)100 void LV_ATTRIBUTE_FAST_MEM lv_draw_label(lv_layer_t * layer, const lv_draw_label_dsc_t * dsc,
101                                          const lv_area_t * coords)
102 {
103     if(dsc->opa <= LV_OPA_MIN) return;
104     if(dsc->text == NULL || dsc->text[0] == '\0') return;
105     if(dsc->font == NULL) {
106         LV_LOG_WARN("dsc->font == NULL");
107         return;
108     }
109 
110     LV_PROFILER_DRAW_BEGIN;
111     lv_draw_task_t * t = lv_draw_add_task(layer, coords);
112 
113     t->draw_dsc = lv_malloc(sizeof(*dsc));
114     LV_ASSERT_MALLOC(t->draw_dsc);
115     lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
116     t->type = LV_DRAW_TASK_TYPE_LABEL;
117 
118     /*The text is stored in a local variable so malloc memory for it*/
119     if(dsc->text_local) {
120         lv_draw_label_dsc_t * new_dsc = t->draw_dsc;
121         new_dsc->text = lv_strdup(dsc->text);
122     }
123 
124     lv_draw_finalize_task_creation(layer, t);
125     LV_PROFILER_DRAW_END;
126 }
127 
lv_draw_character(lv_layer_t * layer,lv_draw_label_dsc_t * dsc,const lv_point_t * point,uint32_t unicode_letter)128 void LV_ATTRIBUTE_FAST_MEM lv_draw_character(lv_layer_t * layer, lv_draw_label_dsc_t * dsc,
129                                              const lv_point_t * point, uint32_t unicode_letter)
130 {
131     if(dsc->opa <= LV_OPA_MIN) return;
132     if(dsc->font == NULL) {
133         LV_LOG_WARN("dsc->font == NULL");
134         return;
135     }
136 
137     if(lv_text_is_marker(unicode_letter)) return;
138 
139     LV_PROFILER_DRAW_BEGIN;
140 
141     lv_font_glyph_dsc_t g;
142     lv_font_get_glyph_dsc(dsc->font, &g, unicode_letter, 0);
143 
144     lv_area_t a;
145     a.x1 = point->x;
146     a.y1 = point->y;
147     a.x2 = a.x1 + g.adv_w;
148     a.y2 = a.y1 + lv_font_get_line_height(g.resolved_font ? g.resolved_font : dsc->font);
149 
150     /*lv_draw_label needs UTF8 text so convert the Unicode character to an UTF8 string */
151     uint32_t letter_buf[2];
152     letter_buf[0] = lv_text_unicode_to_encoded(unicode_letter);
153     letter_buf[1] = '\0';
154 
155     const char * letter_buf_char = (const char *)letter_buf;
156 
157 #if LV_BIG_ENDIAN_SYSTEM
158     while(*letter_buf_char == 0) ++letter_buf_char;
159 #endif
160 
161     dsc->text = letter_buf_char;
162     dsc->text_local = 1;
163 
164     lv_draw_label(layer, dsc, &a);
165     LV_PROFILER_DRAW_END;
166 }
167 
lv_draw_letter(lv_layer_t * layer,lv_draw_letter_dsc_t * dsc,const lv_point_t * point)168 void LV_ATTRIBUTE_FAST_MEM lv_draw_letter(lv_layer_t * layer, lv_draw_letter_dsc_t * dsc, const lv_point_t * point)
169 {
170     if(dsc->opa <= LV_OPA_MIN) return;
171     if(dsc->font == NULL) {
172         LV_LOG_WARN("dsc->font == NULL");
173         return;
174     }
175 
176     const lv_font_t * font = dsc->font;
177 
178     LV_PROFILER_DRAW_BEGIN;
179     lv_font_glyph_dsc_t g;
180     lv_font_get_glyph_dsc(font, &g, dsc->unicode, 0);
181 
182     font = g.resolved_font ? g.resolved_font : dsc->font;
183 
184     lv_area_t a;
185     a.x1 = point->x;
186     a.y1 = point->y;
187     a.x2 = a.x1 + g.adv_w;
188     a.y2 = a.y1 + lv_font_get_line_height(font);
189 
190     dsc->pivot.x = g.adv_w / 2 ;
191     dsc->pivot.y = font->line_height - font->base_line;
192 
193     lv_draw_task_t * t = lv_draw_add_task(layer, &a);
194 
195     t->draw_dsc = lv_malloc(sizeof(*dsc));
196     LV_ASSERT_MALLOC(t->draw_dsc);
197     lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
198     t->type = LV_DRAW_TASK_TYPE_LETTER;
199 
200     lv_draw_finalize_task_creation(layer, t);
201     LV_PROFILER_DRAW_END;
202 }
203 
lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit,const lv_draw_label_dsc_t * dsc,const lv_area_t * coords,lv_draw_glyph_cb_t cb)204 void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
205                                       const lv_area_t * coords,
206                                       lv_draw_glyph_cb_t cb)
207 {
208     const lv_font_t * font = dsc->font;
209     int32_t w;
210 
211     lv_area_t clipped_area;
212     bool clip_ok = lv_area_intersect(&clipped_area, coords, draw_unit->clip_area);
213     if(!clip_ok) return;
214 
215     lv_text_align_t align = dsc->align;
216     lv_base_dir_t base_dir = dsc->bidi_dir;
217 
218     lv_bidi_calculate_align(&align, &base_dir, dsc->text);
219 
220     if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
221         /*Normally use the label's width as width*/
222         w = lv_area_get_width(coords);
223     }
224     else {
225         /*If EXPAND is enabled then not limit the text's width to the object's width*/
226         lv_point_t p;
227         lv_text_get_size(&p, dsc->text, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
228                          dsc->flag);
229         w = p.x;
230     }
231 
232     int32_t line_height_font = lv_font_get_line_height(font);
233     int32_t line_height = line_height_font + dsc->line_space;
234 
235     /*Init variables for the first line*/
236     int32_t line_width = 0;
237     lv_point_t pos;
238     lv_point_set(&pos, coords->x1, coords->y1);
239 
240     int32_t x_ofs = 0;
241     int32_t y_ofs = 0;
242     x_ofs = dsc->ofs_x;
243     y_ofs = dsc->ofs_y;
244     pos.y += y_ofs;
245 
246     uint32_t line_start     = 0;
247     int32_t last_line_start = -1;
248 
249     /*Check the hint to use the cached info*/
250     if(dsc->hint && y_ofs == 0 && coords->y1 < 0) {
251         /*If the label changed too much recalculate the hint.*/
252         if(LV_ABS(dsc->hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
253             dsc->hint->line_start = -1;
254         }
255         last_line_start = dsc->hint->line_start;
256     }
257 
258     /*Use the hint if it's valid*/
259     if(dsc->hint && last_line_start >= 0) {
260         line_start = last_line_start;
261         pos.y += dsc->hint->y;
262     }
263 
264     uint32_t remaining_len = dsc->text_length;
265 
266     uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space,
267                                                            w, NULL, dsc->flag);
268 
269     /*Go the first visible line*/
270     while(pos.y + line_height_font < draw_unit->clip_area->y1) {
271         /*Go to next line*/
272         line_start = line_end;
273         line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
274         pos.y += line_height;
275 
276         /*Save at the threshold coordinate*/
277         if(dsc->hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && dsc->hint->line_start < 0) {
278             dsc->hint->line_start = line_start;
279             dsc->hint->y          = pos.y - coords->y1;
280             dsc->hint->coord_y    = coords->y1;
281         }
282 
283         if(dsc->text[line_start] == '\0') return;
284     }
285 
286     /*Align to middle*/
287     if(align == LV_TEXT_ALIGN_CENTER) {
288         line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
289                                                   dsc->flag);
290 
291         pos.x += (lv_area_get_width(coords) - line_width) / 2;
292 
293     }
294     /*Align to the right*/
295     else if(align == LV_TEXT_ALIGN_RIGHT) {
296         line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
297                                                   dsc->flag);
298         pos.x += lv_area_get_width(coords) - line_width;
299     }
300 
301     uint32_t sel_start = dsc->sel_start;
302     uint32_t sel_end = dsc->sel_end;
303     if(sel_start > sel_end) {
304         uint32_t tmp = sel_start;
305         sel_start = sel_end;
306         sel_end = tmp;
307     }
308 
309     lv_area_t bg_coords;
310     lv_draw_glyph_dsc_t draw_letter_dsc;
311     lv_draw_glyph_dsc_init(&draw_letter_dsc);
312     draw_letter_dsc.opa = dsc->opa;
313     draw_letter_dsc.bg_coords = &bg_coords;
314     draw_letter_dsc.color = dsc->color;
315     draw_letter_dsc.rotation = dsc->rotation;
316 
317     lv_draw_fill_dsc_t fill_dsc;
318     lv_draw_fill_dsc_init(&fill_dsc);
319     fill_dsc.opa = dsc->opa;
320     int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
321     int32_t line_start_x;
322     uint32_t next_char_offset;
323     uint32_t recolor_command_start_index = 0;
324     int32_t letter_w;
325     cmd_state_t recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
326     lv_color_t recolor = lv_color_black(); /* Holds the selected color inside the recolor command */
327     uint8_t is_first_space_after_cmd = 0;
328 
329     /*Write out all lines*/
330     while(remaining_len && dsc->text[line_start] != '\0') {
331         pos.x += x_ofs;
332         line_start_x = pos.x;
333 
334         /*Write all letter of a line*/
335         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
336         next_char_offset = 0;
337 #if LV_USE_BIDI
338         char * bidi_txt = lv_malloc(line_end - line_start + 1);
339         LV_ASSERT_MALLOC(bidi_txt);
340         lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
341 #else
342         const char * bidi_txt = dsc->text + line_start;
343 #endif
344 
345         while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) {
346             uint32_t logical_char_pos = 0;
347 
348             /* Check if the text selection is enabled */
349             if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
350 #if LV_USE_BIDI
351                 logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
352                 uint32_t t = lv_text_encoded_get_char_id(bidi_txt, next_char_offset);
353                 logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
354 #else
355                 logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
356 #endif
357             }
358 
359             uint32_t letter;
360             uint32_t letter_next;
361             lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &next_char_offset);
362 
363             /* If recolor is enabled */
364             if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
365 
366                 if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
367                     /* Handle the recolor command marker depending of the current recolor state */
368 
369                     if(recolor_cmd_state == RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER) {
370                         recolor_command_start_index = next_char_offset;
371                         recolor_cmd_state = RECOLOR_CMD_STATE_PARAMETER;
372                         continue;
373                     }
374                     /*Other start char in parameter escaped cmd. char*/
375                     else if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
376                         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
377                     }
378                     /* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */
379                     else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
380                         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
381                         continue;
382                     }
383                 }
384 
385                 /* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */
386                 if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) {
387                     is_first_space_after_cmd = 1;
388                 }
389                 else {
390                     is_first_space_after_cmd = 0;
391                 }
392 
393                 /* Skip the color parameter and wait the space after it
394                  * Once we have reach the space ' ', then we will extract the color information
395                  * and store it into the recolor variable */
396                 if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
397                     /* Not an space? Continue with the next character */
398                     if(letter != ' ') {
399                         continue;
400                     }
401 
402                     /*Get the recolor parameter*/
403                     if((next_char_offset - recolor_command_start_index) == LABEL_RECOLOR_PAR_LENGTH + 1) {
404                         /* Temporary buffer to hold the recolor information */
405                         char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
406                         lv_memcpy(buf, &bidi_txt[recolor_command_start_index], LABEL_RECOLOR_PAR_LENGTH);
407                         buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
408 
409                         uint8_t r, g, b;
410                         r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
411                         g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
412                         b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
413 
414                         recolor = lv_color_make(r, g, b);
415                     }
416                     else {
417                         recolor.red = dsc->color.red;
418                         recolor.blue = dsc->color.blue;
419                         recolor.green = dsc->color.green;
420                     }
421 
422                     /*After the parameter the text is in the command*/
423                     recolor_cmd_state = RECOLOR_CMD_STATE_TEXT_INPUT;
424                 }
425 
426                 /* Don't draw the first space after the recolor command */
427                 if(is_first_space_after_cmd) {
428                     continue;
429                 }
430             }
431 
432             /* If we're in the CMD_STATE_IN state then we need to subtract the recolor command length */
433             if(((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) && (recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT)) {
434                 logical_char_pos -= (LABEL_RECOLOR_PAR_LENGTH + 1);
435             }
436 
437             letter_w = lv_font_get_glyph_width(font, letter, letter_next);
438 
439             /*Always set the bg_coordinates for placeholder drawing*/
440             bg_coords.x1 = pos.x;
441             bg_coords.y1 = pos.y;
442             bg_coords.x2 = pos.x + letter_w - 1;
443             bg_coords.y2 = pos.y + line_height - 1;
444 
445             if(next_char_offset >= line_end - line_start) {
446                 if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
447                     lv_area_t fill_area;
448                     fill_area.x1 = line_start_x;
449                     fill_area.x2 = pos.x + letter_w - 1;
450                     fill_area.y1 = pos.y + font->line_height - font->base_line - font->underline_position;
451                     fill_area.y2 = fill_area.y1 + underline_width - 1;
452 
453                     fill_dsc.color = dsc->color;
454                     cb(draw_unit, NULL, &fill_dsc, &fill_area);
455                 }
456                 if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
457                     lv_area_t fill_area;
458                     fill_area.x1 = line_start_x;
459                     fill_area.x2 = pos.x + letter_w - 1;
460                     fill_area.y1 = pos.y + (font->line_height - font->base_line) * 2 / 3 + font->underline_thickness / 2;
461                     fill_area.y2 = fill_area.y1 + underline_width - 1;
462 
463                     fill_dsc.color = dsc->color;
464                     cb(draw_unit, NULL, &fill_dsc, &fill_area);
465                 }
466             }
467 
468             /* Handle text selection */
469             if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL
470                && logical_char_pos >= sel_start && logical_char_pos < sel_end) {
471                 draw_letter_dsc.color = dsc->sel_color;
472                 fill_dsc.color = dsc->sel_bg_color;
473                 cb(draw_unit, NULL, &fill_dsc, &bg_coords);
474             }
475             else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
476                 draw_letter_dsc.color = recolor;
477             }
478             else {
479                 draw_letter_dsc.color = dsc->color;
480             }
481 
482             lv_draw_unit_draw_letter(draw_unit, &draw_letter_dsc, &pos, font, letter, cb);
483 
484             if(letter_w > 0) {
485                 pos.x += letter_w + dsc->letter_space;
486             }
487         }
488 
489 #if LV_USE_BIDI
490         lv_free(bidi_txt);
491         bidi_txt = NULL;
492 #endif
493         /*Go to next line*/
494         remaining_len -= line_end - line_start;
495         line_start = line_end;
496         if(remaining_len) {
497             line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
498         }
499 
500         pos.x = coords->x1;
501         /*Align to middle*/
502         if(align == LV_TEXT_ALIGN_CENTER) {
503             line_width =
504                 lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
505 
506             pos.x += (lv_area_get_width(coords) - line_width) / 2;
507         }
508         /*Align to the right*/
509         else if(align == LV_TEXT_ALIGN_RIGHT) {
510             line_width =
511                 lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
512             pos.x += lv_area_get_width(coords) - line_width;
513         }
514 
515         /*Go the next line position*/
516         pos.y += line_height;
517 
518         if(pos.y > draw_unit->clip_area->y2) break;
519     }
520 
521     if(draw_letter_dsc._draw_buf) lv_draw_buf_destroy(draw_letter_dsc._draw_buf);
522 
523     LV_ASSERT_MEM_INTEGRITY();
524 }
525 
526 /**********************
527  *   STATIC FUNCTIONS
528  **********************/
529 
530 /**
531  * Convert a hexadecimal characters to a number (0..15)
532  * @param hex Pointer to a hexadecimal character (0..9, A..F)
533  * @return the numerical value of `hex` or 0 on error
534  */
hex_char_to_num(char hex)535 static uint8_t hex_char_to_num(char hex)
536 {
537     if(hex >= '0' && hex <= '9') return hex - '0';
538     if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
539     return 'A' <= hex && hex <= 'F' ? hex - 'A' + 10 : 0;
540 }
541 
lv_draw_unit_draw_letter(lv_draw_unit_t * draw_unit,lv_draw_glyph_dsc_t * dsc,const lv_point_t * pos,const lv_font_t * font,uint32_t letter,lv_draw_glyph_cb_t cb)542 void lv_draw_unit_draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc,  const lv_point_t * pos,
543                               const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb)
544 {
545     lv_font_glyph_dsc_t g;
546 
547     if(lv_text_is_marker(letter)) /*Markers are valid letters but should not be rendered.*/
548         return;
549 
550     LV_PROFILER_DRAW_BEGIN;
551     bool g_ret = lv_font_get_glyph_dsc(font, &g, letter, '\0');
552     if(g_ret == false) {
553         /*Add warning if the dsc is not found*/
554         LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%" LV_PRIX32, letter);
555     }
556 
557     /*Don't draw anything if the character is empty. E.g. space*/
558     if((g.box_h == 0) || (g.box_w == 0)) {
559         LV_PROFILER_DRAW_END;
560         return;
561     }
562 
563     lv_area_t letter_coords;
564     letter_coords.x1 = pos->x + g.ofs_x;
565     letter_coords.x2 = letter_coords.x1 + g.box_w - 1;
566     letter_coords.y1 = pos->y + (font->line_height - font->base_line) - g.box_h - g.ofs_y;
567     letter_coords.y2 = letter_coords.y1 + g.box_h - 1;
568     lv_area_move(&letter_coords, -dsc->pivot.x, -dsc->pivot.y);
569 
570     /*If the letter is completely out of mask don't draw it*/
571     if(lv_area_is_out(&letter_coords, draw_unit->clip_area, 0) &&
572        dsc->bg_coords &&
573        lv_area_is_out(dsc->bg_coords, draw_unit->clip_area, 0)) {
574         LV_PROFILER_DRAW_END;
575         return;
576     }
577 
578     if(g.resolved_font) {
579         lv_draw_buf_t * draw_buf = NULL;
580         if(LV_FONT_GLYPH_FORMAT_NONE < g.format && g.format < LV_FONT_GLYPH_FORMAT_IMAGE) {
581             /*Only check draw buf for bitmap glyph*/
582             draw_buf = lv_draw_buf_reshape(dsc->_draw_buf, 0, g.box_w, g.box_h, LV_STRIDE_AUTO);
583             if(draw_buf == NULL) {
584                 if(dsc->_draw_buf) lv_draw_buf_destroy(dsc->_draw_buf);
585 
586                 uint32_t h = g.box_h;
587                 if(h * g.box_w < 64) h *= 2; /*Alloc a slightly larger buffer*/
588                 draw_buf = lv_draw_buf_create_ex(font_draw_buf_handlers, g.box_w, h, LV_COLOR_FORMAT_A8, LV_STRIDE_AUTO);
589                 LV_ASSERT_MALLOC(draw_buf);
590                 draw_buf->header.h = g.box_h;
591                 dsc->_draw_buf = draw_buf;
592             }
593         }
594 
595         dsc->format = g.format;
596     }
597     else {
598         dsc->format = LV_FONT_GLYPH_FORMAT_NONE;
599     }
600 
601     dsc->letter_coords = &letter_coords;
602     dsc->g = &g;
603     cb(draw_unit, dsc, NULL, NULL);
604 
605     lv_font_glyph_release_draw_data(&g);
606 
607     LV_PROFILER_DRAW_END;
608 }
609