1 /**
2  * @file lv_draw_vg_lite_label.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../../misc/lv_area_private.h"
11 #include "../../libs/freetype/lv_freetype_private.h"
12 #include "../lv_draw_label_private.h"
13 #include "lv_draw_vg_lite.h"
14 
15 #include "../../lvgl.h"
16 
17 #if LV_USE_DRAW_VG_LITE
18 
19 #include "lv_vg_lite_utils.h"
20 #include "lv_vg_lite_path.h"
21 #include "lv_draw_vg_lite_type.h"
22 #include <float.h>
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 
28 #define PATH_DATA_COORD_FORMAT VG_LITE_S16
29 
30 #if LV_VG_LITE_FLUSH_MAX_COUNT > 0
31     #define PATH_FLUSH_COUNT_MAX 0
32 #else
33     /* When using IDLE Flush mode, reduce the number of flushes */
34     #define PATH_FLUSH_COUNT_MAX 8
35 #endif
36 
37 #define FT_F26DOT6_SHIFT 6
38 
39 /** After converting the font reference size, it is also necessary to scale the 26dot6 data
40  * in the path to the real physical size
41  */
42 #define FT_F26DOT6_TO_PATH_SCALE(x) (LV_FREETYPE_F26DOT6_TO_FLOAT(x) / (1 << FT_F26DOT6_SHIFT))
43 
44 /**********************
45  *      TYPEDEFS
46  **********************/
47 
48 /**********************
49  *  STATIC PROTOTYPES
50  **********************/
51 
52 static void draw_letter_cb(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc,
53                            lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area);
54 
55 static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc);
56 
57 #if LV_USE_FREETYPE
58     static void freetype_outline_event_cb(lv_event_t * e);
59     static void draw_letter_outline(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc);
60 #endif /* LV_USE_FREETYPE */
61 
62 /**********************
63  *  STATIC VARIABLES
64  **********************/
65 
66 /**********************
67  *      MACROS
68  **********************/
69 
70 /**********************
71  *   GLOBAL FUNCTIONS
72  **********************/
73 
lv_draw_vg_lite_label_init(lv_draw_unit_t * draw_unit)74 void lv_draw_vg_lite_label_init(lv_draw_unit_t * draw_unit)
75 {
76 #if LV_USE_FREETYPE
77     /*Set up the freetype outline event*/
78     lv_freetype_outline_add_event(freetype_outline_event_cb, LV_EVENT_ALL, draw_unit);
79 #else
80     LV_UNUSED(draw_unit);
81 #endif /* LV_USE_FREETYPE */
82 }
83 
lv_draw_vg_lite_letter(lv_draw_unit_t * draw_unit,const lv_draw_letter_dsc_t * dsc,const lv_area_t * coords)84 void lv_draw_vg_lite_letter(lv_draw_unit_t * draw_unit, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords)
85 {
86     if(dsc->opa <= LV_OPA_MIN)
87         return;
88 
89     lv_draw_glyph_dsc_t glyph_dsc;
90     lv_draw_glyph_dsc_init(&glyph_dsc);
91     glyph_dsc.opa = dsc->opa;
92     glyph_dsc.bg_coords = NULL;
93     glyph_dsc.color = dsc->color;
94     glyph_dsc.rotation = dsc->rotation;
95     glyph_dsc.pivot = dsc->pivot;
96 
97     LV_PROFILER_BEGIN;
98     lv_draw_unit_draw_letter(draw_unit, &glyph_dsc, &(lv_point_t) {
99         .x = coords->x1, .y = coords->y1
100     },
101     dsc->font, dsc->unicode, draw_letter_cb);
102     LV_PROFILER_END;
103 
104     if(glyph_dsc._draw_buf) {
105         lv_draw_buf_destroy(glyph_dsc._draw_buf);
106         glyph_dsc._draw_buf = NULL;
107     }
108 }
109 
lv_draw_vg_lite_label(lv_draw_unit_t * draw_unit,const lv_draw_label_dsc_t * dsc,const lv_area_t * coords)110 void lv_draw_vg_lite_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
111                            const lv_area_t * coords)
112 {
113     LV_PROFILER_DRAW_BEGIN;
114     lv_draw_label_iterate_characters(draw_unit, dsc, coords, draw_letter_cb);
115     LV_PROFILER_DRAW_END;
116 }
117 
118 /**********************
119  *   STATIC FUNCTIONS
120  **********************/
121 
draw_letter_cb(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)122 static void draw_letter_cb(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc,
123                            lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area)
124 {
125     lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit;
126     if(glyph_draw_dsc) {
127         switch(glyph_draw_dsc->format) {
128             case LV_FONT_GLYPH_FORMAT_A1:
129             case LV_FONT_GLYPH_FORMAT_A2:
130             case LV_FONT_GLYPH_FORMAT_A3:
131             case LV_FONT_GLYPH_FORMAT_A4:
132             case LV_FONT_GLYPH_FORMAT_A8:
133             case LV_FONT_GLYPH_FORMAT_A1_ALIGNED:
134             case LV_FONT_GLYPH_FORMAT_A2_ALIGNED:
135             case LV_FONT_GLYPH_FORMAT_A4_ALIGNED:
136             case LV_FONT_GLYPH_FORMAT_A8_ALIGNED: {
137                     glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
138                     draw_letter_bitmap(u, glyph_draw_dsc);
139                 }
140                 break;
141 
142 #if LV_USE_FREETYPE
143             case LV_FONT_GLYPH_FORMAT_VECTOR: {
144                     if(lv_freetype_is_outline_font(glyph_draw_dsc->g->resolved_font)) {
145                         glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
146                         draw_letter_outline(u, glyph_draw_dsc);
147                     }
148                 }
149                 break;
150 #endif /* LV_USE_FREETYPE */
151 
152             case LV_FONT_GLYPH_FORMAT_IMAGE: {
153                     glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
154                     lv_draw_image_dsc_t image_dsc;
155                     lv_draw_image_dsc_init(&image_dsc);
156                     image_dsc.opa = glyph_draw_dsc->opa;
157                     image_dsc.src = glyph_draw_dsc->glyph_data;
158                     image_dsc.rotation = glyph_draw_dsc->rotation;
159                     lv_draw_vg_lite_img(draw_unit, &image_dsc, glyph_draw_dsc->letter_coords, false);
160                 }
161                 break;
162 
163 #if LV_USE_FONT_PLACEHOLDER
164             case LV_FONT_GLYPH_FORMAT_NONE: {
165                     /* Draw a placeholder rectangle*/
166                     lv_draw_border_dsc_t border_draw_dsc;
167                     lv_draw_border_dsc_init(&border_draw_dsc);
168                     border_draw_dsc.opa = glyph_draw_dsc->opa;
169                     border_draw_dsc.color = glyph_draw_dsc->color;
170                     border_draw_dsc.width = 1;
171                     lv_draw_vg_lite_border(draw_unit, &border_draw_dsc, glyph_draw_dsc->bg_coords);
172                 }
173                 break;
174 #endif /* LV_USE_FONT_PLACEHOLDER */
175 
176             default:
177                 break;
178         }
179     }
180 
181     if(fill_draw_dsc && fill_area) {
182         lv_draw_vg_lite_fill(draw_unit, fill_draw_dsc, fill_area);
183     }
184 
185     /* Flush in time to avoid accumulation of drawing commands */
186     u->letter_count++;
187     if(u->letter_count > PATH_FLUSH_COUNT_MAX) {
188         lv_vg_lite_flush(u);
189     }
190 }
191 
draw_letter_bitmap(lv_draw_vg_lite_unit_t * u,const lv_draw_glyph_dsc_t * dsc)192 static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc)
193 {
194     lv_area_t clip_area;
195     if(!lv_area_intersect(&clip_area, u->base_unit.clip_area, dsc->letter_coords)) {
196         return;
197     }
198 
199     LV_PROFILER_DRAW_BEGIN;
200 
201     const lv_area_t image_area = *dsc->letter_coords;
202 
203     vg_lite_matrix_t matrix = u->global_matrix;
204 
205     const bool is_rotated = dsc->rotation % 3600 != 0;
206 
207     if(!is_rotated) {
208         vg_lite_translate(image_area.x1, image_area.y1, &matrix);
209     }
210     else {
211         vg_lite_translate(image_area.x1 + dsc->pivot.x, image_area.y1 + (dsc->g->box_h + dsc->g->ofs_y), &matrix);
212         vg_lite_rotate(dsc->rotation / 10.0f, &matrix);
213         vg_lite_translate(-dsc->pivot.x, -dsc->g->box_h - dsc->g->ofs_y, &matrix);
214     }
215 
216     vg_lite_buffer_t src_buf;
217     const lv_draw_buf_t * draw_buf = dsc->glyph_data;
218     lv_vg_lite_buffer_from_draw_buf(&src_buf, draw_buf);
219 
220     const vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true);
221 
222     LV_VG_LITE_ASSERT_SRC_BUFFER(&src_buf);
223     LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer);
224 
225     /* If clipping is not required, blit directly */
226     if(lv_area_is_in(&image_area, u->base_unit.clip_area, false)) {
227         /* rect is used to crop the pixel-aligned padding area */
228         vg_lite_rectangle_t rect = {
229             .x = 0,
230             .y = 0,
231             .width = lv_area_get_width(&image_area),
232             .height = lv_area_get_height(&image_area)
233         };
234 
235         LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_blit_rect");
236         LV_VG_LITE_CHECK_ERROR(vg_lite_blit_rect(
237                                    &u->target_buffer,
238                                    &src_buf,
239                                    &rect,
240                                    &matrix,
241                                    VG_LITE_BLEND_SRC_OVER,
242                                    color,
243                                    VG_LITE_FILTER_LINEAR));
244         LV_PROFILER_DRAW_END_TAG("vg_lite_blit_rect");
245     }
246     else {
247         lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_S16);
248         lv_vg_lite_path_append_rect(
249             path,
250             clip_area.x1, clip_area.y1,
251             lv_area_get_width(&clip_area), lv_area_get_height(&clip_area),
252             0);
253         lv_vg_lite_path_set_bounding_box_area(path, &clip_area);
254         lv_vg_lite_path_end(path);
255 
256         vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path);
257         LV_VG_LITE_ASSERT_PATH(vg_lite_path);
258 
259         vg_lite_matrix_t path_matrix = u->global_matrix;
260         if(is_rotated) vg_lite_rotate(dsc->rotation / 10.0f, &path_matrix);
261         LV_VG_LITE_ASSERT_MATRIX(&path_matrix);
262 
263         LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw_pattern");
264         LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern(
265                                    &u->target_buffer,
266                                    vg_lite_path,
267                                    VG_LITE_FILL_EVEN_ODD,
268                                    &path_matrix,
269                                    &src_buf,
270                                    &matrix,
271                                    VG_LITE_BLEND_SRC_OVER,
272                                    VG_LITE_PATTERN_COLOR,
273                                    0,
274                                    color,
275                                    VG_LITE_FILTER_LINEAR));
276         LV_PROFILER_DRAW_END_TAG("vg_lite_draw_pattern");
277 
278         lv_vg_lite_path_drop(u, path);
279     }
280 
281     /* TODO: The temporary buffer of the built-in font is reused.
282      * You need to wait for the GPU to finish using the buffer before releasing it.
283      * Later, use the font cache for management to improve efficiency.
284      */
285     lv_vg_lite_finish(u);
286     LV_PROFILER_DRAW_END;
287 }
288 
289 #if LV_USE_FREETYPE
290 
draw_letter_outline(lv_draw_vg_lite_unit_t * u,const lv_draw_glyph_dsc_t * dsc)291 static void draw_letter_outline(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc)
292 {
293     /* get clip area */
294     lv_area_t path_clip_area;
295     if(!lv_area_intersect(&path_clip_area, u->base_unit.clip_area, dsc->letter_coords)) {
296         return;
297     }
298 
299     LV_PROFILER_DRAW_BEGIN;
300 
301     /* vg-lite bounding_box will crop the pixels on the edge, so +1px is needed here */
302     path_clip_area.x2++;
303     path_clip_area.y2++;
304 
305     lv_vg_lite_path_t * outline = (lv_vg_lite_path_t *)dsc->glyph_data;
306     const lv_point_t pos = {dsc->letter_coords->x1, dsc->letter_coords->y1};
307     /* scale size */
308     const float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(dsc->g->resolved_font));
309     const bool is_rotated = dsc->rotation % 3600 != 0;
310 
311     /* calc convert matrix */
312     vg_lite_matrix_t matrix;
313     vg_lite_identity(&matrix);
314 
315     if(!is_rotated) {
316         vg_lite_translate(pos.x - dsc->g->ofs_x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix);
317         vg_lite_scale(scale, scale, &matrix);
318     }
319     else {
320         vg_lite_translate(pos.x - dsc->g->ofs_x + dsc->pivot.x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix);
321         vg_lite_rotate(dsc->rotation / 10.0f, &matrix);
322         vg_lite_translate(-dsc->pivot.x, 0, &matrix);
323         vg_lite_scale(scale, scale, &matrix);
324     }
325 
326     if(vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR)) {
327         /* set scissor area */
328         lv_vg_lite_set_scissor_area(u->base_unit.clip_area);
329 
330         /* no bounding box */
331         lv_vg_lite_path_set_bounding_box(outline,
332                                          (float)PATH_COORD_MIN, (float)PATH_COORD_MIN,
333                                          (float)PATH_COORD_MAX, (float)PATH_COORD_MAX);
334     }
335     else {
336         if(is_rotated) {
337             LV_LOG_WARN("clip may be incorrect when vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR) is false");
338         }
339 
340         /* calc inverse matrix */
341         vg_lite_matrix_t result;
342         if(!lv_vg_lite_matrix_inverse(&result, &matrix)) {
343             LV_LOG_ERROR("no inverse matrix");
344             LV_PROFILER_DRAW_END;
345             return;
346         }
347 
348         const lv_point_precise_t p1 = { path_clip_area.x1, path_clip_area.y1 };
349         const lv_point_precise_t p1_res = lv_vg_lite_matrix_transform_point(&result, &p1);
350 
351         const lv_point_precise_t p2 = { path_clip_area.x2, path_clip_area.y2 };
352         const lv_point_precise_t p2_res = lv_vg_lite_matrix_transform_point(&result, &p2);
353 
354         /* Since the font uses Cartesian coordinates, the y coordinates need to be reversed */
355         if(is_rotated)
356             lv_vg_lite_path_set_bounding_box(outline,
357                                              (float)PATH_COORD_MIN, (float)PATH_COORD_MIN,
358                                              (float)PATH_COORD_MAX, (float)PATH_COORD_MAX);
359         else lv_vg_lite_path_set_bounding_box(outline, p1_res.x, p2_res.y, p2_res.x, p1_res.y);
360     }
361 
362     /* matrix for drawing, different from matrix for calculating the bounding box */
363     vg_lite_matrix_t draw_matrix = u->global_matrix;
364     lv_vg_lite_matrix_multiply(&draw_matrix, &matrix);
365 
366     vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(outline);
367 
368     LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer);
369     LV_VG_LITE_ASSERT_PATH(vg_lite_path);
370     LV_VG_LITE_ASSERT_MATRIX(&draw_matrix);
371 
372     LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw");
373     LV_VG_LITE_CHECK_ERROR(vg_lite_draw(
374                                &u->target_buffer, vg_lite_path, VG_LITE_FILL_NON_ZERO,
375                                &draw_matrix, VG_LITE_BLEND_SRC_OVER, lv_vg_lite_color(dsc->color, dsc->opa, true)));
376     LV_PROFILER_DRAW_END_TAG("vg_lite_draw");
377 
378     LV_PROFILER_DRAW_END;
379 }
380 
vg_lite_outline_push(const lv_freetype_outline_event_param_t * param)381 static void vg_lite_outline_push(const lv_freetype_outline_event_param_t * param)
382 {
383     LV_PROFILER_DRAW_BEGIN;
384     lv_vg_lite_path_t * outline = param->outline;
385     LV_ASSERT_NULL(outline);
386 
387     lv_freetype_outline_type_t type = param->type;
388     switch(type) {
389 
390         /**
391          * Reverse the Y-axis coordinate direction to achieve
392          * the conversion from Cartesian coordinate system to LCD coordinate system
393          */
394         case LV_FREETYPE_OUTLINE_END:
395             lv_vg_lite_path_end(outline);
396             break;
397         case LV_FREETYPE_OUTLINE_MOVE_TO:
398             lv_vg_lite_path_move_to(outline, param->to.x, -param->to.y);
399             break;
400         case LV_FREETYPE_OUTLINE_LINE_TO:
401             lv_vg_lite_path_line_to(outline, param->to.x, -param->to.y);
402             break;
403         case LV_FREETYPE_OUTLINE_CUBIC_TO:
404             lv_vg_lite_path_cubic_to(outline, param->control1.x, -param->control1.y,
405                                      param->control2.x, -param->control2.y,
406                                      param->to.x, -param->to.y);
407             break;
408         case LV_FREETYPE_OUTLINE_CONIC_TO:
409             lv_vg_lite_path_quad_to(outline, param->control1.x, -param->control1.y,
410                                     param->to.x, -param->to.y);
411             break;
412         default:
413             LV_LOG_ERROR("unknown point type: %d", type);
414             LV_ASSERT(false);
415             break;
416     }
417     LV_PROFILER_DRAW_END;
418 }
419 
freetype_outline_event_cb(lv_event_t * e)420 static void freetype_outline_event_cb(lv_event_t * e)
421 {
422     LV_PROFILER_DRAW_BEGIN;
423     lv_event_code_t code = lv_event_get_code(e);
424     lv_freetype_outline_event_param_t * param = lv_event_get_param(e);
425     switch(code) {
426         case LV_EVENT_CREATE:
427             param->outline = lv_vg_lite_path_create(PATH_DATA_COORD_FORMAT);
428             break;
429         case LV_EVENT_DELETE:
430             lv_vg_lite_path_destroy(param->outline);
431             break;
432         case LV_EVENT_INSERT:
433             vg_lite_outline_push(param);
434             break;
435         default:
436             LV_LOG_WARN("unknown event code: %d", code);
437             break;
438     }
439     LV_PROFILER_DRAW_END;
440 }
441 
442 #endif /* LV_USE_FREETYPE */
443 
444 #endif /*LV_USE_DRAW_VG_LITE*/
445