1 /**
2  * @file lv_draw_label.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_label.h"
10 #include "../lv_misc/lv_math.h"
11 #include "../lv_hal/lv_hal_disp.h"
12 #include "../lv_core/lv_refr.h"
13 #include "../lv_misc/lv_bidi.h"
14 #include "../lv_misc/lv_debug.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define LABEL_RECOLOR_PAR_LENGTH 6
20 #define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 enum {
26     CMD_STATE_WAIT,
27     CMD_STATE_PAR,
28     CMD_STATE_IN,
29 };
30 typedef uint8_t cmd_state_t;
31 
32 /**********************
33  *  STATIC PROTOTYPES
34  **********************/
35 LV_ATTRIBUTE_FAST_MEM static void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
36                                                  const lv_font_t * font_p,
37                                                  uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
38 LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
39                                                      const lv_area_t * clip_area,
40                                                      const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
41 static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
42                               const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
43 
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 const uint8_t _lv_bpp1_opa_table[2]  = {0, 255};          /*Opacity mapping with bpp = 1 (Just for compatibility)*/
56 const uint8_t _lv_bpp2_opa_table[4]  = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
57 
58 const uint8_t _lv_bpp3_opa_table[8]  = {0, 36,  73, 109,   /*Opacity mapping with bpp = 3*/
59                                         146, 182,  219, 255
60                                        };
61 
62 const uint8_t _lv_bpp4_opa_table[16] = {0,  17, 34,  51,  /*Opacity mapping with bpp = 4*/
63                                         68, 85, 102, 119,
64                                         136, 153, 170, 187,
65                                         204, 221, 238, 255
66                                        };
67 const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
68                                          16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
69                                          32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
70                                          48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
71                                          64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
72                                          80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
73                                          96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
74                                          112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
75                                          128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
76                                          144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
77                                          160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
78                                          176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
79                                          192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
80                                          208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
81                                          224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
82                                          240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
83                                         };
84 
85 /**********************
86  *      MACROS
87  **********************/
88 
89 /**********************
90  *   GLOBAL FUNCTIONS
91  **********************/
92 
lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)93 LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
94 {
95     _lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
96     dsc->opa = LV_OPA_COVER;
97     dsc->color = LV_COLOR_BLACK;
98     dsc->font = LV_THEME_DEFAULT_FONT_NORMAL;
99     dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
100     dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
101     dsc->sel_color = LV_COLOR_BLUE;
102     dsc->bidi_dir = LV_BIDI_DIR_LTR;
103 }
104 
105 /**
106  * Write a text
107  * @param coords coordinates of the label
108  * @param mask the label will be drawn only in this area
109  * @param dsc pointer to draw descriptor
110  * @param txt `\0` terminated text to write
111  * @param hint pointer to a `lv_draw_label_hint_t` variable.
112  * It is managed by the drawer to speed up the drawing of very long texts (thousands of lines).
113  */
lv_draw_label(const lv_area_t * coords,const lv_area_t * mask,const lv_draw_label_dsc_t * dsc,const char * txt,lv_draw_label_hint_t * hint)114 LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask,
115                                          const lv_draw_label_dsc_t * dsc,
116                                          const char * txt,
117                                          lv_draw_label_hint_t * hint)
118 {
119 
120     if(dsc->opa <= LV_OPA_MIN) return;
121     const lv_font_t * font = dsc->font;
122     int32_t w;
123 
124     /*No need to waste processor time if string is empty*/
125     if(txt[0] == '\0')  return;
126 
127     lv_area_t clipped_area;
128     bool clip_ok = _lv_area_intersect(&clipped_area, coords, mask);
129     if(!clip_ok) return;
130 
131 
132     if((dsc->flag & LV_TXT_FLAG_EXPAND) == 0) {
133         /*Normally use the label's width as width*/
134         w = lv_area_get_width(coords);
135     }
136     else {
137         /*If EXAPND is enabled then not limit the text's width to the object's width*/
138         lv_point_t p;
139         _lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
140                          dsc->flag);
141         w = p.x;
142     }
143 
144     int32_t line_height_font = lv_font_get_line_height(font);
145     int32_t line_height = line_height_font + dsc->line_space;
146 
147     /*Init variables for the first line*/
148     int32_t line_width = 0;
149     lv_point_t pos;
150     pos.x = coords->x1;
151     pos.y = coords->y1;
152 
153     int32_t x_ofs = 0;
154     int32_t y_ofs = 0;
155     x_ofs = dsc->ofs_x;
156     y_ofs = dsc->ofs_y;
157     pos.y += y_ofs;
158 
159     uint32_t line_start     = 0;
160     int32_t last_line_start = -1;
161 
162     /*Check the hint to use the cached info*/
163     if(hint && y_ofs == 0 && coords->y1 < 0) {
164         /*If the label changed too much recalculate the hint.*/
165         if(LV_MATH_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
166             hint->line_start = -1;
167         }
168         last_line_start = hint->line_start;
169     }
170 
171     /*Use the hint if it's valid*/
172     if(hint && last_line_start >= 0) {
173         line_start = last_line_start;
174         pos.y += hint->y;
175     }
176 
177     uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
178 
179     /*Go the first visible line*/
180     while(pos.y + line_height_font < mask->y1) {
181         /*Go to next line*/
182         line_start = line_end;
183         line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
184         pos.y += line_height;
185 
186         /*Save at the threshold coordinate*/
187         if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
188             hint->line_start = line_start;
189             hint->y          = pos.y - coords->y1;
190             hint->coord_y    = coords->y1;
191         }
192 
193         if(txt[line_start] == '\0') return;
194     }
195 
196     /*Align to middle*/
197     if(dsc->flag & LV_TXT_FLAG_CENTER) {
198         line_width = _lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
199 
200         pos.x += (lv_area_get_width(coords) - line_width) / 2;
201 
202     }
203     /*Align to the right*/
204     else if(dsc->flag & LV_TXT_FLAG_RIGHT) {
205         line_width = _lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
206         pos.x += lv_area_get_width(coords) - line_width;
207     }
208 
209     lv_opa_t opa = dsc->opa;
210 
211     uint32_t sel_start = dsc->sel_start;
212     uint32_t sel_end = dsc->sel_end;
213     if(sel_start > sel_end) {
214         uint32_t tmp = sel_start;
215         sel_start = sel_end;
216         sel_end = tmp;
217     }
218     lv_draw_line_dsc_t line_dsc;
219 
220     if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
221         lv_draw_line_dsc_init(&line_dsc);
222         line_dsc.color = dsc->color;
223         line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
224         line_dsc.opa = dsc->opa;
225         line_dsc.blend_mode = dsc->blend_mode;
226     }
227 
228     cmd_state_t cmd_state = CMD_STATE_WAIT;
229     uint32_t i;
230     uint32_t par_start = 0;
231     lv_color_t recolor;
232     int32_t letter_w;
233 
234 
235     lv_draw_rect_dsc_t draw_dsc_sel;
236     lv_draw_rect_dsc_init(&draw_dsc_sel);
237     draw_dsc_sel.bg_color = dsc->sel_color;
238 
239     int32_t pos_x_start = pos.x;
240     /*Write out all lines*/
241     while(txt[line_start] != '\0') {
242         pos.x += x_ofs;
243 
244         /*Write all letter of a line*/
245         cmd_state = CMD_STATE_WAIT;
246         i         = 0;
247 #if LV_USE_BIDI
248         char * bidi_txt = _lv_mem_buf_get(line_end - line_start + 1);
249         _lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, dsc->bidi_dir, NULL, 0);
250 #else
251         const char * bidi_txt = txt + line_start;
252 #endif
253 
254         while(i < line_end - line_start) {
255             uint32_t logical_char_pos = 0;
256             if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
257 #if LV_USE_BIDI
258                 logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
259                 uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
260                 logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, dsc->bidi_dir, t, NULL);
261 #else
262                 logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
263 #endif
264             }
265 
266             uint32_t letter      = _lv_txt_encoded_next(bidi_txt, &i);
267             uint32_t letter_next = _lv_txt_encoded_next(&bidi_txt[i], NULL);
268 
269             /*Handle the re-color command*/
270             if((dsc->flag & LV_TXT_FLAG_RECOLOR) != 0) {
271                 if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
272                     if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
273                         par_start = i;
274                         cmd_state = CMD_STATE_PAR;
275                         continue;
276                     }
277                     else if(cmd_state == CMD_STATE_PAR) {   /*Other start char in parameter escaped cmd. char */
278                         cmd_state = CMD_STATE_WAIT;
279                     }
280                     else if(cmd_state == CMD_STATE_IN) {   /*Command end */
281                         cmd_state = CMD_STATE_WAIT;
282                         continue;
283                     }
284                 }
285 
286                 /*Skip the color parameter and wait the space after it*/
287                 if(cmd_state == CMD_STATE_PAR) {
288                     if(letter == ' ') {
289                         /*Get the parameter*/
290                         if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
291                             char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
292                             _lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
293                             buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
294                             int r, g, b;
295                             r       = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
296                             g       = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
297                             b       = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
298                             recolor = lv_color_make(r, g, b);
299                         }
300                         else {
301                             recolor.full = dsc->color.full;
302                         }
303                         cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
304                     }
305                     continue;
306                 }
307             }
308 
309             lv_color_t color = dsc->color;
310 
311             if(cmd_state == CMD_STATE_IN) color = recolor;
312 
313             letter_w = lv_font_get_glyph_width(font, letter, letter_next);
314 
315             if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
316                 if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
317                     lv_area_t sel_coords;
318                     sel_coords.x1 = pos.x;
319                     sel_coords.y1 = pos.y;
320                     sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
321                     sel_coords.y2 = pos.y + line_height - 1;
322                     lv_draw_rect(&sel_coords, mask, &draw_dsc_sel);
323                 }
324             }
325 
326             lv_draw_letter(&pos, mask, font, letter, color, opa, dsc->blend_mode);
327 
328             if(letter_w > 0) {
329                 pos.x += letter_w + dsc->letter_space;
330             }
331         }
332 
333         if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
334             lv_point_t p1;
335             lv_point_t p2;
336             p1.x = pos_x_start;
337             p1.y = pos.y + (dsc->font->line_height / 2)  + line_dsc.width / 2;
338             p2.x = pos.x;
339             p2.y = p1.y;
340             lv_draw_line(&p1, &p2, mask, &line_dsc);
341         }
342 
343         if(dsc->decor  & LV_TEXT_DECOR_UNDERLINE) {
344             lv_point_t p1;
345             lv_point_t p2;
346             p1.x = pos_x_start;
347             p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
348             p2.x = pos.x;
349             p2.y = p1.y;
350             lv_draw_line(&p1, &p2, mask, &line_dsc);
351         }
352 
353 #if LV_USE_BIDI
354         _lv_mem_buf_release(bidi_txt);
355         bidi_txt = NULL;
356 #endif
357         /*Go to next line*/
358         line_start = line_end;
359         line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
360 
361         pos.x = coords->x1;
362         /*Align to middle*/
363         if(dsc->flag & LV_TXT_FLAG_CENTER) {
364             line_width =
365                 _lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
366 
367             pos.x += (lv_area_get_width(coords) - line_width) / 2;
368 
369         }
370         /*Align to the right*/
371         else if(dsc->flag & LV_TXT_FLAG_RIGHT) {
372             line_width =
373                 _lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
374             pos.x += lv_area_get_width(coords) - line_width;
375         }
376 
377         /*Go the next line position*/
378         pos.y += line_height;
379 
380         if(pos.y > mask->y2) return;
381     }
382 
383     LV_ASSERT_MEM_INTEGRITY();
384 }
385 
386 /**********************
387  *   STATIC FUNCTIONS
388  **********************/
389 
390 
391 /**
392  * Draw a letter in the Virtual Display Buffer
393  * @param pos_p left-top coordinate of the latter
394  * @param mask_p the letter will be drawn only on this area  (truncated to VDB area)
395  * @param font_p pointer to font
396  * @param letter a letter to draw
397  * @param color color of letter
398  * @param opa opacity of letter (0..255)
399  */
lv_draw_letter(const lv_point_t * pos_p,const lv_area_t * clip_area,const lv_font_t * font_p,uint32_t letter,lv_color_t color,lv_opa_t opa,lv_blend_mode_t blend_mode)400 LV_ATTRIBUTE_FAST_MEM static void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
401                                                  const lv_font_t * font_p,
402                                                  uint32_t letter,
403                                                  lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
404 {
405     if(opa < LV_OPA_MIN) return;
406     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
407 
408     if(font_p == NULL) {
409         LV_LOG_WARN("lv_draw_letter: font is NULL");
410         return;
411     }
412 
413     lv_font_glyph_dsc_t g;
414     bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
415     if(g_ret == false)  {
416         /* Add waring if the dsc is not found
417          * but do not print warning for non printable ASCII chars (e.g. '\n')*/
418         if(letter >= 0x20) {
419             LV_LOG_WARN("lv_draw_letter: glyph dsc. not found");
420         }
421         return;
422     }
423 
424     /* Don't draw anything if the character is empty. E.g. space */
425     if((g.box_h == 0) || (g.box_w == 0)) return;
426 
427     int32_t pos_x = pos_p->x + g.ofs_x;
428     int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
429 
430     /*If the letter is completely out of mask don't draw it */
431     if(pos_x + g.box_w < clip_area->x1 ||
432        pos_x > clip_area->x2 ||
433        pos_y + g.box_h < clip_area->y1 ||
434        pos_y > clip_area->y2)  {
435         return;
436     }
437 
438 
439     const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
440     if(map_p == NULL) {
441         LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
442         return;
443     }
444 
445     if(font_p->subpx) {
446         draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
447     }
448     else {
449         draw_letter_normal(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
450     }
451 }
452 
453 
draw_letter_normal(lv_coord_t pos_x,lv_coord_t pos_y,lv_font_glyph_dsc_t * g,const lv_area_t * clip_area,const uint8_t * map_p,lv_color_t color,lv_opa_t opa,lv_blend_mode_t blend_mode)454 LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
455                                                      const lv_area_t * clip_area,
456                                                      const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
457 {
458     const uint8_t * bpp_opa_table_p;
459     uint32_t bitmask_init;
460     uint32_t bitmask;
461     uint32_t bpp = g->bpp;
462     uint32_t shades;
463     if(bpp == 3) bpp = 4;
464 
465     switch(bpp) {
466         case 1:
467             bpp_opa_table_p = _lv_bpp1_opa_table;
468             bitmask_init  = 0x80;
469             shades = 2;
470             break;
471         case 2:
472             bpp_opa_table_p = _lv_bpp2_opa_table;
473             bitmask_init  = 0xC0;
474             shades = 4;
475             break;
476         case 4:
477             bpp_opa_table_p = _lv_bpp4_opa_table;
478             bitmask_init  = 0xF0;
479             shades = 16;
480             break;
481         case 8:
482             bpp_opa_table_p = _lv_bpp8_opa_table;
483             bitmask_init  = 0xFF;
484             shades = 256;
485             break;       /*No opa table, pixel value will be used directly*/
486         default:
487             LV_LOG_WARN("lv_draw_letter: invalid bpp");
488             return; /*Invalid bpp. Can't render the letter*/
489     }
490 
491     static lv_opa_t opa_table[256];
492     static lv_opa_t prev_opa = LV_OPA_TRANSP;
493     static uint32_t prev_bpp = 0;
494     if(opa < LV_OPA_MAX) {
495         if(prev_opa != opa || prev_bpp != bpp) {
496             uint32_t i;
497             for(i = 0; i < shades; i++) {
498                 opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8);
499             }
500         }
501         bpp_opa_table_p = opa_table;
502         prev_opa = opa;
503         prev_bpp = bpp;
504     }
505 
506     int32_t col, row;
507     int32_t box_w = g->box_w;
508     int32_t box_h = g->box_h;
509     int32_t width_bit = box_w * bpp; /*Letter width in bits*/
510 
511     /* Calculate the col/row start/end on the map*/
512     int32_t col_start = pos_x >= clip_area->x1 ? 0 : clip_area->x1 - pos_x;
513     int32_t col_end   = pos_x + box_w <= clip_area->x2 ? box_w : clip_area->x2 - pos_x + 1;
514     int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
515     int32_t row_end   = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
516 
517     /*Move on the map too*/
518     uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
519     map_p += bit_ofs >> 3;
520 
521     uint8_t letter_px;
522     uint32_t col_bit;
523     col_bit = bit_ofs & 0x7; /* "& 0x7" equals to "% 8" just faster */
524 
525     uint32_t mask_buf_size = box_w * box_h > LV_HOR_RES_MAX ? LV_HOR_RES_MAX : box_w * box_h;
526     lv_opa_t * mask_buf = _lv_mem_buf_get(mask_buf_size);
527     int32_t mask_p = 0;
528 
529     lv_area_t fill_area;
530     fill_area.x1 = col_start + pos_x;
531     fill_area.x2 = col_end  + pos_x - 1;
532     fill_area.y1 = row_start + pos_y;
533     fill_area.y2 = fill_area.y1;
534 
535     uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
536 
537     uint32_t col_bit_max = 8 - bpp;
538     uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp;
539 
540     for(row = row_start ; row < row_end; row++) {
541         int32_t mask_p_start = mask_p;
542 
543         bitmask = bitmask_init >> col_bit;
544         for(col = col_start; col < col_end; col++) {
545             /*Load the pixel's opacity into the mask*/
546             letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit);
547             if(letter_px) {
548                 mask_buf[mask_p] = bpp_opa_table_p[letter_px];
549             }
550             else {
551                 mask_buf[mask_p] = 0;
552             }
553 
554             /*Go to the next column*/
555             if(col_bit < col_bit_max) {
556                 col_bit += bpp;
557                 bitmask = bitmask >> bpp;
558             }
559             else {
560                 col_bit = 0;
561                 bitmask = bitmask_init;
562                 map_p++;
563             }
564 
565             /*Next mask byte*/
566             mask_p++;
567         }
568 
569 
570         /*Apply masks if any*/
571         if(other_mask_cnt) {
572             lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2,
573                                                              lv_area_get_width(&fill_area));
574             if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
575                 _lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&fill_area));
576             }
577         }
578 
579         if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) {
580             fill_area.y2 ++;
581         }
582         else {
583             _lv_blend_fill(clip_area, &fill_area,
584                            color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
585                            blend_mode);
586 
587             fill_area.y1 = fill_area.y2 + 1;
588             fill_area.y2 = fill_area.y1;
589             mask_p = 0;
590         }
591 
592         col_bit += col_bit_row_ofs;
593         map_p += (col_bit >> 3);
594         col_bit = col_bit & 0x7;
595     }
596 
597     /*Flush the last part*/
598     if(fill_area.y1 != fill_area.y2) {
599         fill_area.y2--;
600         _lv_blend_fill(clip_area, &fill_area,
601                        color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
602                        blend_mode);
603         mask_p = 0;
604     }
605 
606     _lv_mem_buf_release(mask_buf);
607 }
608 
draw_letter_subpx(lv_coord_t pos_x,lv_coord_t pos_y,lv_font_glyph_dsc_t * g,const lv_area_t * clip_area,const uint8_t * map_p,lv_color_t color,lv_opa_t opa,lv_blend_mode_t blend_mode)609 static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
610                               const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
611 {
612 #if LV_USE_FONT_SUBPX
613     const uint8_t * bpp_opa_table;
614     uint32_t bitmask_init;
615     uint32_t bitmask;
616     uint32_t bpp = g->bpp;
617     if(bpp == 3) bpp = 4;
618 
619     switch(bpp) {
620         case 1:
621             bpp_opa_table = _lv_bpp1_opa_table;
622             bitmask_init  = 0x80;
623             break;
624         case 2:
625             bpp_opa_table = _lv_bpp2_opa_table;
626             bitmask_init  = 0xC0;
627             break;
628         case 4:
629             bpp_opa_table = _lv_bpp4_opa_table;
630             bitmask_init  = 0xF0;
631             break;
632         case 8:
633             bpp_opa_table = _lv_bpp8_opa_table;
634             bitmask_init  = 0xFF;
635             break;       /*No opa table, pixel value will be used directly*/
636         default:
637             LV_LOG_WARN("lv_draw_letter: invalid bpp not found");
638             return; /*Invalid bpp. Can't render the letter*/
639     }
640 
641     int32_t col, row;
642 
643     int32_t box_w = g->box_w;
644     int32_t box_h = g->box_h;
645     int32_t width_bit = box_w * bpp; /*Letter width in bits*/
646 
647 
648     /* Calculate the col/row start/end on the map*/
649     int32_t col_start = pos_x >= clip_area->x1 ? 0 : (clip_area->x1 - pos_x) * 3;
650     int32_t col_end   = pos_x + box_w / 3 <= clip_area->x2 ? box_w : (clip_area->x2 - pos_x + 1) * 3;
651     int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
652     int32_t row_end   = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
653 
654     /*Move on the map too*/
655     int32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
656     map_p += bit_ofs >> 3;
657 
658     uint8_t letter_px;
659     lv_opa_t px_opa;
660     int32_t col_bit;
661     col_bit = bit_ofs & 0x7; /* "& 0x7" equals to "% 8" just faster */
662 
663     int32_t mask_buf_size = box_w * box_h > LV_HOR_RES_MAX ? LV_HOR_RES_MAX : g->box_w * g->box_h;
664     lv_opa_t * mask_buf = _lv_mem_buf_get(mask_buf_size);
665     int32_t mask_p = 0;
666 
667     lv_color_t * color_buf = _lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
668 
669     lv_disp_t * disp    = _lv_refr_get_disp_refreshing();
670     lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
671 
672     int32_t vdb_width     = lv_area_get_width(&vdb->area);
673     lv_color_t * vdb_buf_tmp = vdb->buf_act;
674 
675     /*Set a pointer on VDB to the first pixel of the letter*/
676     vdb_buf_tmp += ((pos_y - vdb->area.y1) * vdb_width) + pos_x - vdb->area.x1;
677 
678     /*If the letter is partially out of mask the move there on VDB*/
679     vdb_buf_tmp += (row_start * vdb_width) + col_start / 3;
680 
681     lv_area_t map_area;
682     map_area.x1 = col_start / 3 + pos_x;
683     map_area.x2 = col_end / 3  + pos_x - 1;
684     map_area.y1 = row_start + pos_y;
685     map_area.y2 = map_area.y1;
686 
687     uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
688 
689     uint8_t font_rgb[3];
690 
691 #if LV_COLOR_16_SWAP == 0
692     uint8_t txt_rgb[3] = {color.ch.red, color.ch.green, color.ch.blue};
693 #else
694     uint8_t txt_rgb[3] = {color.ch.red, (color.ch.green_h << 3) + color.ch.green_l, color.ch.blue};
695 #endif
696 
697     for(row = row_start ; row < row_end; row++) {
698         uint32_t subpx_cnt = 0;
699         bitmask = bitmask_init >> col_bit;
700         int32_t mask_p_start = mask_p;
701 
702         for(col = col_start; col < col_end; col++) {
703             /*Load the pixel's opacity into the mask*/
704             letter_px = (*map_p & bitmask) >> (8 - col_bit - bpp);
705             if(letter_px != 0) {
706                 if(opa == LV_OPA_COVER) {
707                     px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
708                 }
709                 else {
710                     px_opa = bpp == 8 ? (uint32_t)((uint32_t)letter_px * opa) >> 8
711                              : (uint32_t)((uint32_t)bpp_opa_table[letter_px] * opa) >> 8;
712                 }
713             }
714             else {
715                 px_opa = 0;
716             }
717 
718             font_rgb[subpx_cnt] = px_opa;
719 
720             subpx_cnt ++;
721             if(subpx_cnt == 3) {
722                 subpx_cnt = 0;
723 
724                 lv_color_t res_color;
725 #if LV_COLOR_16_SWAP == 0
726                 uint8_t bg_rgb[3] = {vdb_buf_tmp->ch.red, vdb_buf_tmp->ch.green, vdb_buf_tmp->ch.blue};
727 #else
728                 uint8_t bg_rgb[3] = {vdb_buf_tmp->ch.red,
729                                      (vdb_buf_tmp->ch.green_h << 3) + vdb_buf_tmp->ch.green_l,
730                                      vdb_buf_tmp->ch.blue
731                                     };
732 #endif
733 
734 #if LV_FONT_SUBPX_BGR
735                 res_color.ch.blue = (uint32_t)((uint32_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
736                 res_color.ch.red = (uint32_t)((uint32_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
737 #else
738                 res_color.ch.red = (uint32_t)((uint16_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
739                 res_color.ch.blue = (uint32_t)((uint16_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
740 #endif
741 
742 #if LV_COLOR_16_SWAP == 0
743                 res_color.ch.green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
744 #else
745                 uint8_t green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
746                 res_color.ch.green_h = green >> 3;
747                 res_color.ch.green_l = green & 0x7;
748 #endif
749 
750 #if LV_COLOR_DEPTH == 32
751                 res_color.ch.alpha =  0xff;
752 #endif
753 
754                 if(font_rgb[0] == 0 && font_rgb[1] == 0 && font_rgb[2] == 0) mask_buf[mask_p] = LV_OPA_TRANSP;
755                 else mask_buf[mask_p] = LV_OPA_COVER;
756                 color_buf[mask_p] = res_color;
757 
758                 /*Next mask byte*/
759                 mask_p++;
760                 vdb_buf_tmp++;
761             }
762 
763             /*Go to the next column*/
764             if(col_bit < (int32_t)(8 - bpp)) {
765                 col_bit += bpp;
766                 bitmask = bitmask >> bpp;
767             }
768             else {
769                 col_bit = 0;
770                 bitmask = bitmask_init;
771                 map_p++;
772             }
773         }
774 
775         /*Apply masks if any*/
776         if(other_mask_cnt) {
777             lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2,
778                                                              lv_area_get_width(&map_area));
779             if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
780                 _lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&map_area));
781             }
782         }
783 
784         if((int32_t) mask_p + (col_end - col_start) < mask_buf_size) {
785             map_area.y2 ++;
786         }
787         else {
788             _lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
789 
790             map_area.y1 = map_area.y2 + 1;
791             map_area.y2 = map_area.y1;
792             mask_p = 0;
793         }
794 
795         col_bit += ((box_w - col_end) + col_start) * bpp;
796 
797         map_p += (col_bit >> 3);
798         col_bit = col_bit & 0x7;
799 
800         /*Next row in VDB*/
801         vdb_buf_tmp += vdb_width - (col_end - col_start) / 3;
802     }
803 
804     /*Flush the last part*/
805     if(map_area.y1 != map_area.y2) {
806         map_area.y2--;
807         _lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
808     }
809 
810     _lv_mem_buf_release(mask_buf);
811     _lv_mem_buf_release(color_buf);
812 #else
813     LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h");
814 #endif
815 }
816 
817 
818 /**
819  * Convert a hexadecimal characters to a number (0..15)
820  * @param hex Pointer to a hexadecimal character (0..9, A..F)
821  * @return the numerical value of `hex` or 0 on error
822  */
hex_char_to_num(char hex)823 static uint8_t hex_char_to_num(char hex)
824 {
825     uint8_t result = 0;
826 
827     if(hex >= '0' && hex <= '9') {
828         result = hex - '0';
829     }
830     else {
831         if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
832 
833         switch(hex) {
834             case 'A':
835                 result = 10;
836                 break;
837             case 'B':
838                 result = 11;
839                 break;
840             case 'C':
841                 result = 12;
842                 break;
843             case 'D':
844                 result = 13;
845                 break;
846             case 'E':
847                 result = 14;
848                 break;
849             case 'F':
850                 result = 15;
851                 break;
852             default:
853                 result = 0;
854                 break;
855         }
856     }
857 
858     return result;
859 }
860