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*/