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