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