1 /**
2  * @file lv_draw_vg_lite_vector.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../lv_image_decoder_private.h"
11 #include "../lv_draw_vector_private.h"
12 #include "lv_draw_vg_lite.h"
13 
14 #if LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC
15 
16 #include "lv_draw_vg_lite_type.h"
17 #include "lv_vg_lite_path.h"
18 #include "lv_vg_lite_pending.h"
19 #include "lv_vg_lite_utils.h"
20 #include "lv_vg_lite_grad.h"
21 #include "lv_vg_lite_stroke.h"
22 #include <float.h>
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 
28 /**********************
29  *      TYPEDEFS
30  **********************/
31 
32 typedef void * path_drop_data_t;
33 typedef void (*path_drop_func_t)(struct _lv_draw_vg_lite_unit_t *, path_drop_data_t);
34 
35 /**********************
36  *  STATIC PROTOTYPES
37  **********************/
38 
39 static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc);
40 static void lv_path_to_vg(lv_vg_lite_path_t * dest, const lv_vector_path_t * src);
41 static vg_lite_path_type_t lv_path_opa_to_path_type(const lv_vector_draw_dsc_t * dsc);
42 static vg_lite_blend_t lv_blend_to_vg(lv_vector_blend_t blend);
43 static vg_lite_fill_t lv_fill_to_vg(lv_vector_fill_t fill_rule);
44 
45 /**********************
46  *  STATIC VARIABLES
47  **********************/
48 
49 /**********************
50  *      MACROS
51  **********************/
52 
53 /**********************
54  *   GLOBAL FUNCTIONS
55  **********************/
56 
lv_draw_vg_lite_vector(lv_draw_unit_t * draw_unit,const lv_draw_vector_task_dsc_t * dsc)57 void lv_draw_vg_lite_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc)
58 {
59     if(dsc->task_list == NULL)
60         return;
61 
62     lv_layer_t * layer = dsc->base.layer;
63     if(layer->draw_buf == NULL)
64         return;
65 
66     LV_PROFILER_DRAW_BEGIN;
67     lv_vector_for_each_destroy_tasks(dsc->task_list, task_draw_cb, draw_unit);
68     LV_PROFILER_DRAW_END;
69 }
70 
71 /**********************
72  *   STATIC FUNCTIONS
73  **********************/
74 
lv_color32_to_vg(lv_color32_t color,lv_opa_t opa)75 static vg_lite_color_t lv_color32_to_vg(lv_color32_t color, lv_opa_t opa)
76 {
77     uint8_t a = LV_OPA_MIX2(color.alpha, opa);
78     if(a < LV_OPA_COVER) {
79         color.red = LV_UDIV255(color.red * a);
80         color.green = LV_UDIV255(color.green * a);
81         color.blue = LV_UDIV255(color.blue * a);
82     }
83     return (uint32_t)a << 24 | (uint32_t)color.blue << 16 | (uint32_t)color.green << 8 | color.red;
84 }
85 
task_draw_cb(void * ctx,const lv_vector_path_t * path,const lv_vector_draw_dsc_t * dsc)86 static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc)
87 {
88     LV_PROFILER_DRAW_BEGIN;
89     lv_draw_vg_lite_unit_t * u = ctx;
90     LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer);
91 
92     /* clear area */
93     if(!path) {
94         /* clear color needs to ignore fill_dsc.opa */
95         vg_lite_color_t c = lv_color32_to_vg(dsc->fill_dsc.color, LV_OPA_COVER);
96         vg_lite_rectangle_t rect;
97         lv_vg_lite_rect(&rect, &dsc->scissor_area);
98         LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_clear");
99         LV_VG_LITE_CHECK_ERROR(vg_lite_clear(&u->target_buffer, &rect, c));
100         LV_PROFILER_DRAW_END_TAG("vg_lite_clear");
101         LV_PROFILER_DRAW_END;
102         return;
103     }
104 
105     /* convert color */
106     vg_lite_color_t vg_color = lv_color32_to_vg(dsc->fill_dsc.color, dsc->fill_dsc.opa);
107 
108     /* transform matrix */
109     vg_lite_matrix_t matrix;
110     lv_vg_lite_matrix(&matrix, &dsc->matrix);
111     LV_VG_LITE_ASSERT_MATRIX(&matrix);
112 
113     /* convert path */
114     lv_vg_lite_path_t * lv_vg_path = lv_vg_lite_path_get(u, VG_LITE_FP32);
115     lv_path_to_vg(lv_vg_path, path);
116 
117     /* get path bounds */
118     float min_x, min_y, max_x, max_y;
119     lv_vg_lite_path_get_bounding_box(lv_vg_path, &min_x, &min_y, &max_x, &max_y);
120 
121     /* convert path type */
122     vg_lite_path_type_t path_type = lv_path_opa_to_path_type(dsc);
123 
124     /* convert blend mode and fill rule */
125     vg_lite_blend_t blend = lv_blend_to_vg(dsc->blend_mode);
126     vg_lite_fill_t fill = lv_fill_to_vg(dsc->fill_dsc.fill_rule);
127 
128     /* set default path drop function and data */
129     path_drop_func_t path_drop_func = (path_drop_func_t)lv_vg_lite_path_drop;
130     path_drop_data_t path_drop_data = lv_vg_path;
131 
132     /* If it is fill mode, the end op code should be added */
133     if(path_type == VG_LITE_DRAW_ZERO
134        || path_type == VG_LITE_DRAW_FILL_PATH
135        || path_type == VG_LITE_DRAW_FILL_STROKE_PATH) {
136         lv_vg_lite_path_end(lv_vg_path);
137     }
138 
139     /* convert stroke style */
140     if(path_type == VG_LITE_DRAW_STROKE_PATH
141        || path_type == VG_LITE_DRAW_FILL_STROKE_PATH) {
142         lv_cache_entry_t * stroke_cache_entey = lv_vg_lite_stroke_get(u, lv_vg_path, &dsc->stroke_dsc);
143 
144         if(!stroke_cache_entey) {
145             LV_LOG_ERROR("convert stroke failed");
146 
147             /* drop original path */
148             lv_vg_lite_path_drop(u, lv_vg_path);
149             LV_PROFILER_DRAW_END;
150             return;
151         }
152 
153         lv_vg_lite_path_t * ori_path = lv_vg_path;
154         const vg_lite_path_t * ori_vg_path = lv_vg_lite_path_get_path(ori_path);
155 
156         lv_vg_lite_path_t * stroke_path = lv_vg_lite_stroke_get_path(stroke_cache_entey);
157         vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(stroke_path);
158 
159         /* set stroke params */
160         LV_VG_LITE_CHECK_ERROR(vg_lite_set_path_type(vg_path, path_type));
161         vg_path->stroke_color = lv_color32_to_vg(dsc->stroke_dsc.color, dsc->stroke_dsc.opa);
162         vg_path->quality = ori_vg_path->quality;
163         lv_memcpy(vg_path->bounding_box, ori_vg_path->bounding_box, sizeof(ori_vg_path->bounding_box));
164 
165         /* change path to stroke path */
166         LV_LOG_TRACE("change path to stroke path: %p -> %p", (void *)lv_vg_path, (void *)stroke_path);
167         lv_vg_path = stroke_path;
168         path_drop_func = (path_drop_func_t)lv_vg_lite_stroke_drop;
169         path_drop_data = stroke_cache_entey;
170 
171         /* drop original path */
172         lv_vg_lite_path_drop(u, ori_path);
173     }
174 
175     vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(lv_vg_path);
176     LV_VG_LITE_ASSERT_PATH(vg_path);
177 
178     if(vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR)) {
179         /* set scissor area */
180         lv_vg_lite_set_scissor_area(&dsc->scissor_area);
181 
182         /* no bounding box */
183         lv_vg_lite_path_set_bounding_box(lv_vg_path,
184                                          (float)PATH_COORD_MIN, (float)PATH_COORD_MIN,
185                                          (float)PATH_COORD_MAX, (float)PATH_COORD_MAX);
186     }
187     else {
188         /* calc inverse matrix */
189         vg_lite_matrix_t result;
190         if(!lv_vg_lite_matrix_inverse(&result, &matrix)) {
191             LV_LOG_ERROR("no inverse matrix");
192             path_drop_func(u, path_drop_data);
193             LV_PROFILER_DRAW_END;
194             return;
195         }
196 
197         /* Reverse the clip area on the source */
198         lv_point_precise_t p1 = { dsc->scissor_area.x1, dsc->scissor_area.y1 };
199         lv_point_precise_t p1_res = lv_vg_lite_matrix_transform_point(&result, &p1);
200 
201         /* vg-lite bounding_box will crop the pixels on the edge, so +1px is needed here */
202         lv_point_precise_t p2 = { dsc->scissor_area.x2 + 1, dsc->scissor_area.y2 + 1 };
203         lv_point_precise_t p2_res = lv_vg_lite_matrix_transform_point(&result, &p2);
204 
205         lv_vg_lite_path_set_bounding_box(lv_vg_path, p1_res.x, p1_res.y, p2_res.x, p2_res.y);
206     }
207 
208     switch(dsc->fill_dsc.style) {
209         case LV_VECTOR_DRAW_STYLE_SOLID: {
210                 /* normal draw shape */
211                 LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw");
212                 LV_VG_LITE_CHECK_ERROR(vg_lite_draw(
213                                            &u->target_buffer,
214                                            vg_path,
215                                            fill,
216                                            &matrix,
217                                            blend,
218                                            vg_color));
219                 LV_PROFILER_DRAW_END_TAG("vg_lite_draw");
220             }
221             break;
222         case LV_VECTOR_DRAW_STYLE_PATTERN: {
223                 /* draw image */
224                 vg_lite_buffer_t image_buffer;
225                 lv_image_decoder_dsc_t decoder_dsc;
226                 if(lv_vg_lite_buffer_open_image(&image_buffer, &decoder_dsc, dsc->fill_dsc.img_dsc.src, false, true)) {
227                     /* Calculate pattern matrix. Should start from path bond box, and also apply fill matrix. */
228                     lv_matrix_t m = dsc->matrix;
229                     lv_matrix_translate(&m, min_x, min_y);
230                     lv_matrix_multiply(&m, &dsc->fill_dsc.matrix);
231 
232                     vg_lite_matrix_t pattern_matrix;
233                     lv_vg_lite_matrix(&pattern_matrix, &m);
234 
235                     vg_lite_color_t recolor = lv_vg_lite_image_recolor(&image_buffer, &dsc->fill_dsc.img_dsc);
236 
237                     LV_VG_LITE_ASSERT_MATRIX(&pattern_matrix);
238 
239                     LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw_pattern");
240                     LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern(
241                                                &u->target_buffer,
242                                                vg_path,
243                                                fill,
244                                                &matrix,
245                                                &image_buffer,
246                                                &pattern_matrix,
247                                                blend,
248                                                VG_LITE_PATTERN_COLOR,
249                                                0,
250                                                recolor,
251                                                VG_LITE_FILTER_BI_LINEAR));
252                     LV_PROFILER_DRAW_END_TAG("vg_lite_draw_pattern");
253 
254                     lv_vg_lite_pending_add(u->image_dsc_pending, &decoder_dsc);
255                 }
256             }
257             break;
258         case LV_VECTOR_DRAW_STYLE_GRADIENT: {
259                 vg_lite_matrix_t grad_matrix = matrix;
260 
261 #if LV_USE_VG_LITE_THORVG
262                 /* Workaround inconsistent radial gradient matrix behavior between device and ThorVG */
263                 if(dsc->fill_dsc.gradient.style == LV_VECTOR_GRADIENT_STYLE_RADIAL) {
264                     /* Restore matrix to identity */
265                     vg_lite_identity(&grad_matrix);
266                 }
267 #endif
268                 vg_lite_matrix_t fill_matrix;
269                 lv_vg_lite_matrix(&fill_matrix, &dsc->fill_dsc.matrix);
270                 lv_vg_lite_matrix_multiply(&grad_matrix, &fill_matrix);
271 
272                 lv_vg_lite_draw_grad(
273                     u,
274                     &u->target_buffer,
275                     vg_path,
276                     &dsc->fill_dsc.gradient,
277                     &grad_matrix,
278                     &matrix,
279                     fill,
280                     blend);
281             }
282             break;
283         default:
284             LV_LOG_WARN("unknown style: %d", dsc->fill_dsc.style);
285             break;
286     }
287 
288     /* Flush in time to avoid accumulation of drawing commands */
289     lv_vg_lite_flush(u);
290 
291     /* drop path */
292     path_drop_func(u, path_drop_data);
293 
294     if(vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR)) {
295         /* disable scissor */
296         lv_vg_lite_disable_scissor();
297     }
298 
299     LV_PROFILER_DRAW_END;
300 }
301 
lv_quality_to_vg(lv_vector_path_quality_t quality)302 static vg_lite_quality_t lv_quality_to_vg(lv_vector_path_quality_t quality)
303 {
304     switch(quality) {
305         case LV_VECTOR_PATH_QUALITY_LOW:
306             return VG_LITE_LOW;
307         case LV_VECTOR_PATH_QUALITY_MEDIUM:
308             return VG_LITE_MEDIUM;
309         case LV_VECTOR_PATH_QUALITY_HIGH:
310             return VG_LITE_HIGH;
311         default:
312             return VG_LITE_MEDIUM;
313     }
314 }
315 
lv_path_to_vg(lv_vg_lite_path_t * dest,const lv_vector_path_t * src)316 static void lv_path_to_vg(lv_vg_lite_path_t * dest, const lv_vector_path_t * src)
317 {
318     LV_PROFILER_DRAW_BEGIN;
319     lv_vg_lite_path_set_quality(dest, lv_quality_to_vg(src->quality));
320 
321     /* init bounds */
322     float min_x = FLT_MAX;
323     float min_y = FLT_MAX;
324     float max_x = FLT_MIN;
325     float max_y = FLT_MIN;
326 
327 #define CMP_BOUNDS(point)                           \
328     do {                                            \
329         if((point)->x < min_x) min_x = (point)->x;  \
330         if((point)->y < min_y) min_y = (point)->y;  \
331         if((point)->x > max_x) max_x = (point)->x;  \
332         if((point)->y > max_y) max_y = (point)->y;  \
333     } while(0)
334 
335 #define COPY_POINT_NEXT()        \
336     do {                         \
337         CMP_BOUNDS(point);       \
338         *path_data++ = point->x; \
339         *path_data++ = point->y; \
340         point++;                 \
341     } while(0)
342 
343     const lv_vector_path_op_t * ops = lv_array_front(&src->ops);
344     const lv_fpoint_t * point = lv_array_front(&src->points);
345     const uint32_t op_size = lv_array_size(&src->ops);
346     const uint32_t point_size = lv_array_size(&src->points);
347     const uint32_t path_length = (op_size + point_size * 2) * sizeof(float);
348 
349     /* Reserved memory for path data */
350     lv_vg_lite_path_reserve_space(dest, path_length);
351     vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(dest);
352     vg_path->path_length = path_length;
353     float * path_data = vg_path->path;
354 
355     for(uint32_t i = 0; i < op_size; i++) {
356         switch(ops[i]) {
357             case LV_VECTOR_PATH_OP_MOVE_TO: {
358                     LV_VG_LITE_PATH_SET_OP_CODE(path_data++, uint32_t, VLC_OP_MOVE);
359                     COPY_POINT_NEXT();
360                 }
361                 break;
362             case LV_VECTOR_PATH_OP_LINE_TO: {
363                     LV_VG_LITE_PATH_SET_OP_CODE(path_data++, uint32_t, VLC_OP_LINE);
364                     COPY_POINT_NEXT();
365                 }
366                 break;
367             case LV_VECTOR_PATH_OP_QUAD_TO: {
368                     LV_VG_LITE_PATH_SET_OP_CODE(path_data++, uint32_t, VLC_OP_QUAD);
369                     COPY_POINT_NEXT();
370                     COPY_POINT_NEXT();
371                 }
372                 break;
373             case LV_VECTOR_PATH_OP_CUBIC_TO: {
374                     LV_VG_LITE_PATH_SET_OP_CODE(path_data++, uint32_t, VLC_OP_CUBIC);
375                     COPY_POINT_NEXT();
376                     COPY_POINT_NEXT();
377                     COPY_POINT_NEXT();
378                 }
379                 break;
380             case LV_VECTOR_PATH_OP_CLOSE: {
381                     LV_VG_LITE_PATH_SET_OP_CODE(path_data++, uint32_t, VLC_OP_CLOSE);
382                 }
383                 break;
384             default:
385                 LV_LOG_WARN("unknown op: %d", ops[i]);
386                 break;
387         }
388     }
389 
390     LV_ASSERT_MSG((lv_uintptr_t)path_data - (lv_uintptr_t)vg_path->path == path_length, "path length overflow");
391 
392     lv_vg_lite_path_set_bounding_box(dest, min_x, min_y, max_x, max_y);
393     LV_PROFILER_DRAW_END;
394 }
395 
lv_path_opa_to_path_type(const lv_vector_draw_dsc_t * dsc)396 static vg_lite_path_type_t lv_path_opa_to_path_type(const lv_vector_draw_dsc_t * dsc)
397 {
398     lv_opa_t fill_opa = dsc->fill_dsc.opa;
399     lv_opa_t stroke_opa = dsc->stroke_dsc.opa;
400 
401     if(fill_opa > LV_OPA_0 && stroke_opa > LV_OPA_0) {
402         return VG_LITE_DRAW_FILL_STROKE_PATH;
403     }
404 
405     if(fill_opa == LV_OPA_0 && stroke_opa > LV_OPA_0) {
406         return VG_LITE_DRAW_STROKE_PATH;
407     }
408 
409     if(fill_opa > LV_OPA_0) {
410         return VG_LITE_DRAW_FILL_PATH;
411     }
412 
413     return VG_LITE_DRAW_ZERO;
414 }
415 
lv_blend_to_vg(lv_vector_blend_t blend)416 static vg_lite_blend_t lv_blend_to_vg(lv_vector_blend_t blend)
417 {
418     switch(blend) {
419         case LV_VECTOR_BLEND_SRC_OVER:
420             return VG_LITE_BLEND_SRC_OVER;
421         case LV_VECTOR_BLEND_SCREEN:
422             return VG_LITE_BLEND_SCREEN;
423         case LV_VECTOR_BLEND_MULTIPLY:
424             return VG_LITE_BLEND_MULTIPLY;
425         case LV_VECTOR_BLEND_NONE:
426             return VG_LITE_BLEND_NONE;
427         case LV_VECTOR_BLEND_ADDITIVE:
428             return VG_LITE_BLEND_ADDITIVE;
429         case LV_VECTOR_BLEND_SRC_IN:
430             return VG_LITE_BLEND_SRC_IN;
431         case LV_VECTOR_BLEND_DST_OVER:
432             return VG_LITE_BLEND_DST_OVER;
433         case LV_VECTOR_BLEND_DST_IN:
434             return VG_LITE_BLEND_DST_IN;
435         case LV_VECTOR_BLEND_SUBTRACTIVE:
436             return VG_LITE_BLEND_SUBTRACT;
437         default:
438             return VG_LITE_BLEND_SRC_OVER;
439     }
440 }
441 
lv_fill_to_vg(lv_vector_fill_t fill_rule)442 static vg_lite_fill_t lv_fill_to_vg(lv_vector_fill_t fill_rule)
443 {
444     switch(fill_rule) {
445         case LV_VECTOR_FILL_NONZERO:
446             return VG_LITE_FILL_NON_ZERO;
447         case LV_VECTOR_FILL_EVENODD:
448             return VG_LITE_FILL_EVEN_ODD;
449         default:
450             return VG_LITE_FILL_NON_ZERO;
451     }
452 }
453 
454 #endif /*LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC*/
455