1 /**
2  * MIT License
3  *
4  * -----------------------------------------------------------------------------
5  * Copyright (c) 2008-24 Think Silicon Single Member PC
6  * -----------------------------------------------------------------------------
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights to
11  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12  * the Software, and to permit persons to whom the Software is furnished to do so,
13  * subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the next paragraph)
16  * shall be included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
19  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
23  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26 
27 /**
28  * @file lv_draw_nema_gfx_fill.c
29  *
30  */
31 
32 /*********************
33  *      INCLUDES
34  *********************/
35 #include "lv_draw_nema_gfx.h"
36 
37 #if LV_USE_NEMA_GFX
38 #include "../../misc/lv_utils.h"
39 #include "../../misc/lv_text_private.h"
40 #include "../../lvgl.h"
41 #include "../../libs/freetype/lv_freetype_private.h"
42 #include "../../core/lv_global.h"
43 
44 /*********************
45  *      DEFINES
46  *********************/
47 #define LABEL_RECOLOR_PAR_LENGTH 6
48 #define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
49 #define FT_F26DOT6_SHIFT 6
50 
51 #define font_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->font_draw_buf_handlers)
52 
53 /** After converting the font reference size, it is also necessary to scale the 26dot6 data
54  * in the path to the real physical size
55  */
56 #define FT_F26DOT6_TO_PATH_SCALE(x) (LV_FREETYPE_F26DOT6_TO_FLOAT(x) / (1 << FT_F26DOT6_SHIFT))
57 
58 /*Forward declarations*/
59 void nema_set_matrix(nema_matrix3x3_t m);
60 void nema_raster_rect(int x, int y, int w, int h);
61 
62 /**********************
63  *      TYPEDEFS
64  **********************/
65 enum {
66     RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER,
67     RECOLOR_CMD_STATE_PARAMETER,
68     RECOLOR_CMD_STATE_TEXT_INPUT,
69 };
70 typedef unsigned char cmd_state_t;
71 
72 /**********************
73  *  STATIC PROTOTYPES
74  **********************/
75 
76 static void _draw_nema_gfx_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc,
77                                   lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area);
78 
79 static void _draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
80                                            const lv_area_t * coords);
81 
82 static inline uint8_t _bpp_nema_gfx_format(lv_draw_glyph_dsc_t * glyph_draw_dsc);
83 
84 static void _draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc,  const lv_point_t * pos,
85                          const lv_font_t * font, uint32_t letter);
86 
87 static uint8_t hex_char_to_num(char hex);
88 
89 static bool is_raw_bitmap;
90 
91 #if LV_USE_FREETYPE && LV_USE_NEMA_VG
92 
93     #include "lv_nema_gfx_path.h"
94 
95     static void _draw_nema_gfx_outline(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc);
96 
97     static void freetype_outline_event_cb(lv_event_t * e);
98 
99     static void lv_nema_gfx_outline_push(const lv_freetype_outline_event_param_t * param);
100 
101     static void lv_nema_outline_event_alloc(const lv_freetype_outline_event_param_t * param);
102 #endif
103 
104 /**********************
105  *   GLOBAL FUNCTIONS
106  **********************/
lv_draw_nema_gfx_label_init(lv_draw_unit_t * draw_unit)107 void lv_draw_nema_gfx_label_init(lv_draw_unit_t * draw_unit)
108 {
109 #if LV_USE_FREETYPE
110     /*Set up the freetype outline event*/
111     lv_freetype_outline_add_event(freetype_outline_event_cb, LV_EVENT_ALL, draw_unit);
112 #else
113     LV_UNUSED(draw_unit);
114 #endif /* LV_USE_FREETYPE */
115 }
116 
lv_draw_nema_gfx_label(lv_draw_unit_t * draw_unit,const lv_draw_label_dsc_t * dsc,const lv_area_t * coords)117 void lv_draw_nema_gfx_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords)
118 {
119     if(dsc->opa <= LV_OPA_MIN) return;
120 
121     lv_layer_t * layer = draw_unit->target_layer;
122 
123     lv_area_t clip_area;
124     lv_area_copy(&clip_area, draw_unit->clip_area);
125     lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
126 
127     lv_color_format_t dst_cf = layer->draw_buf->header.cf;
128     uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
129 
130     nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
131                       lv_area_get_height(&(layer->buf_area)), dst_nema_cf,
132                       lv_area_get_width(&(layer->buf_area))*lv_color_format_get_size(dst_cf));
133 
134     nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area));
135 
136     _draw_label_iterate_characters(draw_unit, dsc, coords);
137 
138     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)draw_unit;
139     nema_cl_submit(&(draw_nema_gfx_unit->cl));
140     nema_cl_wait(&(draw_nema_gfx_unit->cl));
141 }
142 
143 /**********************
144  *   STATIC FUNCTIONS
145  **********************/
146 #if LV_USE_FREETYPE && LV_USE_NEMA_VG
147 
_draw_nema_gfx_outline(lv_draw_unit_t * draw_unit,lv_draw_glyph_dsc_t * glyph_draw_dsc)148 static void _draw_nema_gfx_outline(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc)
149 {
150 
151     lv_area_t blend_area;
152     if(!_lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, draw_unit->clip_area))
153         return;
154 
155     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)draw_unit;
156 
157     lv_nema_gfx_path_t * nema_gfx_path = (lv_nema_gfx_path_t *)glyph_draw_dsc->glyph_data;
158 
159     lv_point_t pos = {glyph_draw_dsc->letter_coords->x1, glyph_draw_dsc->letter_coords->y1};
160 
161     float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(glyph_draw_dsc->g->resolved_font));
162 
163     /*Calculate Path Matrix*/
164     nema_matrix3x3_t matrix;
165     nema_mat3x3_load_identity(matrix);
166     nema_mat3x3_scale(matrix, scale, -scale);
167     nema_mat3x3_translate(matrix, pos.x - glyph_draw_dsc->g->ofs_x,
168                           pos.y + glyph_draw_dsc->g->box_h + glyph_draw_dsc->g->ofs_y);
169 
170     nema_vg_path_clear(nema_gfx_path->path);
171     nema_vg_paint_clear(nema_gfx_path->paint);
172 
173     nema_vg_set_fill_rule(NEMA_VG_FILL_EVEN_ODD);
174 
175     nema_vg_path_set_shape(nema_gfx_path->path, nema_gfx_path->seg_size, nema_gfx_path->seg, nema_gfx_path->data_size,
176                            nema_gfx_path->data);
177 
178     nema_vg_paint_set_type(nema_gfx_path->paint, NEMA_VG_PAINT_COLOR);
179 
180     lv_color32_t dsc_col32 = lv_color_to_32(glyph_draw_dsc->color, glyph_draw_dsc->opa);
181     uint32_t nema_dsc_color = nema_rgba(dsc_col32.red, dsc_col32.green, dsc_col32.blue, dsc_col32.alpha);
182 
183     nema_vg_paint_set_paint_color(nema_gfx_path->paint, nema_dsc_color);
184 
185     nema_vg_path_set_matrix(nema_gfx_path->path, matrix);
186     nema_vg_draw_path(nema_gfx_path->path, nema_gfx_path->paint);
187 
188     return;
189 }
190 
freetype_outline_event_cb(lv_event_t * e)191 static void freetype_outline_event_cb(lv_event_t * e)
192 {
193     LV_PROFILER_DRAW_BEGIN;
194     lv_event_code_t code = lv_event_get_code(e);
195     lv_freetype_outline_event_param_t * param = lv_event_get_param(e);
196 
197     switch(code) {
198         case LV_EVENT_CREATE:
199             param->outline = lv_nema_gfx_path_create();
200             lv_nema_outline_event_alloc(param);
201             break;
202         case LV_EVENT_DELETE:
203             lv_nema_gfx_path_destroy(param->outline);
204             break;
205         case LV_EVENT_INSERT:
206             lv_nema_gfx_outline_push(param);
207             break;
208         default:
209             LV_LOG_WARN("unknown event code: %d", code);
210             break;
211     }
212     LV_PROFILER_DRAW_END;
213 }
214 
lv_nema_gfx_outline_push(const lv_freetype_outline_event_param_t * param)215 static void lv_nema_gfx_outline_push(const lv_freetype_outline_event_param_t * param)
216 {
217     LV_PROFILER_DRAW_BEGIN;
218     lv_nema_gfx_path_t * outline = param->outline;
219     LV_ASSERT_NULL(outline);
220 
221     lv_freetype_outline_type_t type = param->type;
222     switch(type) {
223         case LV_FREETYPE_OUTLINE_END:
224             lv_nema_gfx_path_end(outline);
225             break;
226         case LV_FREETYPE_OUTLINE_MOVE_TO:
227             lv_nema_gfx_path_move_to(outline, param->to.x, param->to.y);
228             break;
229         case LV_FREETYPE_OUTLINE_LINE_TO:
230             lv_nema_gfx_path_line_to(outline, param->to.x, param->to.y);
231             break;
232         case LV_FREETYPE_OUTLINE_CUBIC_TO:
233             lv_nema_gfx_path_cubic_to(outline, param->control1.x, param->control1.y,
234                                       param->control2.x, param->control2.y,
235                                       param->to.x, param->to.y);
236             break;
237         case LV_FREETYPE_OUTLINE_CONIC_TO:
238             lv_nema_gfx_path_quad_to(outline, param->control1.x, param->control1.y,
239                                      param->to.x, param->to.y);
240             break;
241         default:
242             LV_LOG_ERROR("unknown point type: %d", type);
243             LV_ASSERT(false);
244             break;
245     }
246     LV_PROFILER_DRAW_END;
247 }
248 
lv_nema_outline_event_alloc(const lv_freetype_outline_event_param_t * param)249 static void lv_nema_outline_event_alloc(const lv_freetype_outline_event_param_t * param)
250 {
251     lv_nema_gfx_path_t * outline = param->outline;
252     outline->data_size = param->sizes.data_size;
253     outline->seg_size = param->sizes.segments_size;
254     lv_nema_gfx_path_alloc(outline);
255 }
256 
257 #endif /* LV_USE_FREETYPE && LV_USE_NEMA_VG */
258 
259 /**
260  * Convert a hexadecimal characters to a number (0..15)
261  * @param hex Pointer to a hexadecimal character (0..9, A..F)
262  * @return the numerical value of `hex` or 0 on error
263  */
hex_char_to_num(char hex)264 static uint8_t hex_char_to_num(char hex)
265 {
266     if(hex >= '0' && hex <= '9') return hex - '0';
267     if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
268     return 'A' <= hex && hex <= 'F' ? hex - 'A' + 10 : 0;
269 }
270 
271 
_bpp_nema_gfx_format(lv_draw_glyph_dsc_t * glyph_draw_dsc)272 static inline uint8_t _bpp_nema_gfx_format(lv_draw_glyph_dsc_t * glyph_draw_dsc)
273 {
274     uint32_t format = glyph_draw_dsc->g->format;
275 
276     switch(format) {
277         case LV_FONT_GLYPH_FORMAT_A1:
278         case LV_FONT_GLYPH_FORMAT_A1_ALIGNED:
279             return NEMA_A1;
280         case LV_FONT_GLYPH_FORMAT_A2:
281         case LV_FONT_GLYPH_FORMAT_A2_ALIGNED:
282             return NEMA_A2;
283         case LV_FONT_GLYPH_FORMAT_A4:
284         case LV_FONT_GLYPH_FORMAT_A4_ALIGNED:
285             return NEMA_A4;
286         case LV_FONT_GLYPH_FORMAT_A8:
287         case LV_FONT_GLYPH_FORMAT_A8_ALIGNED:
288         default:
289             return NEMA_A8;
290     }
291 }
292 
_draw_nema_gfx_letter(lv_draw_unit_t * draw_unit,lv_draw_glyph_dsc_t * glyph_draw_dsc,lv_draw_fill_dsc_t * fill_draw_dsc,const lv_area_t * fill_area)293 static void _draw_nema_gfx_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc,
294                                   lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area)
295 {
296     if(glyph_draw_dsc) {
297         if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_NONE) {
298 #if LV_USE_FONT_PLACEHOLDER
299             /* Draw a placeholder rectangle*/
300             lv_draw_border_dsc_t border_draw_dsc;
301             lv_draw_border_dsc_init(&border_draw_dsc);
302             border_draw_dsc.opa = glyph_draw_dsc->opa;
303             border_draw_dsc.color = glyph_draw_dsc->color;
304             border_draw_dsc.width = 1;
305             lv_draw_nema_gfx_border(draw_unit, &border_draw_dsc, glyph_draw_dsc->bg_coords);
306 #endif
307         }
308         else if(glyph_draw_dsc->format >= LV_FONT_GLYPH_FORMAT_A1 &&
309                 glyph_draw_dsc->format <= LV_FONT_GLYPH_FORMAT_A8_ALIGNED) {
310             /*Do not draw transparent things*/
311             if(glyph_draw_dsc->opa <= LV_OPA_MIN)
312                 return;
313 
314             lv_layer_t * layer = draw_unit->target_layer;
315 
316             lv_area_t blend_area;
317             if(!lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, draw_unit->clip_area))
318                 return;
319             lv_area_move(&blend_area, -layer->buf_area.x1, -layer->buf_area.y1);
320 
321             const lv_draw_buf_t * draw_buf = glyph_draw_dsc->glyph_data;
322             const void * mask_buf;
323 
324             if(is_raw_bitmap) {
325                 mask_buf = glyph_draw_dsc->glyph_data;
326             }
327             else {
328                 mask_buf = draw_buf->data;
329             }
330 
331             int32_t x = glyph_draw_dsc->letter_coords->x1 - layer->buf_area.x1;
332             int32_t y = glyph_draw_dsc->letter_coords->y1 - layer->buf_area.y1;
333             int32_t w = glyph_draw_dsc->g->box_w;
334             int32_t h = glyph_draw_dsc->g->box_h;
335 
336             if(glyph_draw_dsc->format <= LV_FONT_GLYPH_FORMAT_A4) {
337                 nema_bind_src_tex((uintptr_t)(mask_buf), w * h, 1, _bpp_nema_gfx_format(glyph_draw_dsc), 0, NEMA_FILTER_PS);
338 
339                 nema_matrix3x3_t m = {
340                     {1,    w,   -x - (y * w) - (0.5 * w)},
341                     {0,    1,                   0},
342                     {0,    0,                   1}
343                 };
344 
345                 nema_set_matrix(m);
346                 nema_raster_rect(x, y, w, h);
347             }
348             else {
349                 nema_bind_src_tex((uintptr_t)(mask_buf), w, h, _bpp_nema_gfx_format(glyph_draw_dsc), -1,
350                                   NEMA_FILTER_PS);
351                 nema_blit(x, y);
352             }
353         }
354         else if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_IMAGE) {
355 #if LV_USE_IMGFONT
356             lv_draw_img_dsc_t img_dsc;
357             lv_draw_img_dsc_init(&img_dsc);
358             img_dsc.angle = 0;
359             img_dsc.zoom = LV_ZOOM_NONE;
360             img_dsc.opa = glyph_draw_dsc->opa;
361             img_dsc.src = glyph_draw_dsc->glyph_data;
362             lv_draw_nema_gfx_img(draw_unit, &img_dsc, glyph_draw_dsc->letter_coords);
363 #endif
364         }
365 
366 #if LV_USE_FREETYPE && LV_USE_NEMA_VG
367         else if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_VECTOR) {
368             if(lv_freetype_is_outline_font(glyph_draw_dsc->g->resolved_font)) {
369                 _draw_nema_gfx_outline(draw_unit, glyph_draw_dsc);
370             }
371         }
372 #endif
373 
374     }
375 
376     if(fill_draw_dsc && fill_area) {
377         lv_draw_nema_gfx_fill(draw_unit, fill_draw_dsc, fill_area);
378     }
379 
380 }
381 
_set_color_blend(uint32_t color,uint8_t alpha)382 static inline void _set_color_blend(uint32_t color, uint8_t alpha)
383 {
384     nema_set_tex_color(color);
385 
386     if(alpha < 255U) {
387         nema_set_blend_blit(NEMA_BL_SIMPLE | NEMA_BLOP_MODULATE_A);
388         nema_set_const_color(color);
389     }
390     else {
391         nema_set_blend_blit(NEMA_BL_SIMPLE);
392     }
393 }
394 
_draw_label_iterate_characters(lv_draw_unit_t * draw_unit,const lv_draw_label_dsc_t * dsc,const lv_area_t * coords)395 static void _draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
396                                            const lv_area_t * coords)
397 {
398     const lv_font_t * font = dsc->font;
399     int32_t w;
400 
401     lv_area_t clipped_area;
402     bool clip_ok = lv_area_intersect(&clipped_area, coords, draw_unit->clip_area);
403     if(!clip_ok) return;
404 
405     lv_text_align_t align = dsc->align;
406     lv_base_dir_t base_dir = dsc->bidi_dir;
407 
408     lv_bidi_calculate_align(&align, &base_dir, dsc->text);
409 
410     if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
411         /*Normally use the label's width as width*/
412         w = lv_area_get_width(coords);
413     }
414     else {
415         /*If EXPAND is enabled then not limit the text's width to the object's width*/
416         lv_point_t p;
417         lv_text_get_size(&p, dsc->text, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
418                          dsc->flag);
419         w = p.x;
420     }
421 
422     int32_t line_height_font = lv_font_get_line_height(font);
423     int32_t line_height = line_height_font + dsc->line_space;
424 
425     /*Init variables for the first line*/
426     int32_t line_width = 0;
427     lv_point_t pos;
428     lv_point_set(&pos, coords->x1, coords->y1);
429 
430     int32_t x_ofs = 0;
431     int32_t y_ofs = 0;
432     x_ofs = dsc->ofs_x;
433     y_ofs = dsc->ofs_y;
434     pos.y += y_ofs;
435 
436     uint32_t line_start     = 0;
437     int32_t last_line_start = -1;
438 
439     /*Check the hint to use the cached info*/
440     if(dsc->hint && y_ofs == 0 && coords->y1 < 0) {
441         /*If the label changed too much recalculate the hint.*/
442         if(LV_ABS(dsc->hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
443             dsc->hint->line_start = -1;
444         }
445         last_line_start = dsc->hint->line_start;
446     }
447 
448     /*Use the hint if it's valid*/
449     if(dsc->hint && last_line_start >= 0) {
450         line_start = last_line_start;
451         pos.y += dsc->hint->y;
452     }
453 
454     uint32_t remaining_len = dsc->text_length;
455 
456     uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space,
457                                                            w, NULL, dsc->flag);
458 
459     /*Go the first visible line*/
460     while(pos.y + line_height_font < draw_unit->clip_area->y1) {
461         /*Go to next line*/
462         line_start = line_end;
463         line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
464         pos.y += line_height;
465 
466         /*Save at the threshold coordinate*/
467         if(dsc->hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && dsc->hint->line_start < 0) {
468             dsc->hint->line_start = line_start;
469             dsc->hint->y          = pos.y - coords->y1;
470             dsc->hint->coord_y    = coords->y1;
471         }
472 
473         if(dsc->text[line_start] == '\0') return;
474     }
475 
476     /*Align to middle*/
477     if(align == LV_TEXT_ALIGN_CENTER) {
478         line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
479                                                   dsc->flag);
480 
481         pos.x += (lv_area_get_width(coords) - line_width) / 2;
482 
483     }
484     /*Align to the right*/
485     else if(align == LV_TEXT_ALIGN_RIGHT) {
486         line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
487                                                   dsc->flag);
488         pos.x += lv_area_get_width(coords) - line_width;
489     }
490 
491     uint32_t sel_start = dsc->sel_start;
492     uint32_t sel_end = dsc->sel_end;
493     if(sel_start > sel_end) {
494         uint32_t tmp = sel_start;
495         sel_start = sel_end;
496         sel_end = tmp;
497     }
498 
499     lv_area_t bg_coords;
500     lv_draw_glyph_dsc_t draw_letter_dsc;
501     lv_draw_glyph_dsc_init(&draw_letter_dsc);
502     draw_letter_dsc.opa = dsc->opa;
503     draw_letter_dsc.bg_coords = &bg_coords;
504     draw_letter_dsc.color = dsc->color;
505 
506     lv_draw_fill_dsc_t fill_dsc;
507     lv_draw_fill_dsc_init(&fill_dsc);
508     fill_dsc.opa = dsc->opa;
509     int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
510     int32_t line_start_x;
511     uint32_t next_char_offset;
512     uint32_t recolor_command_start_index = 0;
513     int32_t letter_w;
514     cmd_state_t recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
515     lv_color_t recolor = lv_color_black(); /* Holds the selected color inside the recolor command */
516     uint8_t is_first_space_after_cmd = 0;
517 
518     lv_color32_t dsc_col32 = lv_color_to_32(dsc->color, dsc->opa);
519     uint32_t nema_dsc_color = nema_rgba(dsc_col32.red, dsc_col32.green, dsc_col32.blue, dsc_col32.alpha);
520     lv_color32_t dsc_sel_col32 = lv_color_to_32(dsc->sel_color, dsc->opa);
521     uint32_t nema_dsc_sel_color = nema_rgba(dsc_sel_col32.red, dsc_sel_col32.green, dsc_sel_col32.blue,
522                                             dsc_sel_col32.alpha);
523     uint32_t blend_color;
524     uint8_t blend_alpha = 255;
525 
526     _set_color_blend(nema_dsc_color, dsc_col32.alpha);
527 
528     uint8_t cur_state = 2;
529     uint8_t prev_state = 2;
530 
531     /*Write out all lines*/
532     while(remaining_len && dsc->text[line_start] != '\0') {
533         pos.x += x_ofs;
534         line_start_x = pos.x;
535 
536         /*Write all letter of a line*/
537         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
538         next_char_offset = 0;
539 #if LV_USE_BIDI
540         char * bidi_txt = lv_malloc(line_end - line_start + 1);
541         LV_ASSERT_MALLOC(bidi_txt);
542         lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
543 #else
544         const char * bidi_txt = dsc->text + line_start;
545 #endif
546 
547         while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) {
548             uint32_t logical_char_pos = 0;
549 
550             /* Check if the text selection is enabled */
551             if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
552 #if LV_USE_BIDI
553                 logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
554                 uint32_t t = lv_text_encoded_get_char_id(bidi_txt, next_char_offset);
555                 logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
556 #else
557                 logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
558 #endif
559             }
560 
561             uint32_t letter;
562             uint32_t letter_next;
563             lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &next_char_offset);
564 
565             /* If recolor is enabled */
566             if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
567 
568                 if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
569                     /* Handle the recolor command marker depending of the current recolor state */
570 
571                     if(recolor_cmd_state == RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER) {
572                         recolor_command_start_index = next_char_offset;
573                         recolor_cmd_state = RECOLOR_CMD_STATE_PARAMETER;
574                         continue;
575                     }
576                     /*Other start char in parameter escaped cmd. char*/
577                     else if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
578                         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
579                     }
580                     /* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */
581                     else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
582                         recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
583                         continue;
584                     }
585                 }
586 
587                 /* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */
588                 if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) {
589                     is_first_space_after_cmd = 1;
590                 }
591                 else {
592                     is_first_space_after_cmd = 0;
593                 }
594 
595                 /* Skip the color parameter and wait the space after it
596                  * Once we have reach the space ' ', then we will extract the color information
597                  * and store it into the recolor variable */
598                 if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
599                     /* Not an space? Continue with the next character */
600                     if(letter != ' ') {
601                         continue;
602                     }
603 
604                     /*Get the recolor parameter*/
605                     if((next_char_offset - recolor_command_start_index) == LABEL_RECOLOR_PAR_LENGTH + 1) {
606                         /* Temporary buffer to hold the recolor information */
607                         char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
608                         lv_memcpy(buf, &bidi_txt[recolor_command_start_index], LABEL_RECOLOR_PAR_LENGTH);
609                         buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
610 
611                         uint8_t r, g, b;
612                         r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
613                         g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
614                         b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
615 
616                         recolor = lv_color_make(r, g, b);
617                     }
618                     else {
619                         recolor.red = dsc->color.red;
620                         recolor.blue = dsc->color.blue;
621                         recolor.green = dsc->color.green;
622                     }
623 
624                     /*After the parameter the text is in the command*/
625                     recolor_cmd_state = RECOLOR_CMD_STATE_TEXT_INPUT;
626                 }
627 
628                 /* Don't draw the first space after the recolor command */
629                 if(is_first_space_after_cmd) {
630                     continue;
631                 }
632             }
633 
634             /* If we're in the CMD_STATE_IN state then we need to subtract the recolor command length */
635             if(((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) && (recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT)) {
636                 logical_char_pos -= (LABEL_RECOLOR_PAR_LENGTH + 1);
637             }
638 
639             letter_w = lv_font_get_glyph_width(font, letter, letter_next);
640 
641             /*Always set the bg_coordinates for placeholder drawing*/
642             bg_coords.x1 = pos.x;
643             bg_coords.y1 = pos.y;
644             bg_coords.x2 = pos.x + letter_w - 1;
645             bg_coords.y2 = pos.y + line_height - 1;
646 
647             if(next_char_offset >= line_end - line_start) {
648                 if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
649                     lv_area_t fill_area;
650                     fill_area.x1 = line_start_x;
651                     fill_area.x2 = pos.x + letter_w - 1;
652                     fill_area.y1 = pos.y + font->line_height - font->base_line - font->underline_position;
653                     fill_area.y2 = fill_area.y1 + underline_width - 1;
654 
655                     fill_dsc.color = dsc->color;
656                     lv_draw_nema_gfx_fill(draw_unit, &fill_dsc, &fill_area);
657                 }
658                 if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
659                     lv_area_t fill_area;
660                     fill_area.x1 = line_start_x;
661                     fill_area.x2 = pos.x + letter_w - 1;
662                     fill_area.y1 = pos.y + (font->line_height - font->base_line) * 2 / 3 + font->underline_thickness / 2;
663                     fill_area.y2 = fill_area.y1 + underline_width - 1;
664 
665                     fill_dsc.color = dsc->color;
666                     lv_draw_nema_gfx_fill(draw_unit, &fill_dsc, &fill_area);
667                 }
668             }
669 
670             /* Handle text selection */
671             if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL
672                && logical_char_pos >= sel_start && logical_char_pos < sel_end) {
673                 draw_letter_dsc.color = dsc->sel_color;
674                 fill_dsc.color = dsc->sel_bg_color;
675                 lv_draw_nema_gfx_fill(draw_unit, &fill_dsc, &bg_coords);
676                 cur_state = 0 ;
677                 blend_alpha = dsc_sel_col32.alpha;
678                 blend_color = nema_dsc_sel_color;
679             }
680             else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
681                 draw_letter_dsc.color = recolor;
682                 cur_state = 1 ;
683                 blend_alpha = dsc_col32.alpha;
684                 lv_color32_t dsc_recolor_col32 = lv_color_to_32(recolor, dsc->opa);
685                 blend_color = nema_rgba(dsc_recolor_col32.red, dsc_recolor_col32.green, dsc_recolor_col32.blue,
686                                         dsc_recolor_col32.alpha);
687             }
688             else {
689                 draw_letter_dsc.color = dsc->color;
690                 cur_state = 2;
691                 blend_alpha = dsc_col32.alpha;
692                 blend_color = nema_dsc_color;
693             }
694 
695             if(cur_state != prev_state) {
696                 _set_color_blend(blend_color, blend_alpha);
697                 prev_state = cur_state;
698             }
699 
700             _draw_letter(draw_unit, &draw_letter_dsc, &pos, font, letter);
701 
702             if(letter_w > 0) {
703                 pos.x += letter_w + dsc->letter_space;
704             }
705         }
706 
707 #if LV_USE_BIDI
708         lv_free(bidi_txt);
709         bidi_txt = NULL;
710 #endif
711         /*Go to next line*/
712         remaining_len -= line_end - line_start;
713         line_start = line_end;
714         if(remaining_len) {
715             line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
716         }
717 
718         pos.x = coords->x1;
719         /*Align to middle*/
720         if(align == LV_TEXT_ALIGN_CENTER) {
721             line_width =
722                 lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
723 
724             pos.x += (lv_area_get_width(coords) - line_width) / 2;
725         }
726         /*Align to the right*/
727         else if(align == LV_TEXT_ALIGN_RIGHT) {
728             line_width =
729                 lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
730             pos.x += lv_area_get_width(coords) - line_width;
731         }
732 
733         /*Go the next line position*/
734         pos.y += line_height;
735 
736         if(pos.y > draw_unit->clip_area->y2) break;
737     }
738 
739     if(draw_letter_dsc._draw_buf) lv_draw_buf_destroy(draw_letter_dsc._draw_buf);
740 
741     LV_ASSERT_MEM_INTEGRITY();
742 }
743 
_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)744 static void _draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc,  const lv_point_t * pos,
745                          const lv_font_t * font, uint32_t letter)
746 {
747     lv_font_glyph_dsc_t g;
748 
749     if(lv_text_is_marker(letter)) /*Markers are valid letters but should not be rendered.*/
750         return;
751 
752     LV_PROFILER_DRAW_BEGIN;
753     bool g_ret = lv_font_get_glyph_dsc(font, &g, letter, '\0');
754     if(g_ret == false) {
755         /*Add warning if the dsc is not found*/
756         LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%" LV_PRIX32, letter);
757     }
758 
759     /*Don't draw anything if the character is empty. E.g. space*/
760     if((g.box_h == 0) || (g.box_w == 0)) {
761         LV_PROFILER_DRAW_END;
762         return;
763     }
764 
765     lv_area_t letter_coords;
766     letter_coords.x1 = pos->x + g.ofs_x;
767     letter_coords.x2 = letter_coords.x1 + g.box_w - 1;
768     letter_coords.y1 = pos->y + (font->line_height - font->base_line) - g.box_h - g.ofs_y;
769     letter_coords.y2 = letter_coords.y1 + g.box_h - 1;
770 
771     /*If the letter is completely out of mask don't draw it*/
772     if(lv_area_is_out(&letter_coords, draw_unit->clip_area, 0) &&
773        lv_area_is_out(dsc->bg_coords, draw_unit->clip_area, 0)) {
774         LV_PROFILER_DRAW_END;
775         return;
776     }
777 
778     if(g.resolved_font) {
779         lv_draw_buf_t * draw_buf = NULL;
780         if(LV_FONT_GLYPH_FORMAT_NONE < g.format && g.format < LV_FONT_GLYPH_FORMAT_IMAGE) {
781             /*Only check draw buf for bitmap glyph*/
782             draw_buf = lv_draw_buf_reshape(dsc->_draw_buf, 0, g.box_w, g.box_h, LV_STRIDE_AUTO);
783             if(draw_buf == NULL) {
784                 if(dsc->_draw_buf) lv_draw_buf_destroy(dsc->_draw_buf);
785 
786                 uint32_t h = g.box_h;
787                 if(h * g.box_w < 64) h *= 2; /*Alloc a slightly larger buffer*/
788                 draw_buf = lv_draw_buf_create_ex(font_draw_buf_handlers, g.box_w, h, LV_COLOR_FORMAT_A8, LV_STRIDE_AUTO);
789                 LV_ASSERT_MALLOC(draw_buf);
790                 draw_buf->header.h = g.box_h;
791                 dsc->_draw_buf = draw_buf;
792             }
793         }
794 
795         /* Performance Optimization for lv_font_fmt_txt_dsc_t fonts, always request raw bitmaps */
796         g.req_raw_bitmap = 1;
797         is_raw_bitmap = false;
798         if(font->get_glyph_bitmap == lv_font_get_bitmap_fmt_txt) {
799             lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
800             if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
801                 is_raw_bitmap = true;
802             }
803         }
804 
805         dsc->glyph_data = (void *)lv_font_get_glyph_bitmap(&g, draw_buf);
806 
807         dsc->format = dsc->glyph_data ? g.format : LV_FONT_GLYPH_FORMAT_NONE;
808     }
809     else {
810         dsc->format = LV_FONT_GLYPH_FORMAT_NONE;
811     }
812 
813     dsc->letter_coords = &letter_coords;
814     dsc->g = &g;
815     _draw_nema_gfx_letter(draw_unit, dsc, NULL, NULL);
816 
817     lv_font_glyph_release_draw_data(&g);
818 
819     LV_PROFILER_DRAW_END;
820 }
821 
822 #endif /*LV_USE_NEMA_GFX*/