1 /**
2  * @file lv_vg_lite_stroke.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_vg_lite_stroke.h"
11 
12 #if LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC
13 
14 #include "lv_vg_lite_path.h"
15 #include "lv_draw_vg_lite_type.h"
16 #include "lv_vg_lite_math.h"
17 #include "../lv_draw_vector_private.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 
27 
28 /**
29  * Since the key-value data structure of lv_cache is integrated, the kv data structure
30  * will be saved at the same time when the cache is successfully created.
31  * In order to pursue efficiency during the matching process, the primary key (lv) used for matching
32  * will not dup the dash_pattern secondary pointer, so when the creation is successful, dash_pattern needs
33  * to be dup to the child key (vg), so type is added here to distinguish where the real data of dash_pattern exists.
34  */
35 typedef enum {
36     DASH_PATTERN_TYPE_LV,
37     DASH_PATTERN_TYPE_VG
38 } dash_pattern_type_t;
39 
40 typedef struct {
41     dash_pattern_type_t dash_pattern_type;
42 
43     struct {
44         /* path data */
45         lv_vg_lite_path_t * path;
46 
47         /* stroke parameters */
48         float width;
49         lv_vector_stroke_cap_t cap;
50         lv_vector_stroke_join_t join;
51         uint16_t miter_limit;
52         lv_array_t dash_pattern;
53     } lv;
54 
55     struct {
56         /* stroke path */
57         lv_vg_lite_path_t * path;
58 
59         /* dash pattern, for comparison only */
60         lv_array_t dash_pattern;
61     } vg;
62 } stroke_item_t;
63 
64 /**********************
65  *  STATIC PROTOTYPES
66  **********************/
67 
68 static bool stroke_create_cb(stroke_item_t * item, void * user_data);
69 static void stroke_free_cb(stroke_item_t * item, void * user_data);
70 static lv_cache_compare_res_t stroke_compare_cb(const stroke_item_t * lhs, const stroke_item_t * rhs);
71 
72 /**********************
73  *  STATIC VARIABLES
74  **********************/
75 
76 /**********************
77  *      MACROS
78  **********************/
79 
80 /**********************
81  *   GLOBAL FUNCTIONS
82  **********************/
83 
lv_vg_lite_stroke_init(struct _lv_draw_vg_lite_unit_t * unit,uint32_t cache_cnt)84 void lv_vg_lite_stroke_init(struct _lv_draw_vg_lite_unit_t * unit, uint32_t cache_cnt)
85 {
86     LV_ASSERT_NULL(unit);
87 
88     const lv_cache_ops_t ops = {
89         .compare_cb = (lv_cache_compare_cb_t)stroke_compare_cb,
90         .create_cb = (lv_cache_create_cb_t)stroke_create_cb,
91         .free_cb = (lv_cache_free_cb_t)stroke_free_cb,
92     };
93 
94     unit->stroke_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(stroke_item_t), cache_cnt, ops);
95     lv_cache_set_name(unit->stroke_cache, "VG_STROKE");
96 }
97 
lv_vg_lite_stroke_deinit(struct _lv_draw_vg_lite_unit_t * unit)98 void lv_vg_lite_stroke_deinit(struct _lv_draw_vg_lite_unit_t * unit)
99 {
100     LV_ASSERT_NULL(unit);
101     LV_ASSERT_NULL(unit->stroke_cache);
102     lv_cache_destroy(unit->stroke_cache, NULL);
103     unit->stroke_cache = NULL;
104 }
105 
lv_stroke_cap_to_vg(lv_vector_stroke_cap_t cap)106 static vg_lite_cap_style_t lv_stroke_cap_to_vg(lv_vector_stroke_cap_t cap)
107 {
108     switch(cap) {
109         case LV_VECTOR_STROKE_CAP_SQUARE:
110             return VG_LITE_CAP_SQUARE;
111         case LV_VECTOR_STROKE_CAP_ROUND:
112             return VG_LITE_CAP_ROUND;
113         case LV_VECTOR_STROKE_CAP_BUTT:
114             return VG_LITE_CAP_BUTT;
115         default:
116             return VG_LITE_CAP_SQUARE;
117     }
118 }
119 
lv_stroke_join_to_vg(lv_vector_stroke_join_t join)120 static vg_lite_join_style_t lv_stroke_join_to_vg(lv_vector_stroke_join_t join)
121 {
122     switch(join) {
123         case LV_VECTOR_STROKE_JOIN_BEVEL:
124             return VG_LITE_JOIN_BEVEL;
125         case LV_VECTOR_STROKE_JOIN_ROUND:
126             return VG_LITE_JOIN_ROUND;
127         case LV_VECTOR_STROKE_JOIN_MITER:
128             return VG_LITE_JOIN_MITER;
129         default:
130             return VG_LITE_JOIN_BEVEL;
131     }
132 }
133 
lv_vg_lite_stroke_get(struct _lv_draw_vg_lite_unit_t * unit,struct _lv_vg_lite_path_t * path,const lv_vector_stroke_dsc_t * dsc)134 lv_cache_entry_t * lv_vg_lite_stroke_get(struct _lv_draw_vg_lite_unit_t * unit,
135                                          struct _lv_vg_lite_path_t * path,
136                                          const lv_vector_stroke_dsc_t * dsc)
137 {
138     LV_ASSERT_NULL(unit);
139     LV_ASSERT_NULL(path);
140     LV_ASSERT_NULL(dsc);
141 
142     vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(path);
143 
144     if(vg_path->format != VG_LITE_FP32) {
145         LV_LOG_ERROR("only support VG_LITE_FP32 format");
146         return NULL;
147     }
148 
149     /* prepare search key */
150     stroke_item_t search_key;
151     lv_memzero(&search_key, sizeof(search_key));
152     search_key.lv.cap = dsc->cap;
153     search_key.lv.join = dsc->join;
154     search_key.lv.width = dsc->width;
155     search_key.lv.miter_limit = dsc->miter_limit;
156 
157     /* A one-time read-only array that only copies the pointer but not the content */
158     search_key.lv.dash_pattern = dsc->dash_pattern;
159     search_key.lv.path = path;
160 
161     lv_cache_entry_t * cache_node_entry = lv_cache_acquire(unit->stroke_cache, &search_key, NULL);
162     if(cache_node_entry) {
163         return cache_node_entry;
164     }
165 
166     cache_node_entry = lv_cache_acquire_or_create(unit->stroke_cache, &search_key, NULL);
167     if(cache_node_entry == NULL) {
168         LV_LOG_ERROR("stroke cache creating failed");
169         return NULL;
170     }
171 
172     return cache_node_entry;
173 }
174 
lv_vg_lite_stroke_get_path(lv_cache_entry_t * cache_entry)175 struct _lv_vg_lite_path_t * lv_vg_lite_stroke_get_path(lv_cache_entry_t * cache_entry)
176 {
177     LV_ASSERT_NULL(cache_entry);
178 
179     stroke_item_t * stroke_item = lv_cache_entry_get_data(cache_entry);
180     LV_ASSERT_NULL(stroke_item);
181 
182     if(lv_array_size(&stroke_item->vg.dash_pattern)) {
183         /* check if dash pattern must be duped */
184         LV_ASSERT(stroke_item->dash_pattern_type == DASH_PATTERN_TYPE_VG);
185     }
186 
187     return stroke_item->vg.path;
188 }
189 
lv_vg_lite_stroke_drop(struct _lv_draw_vg_lite_unit_t * unit,lv_cache_entry_t * cache_entry)190 void lv_vg_lite_stroke_drop(struct _lv_draw_vg_lite_unit_t * unit,
191                             lv_cache_entry_t * cache_entry)
192 {
193     LV_ASSERT_NULL(unit);
194     LV_ASSERT_NULL(cache_entry);
195     lv_cache_release(unit->stroke_cache, cache_entry, NULL);
196 }
197 
198 /**********************
199  *   STATIC FUNCTIONS
200  **********************/
201 
stroke_create_cb(stroke_item_t * item,void * user_data)202 static bool stroke_create_cb(stroke_item_t * item, void * user_data)
203 {
204     LV_UNUSED(user_data);
205     LV_ASSERT_NULL(item);
206 
207     /* Check if stroke width is valid */
208     if(item->lv.width <= 0) {
209         LV_LOG_WARN("stroke width error: %f", item->lv.width);
210         return false;
211     }
212 
213     /* Reset the dash pattern type */
214     item->dash_pattern_type = DASH_PATTERN_TYPE_LV;
215 
216     /* dup the path */
217     item->vg.path = lv_vg_lite_path_create(VG_LITE_FP32);
218     lv_vg_lite_path_append_path(item->vg.path, item->lv.path);
219 
220     /* dup the dash pattern */
221     vg_lite_float_t * vg_dash_pattern = NULL;
222     const uint32_t size = lv_array_size(&item->lv.dash_pattern);
223     if(size) {
224         /* Only support float dash pattern */
225         LV_ASSERT(item->lv.dash_pattern.element_size == sizeof(float));
226         lv_array_init(&item->vg.dash_pattern, size, sizeof(float));
227         lv_array_copy(&item->vg.dash_pattern, &item->lv.dash_pattern);
228 
229         /* mark dash pattern has been duped */
230         item->dash_pattern_type = DASH_PATTERN_TYPE_VG;
231         vg_dash_pattern = lv_array_front(&item->vg.dash_pattern);
232     }
233 
234     /* update parameters */
235     vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(item->vg.path);
236     LV_VG_LITE_CHECK_ERROR(vg_lite_set_path_type(vg_path, VG_LITE_DRAW_STROKE_PATH));
237 
238     vg_lite_error_t error = vg_lite_set_stroke(
239                                 vg_path,
240                                 lv_stroke_cap_to_vg(item->lv.cap),
241                                 lv_stroke_join_to_vg(item->lv.join),
242                                 item->lv.width,
243                                 item->lv.miter_limit,
244                                 vg_dash_pattern,
245                                 size,
246                                 item->lv.width / 2,
247                                 0);
248 
249     if(error != VG_LITE_SUCCESS) {
250         LV_LOG_ERROR("vg_lite_set_stroke error: %d", (int)error);
251         lv_vg_lite_error_dump_info(error);
252         stroke_free_cb(item, NULL);
253         return false;
254     }
255 
256     const vg_lite_pointer * ori_path = vg_path->path;
257     const vg_lite_uint32_t ori_path_length = vg_path->path_length;
258 
259     LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_update_stroke");
260     error = vg_lite_update_stroke(vg_path);
261     LV_PROFILER_DRAW_END_TAG("vg_lite_update_stroke");
262 
263     /* check if path is changed */
264     LV_ASSERT_MSG(vg_path->path_length == ori_path_length, "vg_path->path_length should not change");
265     LV_ASSERT_MSG(vg_path->path == ori_path, "vg_path->path should not change");
266 
267     if(error != VG_LITE_SUCCESS) {
268         LV_LOG_ERROR("vg_lite_update_stroke error: %d", (int)error);
269         lv_vg_lite_error_dump_info(error);
270         stroke_free_cb(item, NULL);
271         return false;
272     }
273 
274     return true;
275 }
276 
stroke_free_cb(stroke_item_t * item,void * user_data)277 static void stroke_free_cb(stroke_item_t * item, void * user_data)
278 {
279     LV_UNUSED(user_data);
280     LV_ASSERT_NULL(item);
281 
282     if(item->vg.path) {
283         lv_vg_lite_path_destroy(item->vg.path);
284         item->vg.path = NULL;
285     }
286 
287     if(item->dash_pattern_type == DASH_PATTERN_TYPE_VG) {
288         lv_array_deinit(&item->vg.dash_pattern);
289         item->dash_pattern_type = DASH_PATTERN_TYPE_LV;
290     }
291 }
292 
dash_pattern_compare(const stroke_item_t * lhs,const stroke_item_t * rhs)293 static lv_cache_compare_res_t dash_pattern_compare(const stroke_item_t * lhs, const stroke_item_t * rhs)
294 {
295     /* Select the dash pattern to compare */
296     const lv_array_t * lhs_dash_pattern = lhs->dash_pattern_type == DASH_PATTERN_TYPE_LV ?
297                                           &lhs->lv.dash_pattern :
298                                           &lhs->vg.dash_pattern;
299     const lv_array_t * rhs_dash_pattern = rhs->dash_pattern_type == DASH_PATTERN_TYPE_LV ?
300                                           &rhs->lv.dash_pattern :
301                                           &rhs->vg.dash_pattern;
302 
303     const uint32_t lhs_dash_pattern_size = lv_array_size(lhs_dash_pattern);
304     const uint32_t rhs_dash_pattern_size = lv_array_size(rhs_dash_pattern);
305 
306     if(lhs_dash_pattern_size != rhs_dash_pattern_size) {
307         return lhs_dash_pattern_size > rhs_dash_pattern_size ? 1 : -1;
308     }
309 
310     if(lhs_dash_pattern_size == 0 && rhs_dash_pattern_size == 0) {
311         return 0;
312     }
313 
314     /* Both dash pattern has the same size, compare them */
315     LV_ASSERT(lhs_dash_pattern->element_size == sizeof(float));
316     LV_ASSERT(rhs_dash_pattern->element_size == sizeof(float));
317 
318     /* compare dash pattern data */
319     int cmp_res = lv_memcmp(
320                       lv_array_front(lhs_dash_pattern),
321                       lv_array_front(rhs_dash_pattern),
322                       lhs_dash_pattern_size * sizeof(float));
323 
324     if(cmp_res != 0) {
325         return cmp_res > 0 ? 1 : -1;
326     }
327 
328     return 0;
329 }
330 
path_compare(const stroke_item_t * lhs,const stroke_item_t * rhs)331 static lv_cache_compare_res_t path_compare(const stroke_item_t * lhs, const stroke_item_t * rhs)
332 {
333     /* Give priority to using dup vg.path */
334     const vg_lite_path_t * lhs_path = lhs->vg.path ?
335                                       lv_vg_lite_path_get_path(lhs->vg.path) :
336                                       lv_vg_lite_path_get_path(lhs->lv.path);
337     const vg_lite_path_t * rhs_path = rhs->vg.path ?
338                                       lv_vg_lite_path_get_path(rhs->vg.path) :
339                                       lv_vg_lite_path_get_path(rhs->lv.path);
340 
341     LV_ASSERT(lhs_path->format == VG_LITE_FP32);
342     LV_ASSERT(rhs_path->format == VG_LITE_FP32);
343 
344     if(lhs_path->path_length != rhs_path->path_length) {
345         return lhs_path->path_length > rhs_path->path_length ? 1 : -1;
346     }
347 
348     int cmp_res = lv_memcmp(lhs_path->path, rhs_path->path, lhs_path->path_length);
349     if(cmp_res != 0) {
350         return cmp_res > 0 ? 1 : -1;
351     }
352 
353     return 0;
354 }
355 
stroke_compare_cb(const stroke_item_t * lhs,const stroke_item_t * rhs)356 static lv_cache_compare_res_t stroke_compare_cb(const stroke_item_t * lhs, const stroke_item_t * rhs)
357 {
358     if(lhs->lv.width != rhs->lv.width) {
359         return lhs->lv.width > rhs->lv.width ? 1 : -1;
360     }
361 
362     if(lhs->lv.cap != rhs->lv.cap) {
363         return lhs->lv.cap > rhs->lv.cap ? 1 : -1;
364     }
365 
366     if(lhs->lv.join != rhs->lv.join) {
367         return lhs->lv.join > rhs->lv.join ? 1 : -1;
368     }
369 
370     if(lhs->lv.miter_limit != rhs->lv.miter_limit) {
371         return lhs->lv.miter_limit > rhs->lv.miter_limit ? 1 : -1;
372     }
373 
374     lv_cache_compare_res_t dash_pattern_res = dash_pattern_compare(lhs, rhs);
375     if(dash_pattern_res != 0) {
376         return dash_pattern_res;
377     }
378 
379     return path_compare(lhs, rhs);
380 }
381 
382 #endif /*LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC*/
383