1 /**
2  * @file lv_mask.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw.h"
10 #if LV_DRAW_COMPLEX
11 #include "../misc/lv_math.h"
12 #include "../misc/lv_log.h"
13 #include "../misc/lv_assert.h"
14 #include "../misc/lv_gc.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define CIRCLE_CACHE_LIFE_MAX   1000
20 #define CIRCLE_CACHE_AGING(life, r)   life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
30                                                                         lv_coord_t abs_y, lv_coord_t len,
31                                                                         lv_draw_mask_line_param_t * param);
32 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
33                                                                           lv_coord_t abs_y, lv_coord_t len,
34                                                                           lv_draw_mask_radius_param_t * param);
35 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
36                                                                          lv_coord_t abs_y, lv_coord_t len,
37                                                                          lv_draw_mask_angle_param_t * param);
38 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
39                                                                         lv_coord_t abs_y, lv_coord_t len,
40                                                                         lv_draw_mask_fade_param_t * param);
41 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
42                                                                        lv_coord_t abs_y, lv_coord_t len,
43                                                                        lv_draw_mask_map_param_t * param);
44 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_polygon(lv_opa_t * mask_buf, lv_coord_t abs_x,
45                                                                            lv_coord_t abs_y, lv_coord_t len,
46                                                                            lv_draw_mask_polygon_param_t * param);
47 
48 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ line_mask_flat(lv_opa_t * mask_buf, lv_coord_t abs_x,
49                                                                      lv_coord_t abs_y, lv_coord_t len,
50                                                                      lv_draw_mask_line_param_t * p);
51 static lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ line_mask_steep(lv_opa_t * mask_buf, lv_coord_t abs_x,
52                                                                       lv_coord_t abs_y, lv_coord_t len,
53                                                                       lv_draw_mask_line_param_t * p);
54 
55 static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius);
56 static bool circ_cont(lv_point_t * c);
57 static void circ_next(lv_point_t * c, lv_coord_t * tmp);
58 static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius);
59 static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len,
60                                 lv_coord_t * x_start);
61 static inline lv_opa_t /* LV_ATTRIBUTE_FAST_MEM */ mask_mix(lv_opa_t mask_act, lv_opa_t mask_new);
62 
63 /**********************
64  *  STATIC VARIABLES
65  **********************/
66 
67 /**********************
68  *      MACROS
69  **********************/
70 
71 /**********************
72  *   GLOBAL FUNCTIONS
73  **********************/
74 
75 /**
76  * Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
77  * @param param an initialized mask parameter. Only the pointer is saved.
78  * @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
79  * @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
80  */
lv_draw_mask_add(void * param,void * custom_id)81 int16_t lv_draw_mask_add(void * param, void * custom_id)
82 {
83     /*Look for a free entry*/
84     uint8_t i;
85     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
86         if(LV_GC_ROOT(_lv_draw_mask_list[i]).param == NULL) break;
87     }
88 
89     if(i >= _LV_MASK_MAX_NUM) {
90         LV_LOG_WARN("lv_mask_add: no place to add the mask");
91         return LV_MASK_ID_INV;
92     }
93 
94     LV_GC_ROOT(_lv_draw_mask_list[i]).param = param;
95     LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id = custom_id;
96 
97     return i;
98 }
99 
100 /**
101  * Apply the added buffers on a line. Used internally by the library's drawing routines.
102  * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
103  * @param abs_x absolute X coordinate where the line to calculate start
104  * @param abs_y absolute Y coordinate where the line to calculate start
105  * @param len length of the line to calculate (in pixel count)
106  * @return One of these values:
107  * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
108  * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
109  * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
110  */
lv_draw_mask_apply(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len)111 lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x,
112                                                             lv_coord_t abs_y, lv_coord_t len)
113 {
114     bool changed = false;
115     _lv_draw_mask_common_dsc_t * dsc;
116 
117     _lv_draw_mask_saved_t * m = LV_GC_ROOT(_lv_draw_mask_list);
118 
119     while(m->param) {
120         dsc = m->param;
121         lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER;
122         res = dsc->cb(mask_buf, abs_x, abs_y, len, (void *)m->param);
123         if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
124         else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true;
125 
126         m++;
127     }
128 
129     return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
130 }
131 
132 /**
133  * Apply the specified buffers on a line. Used internally by the library's drawing routines.
134  * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
135  * @param abs_x absolute X coordinate where the line to calculate start
136  * @param abs_y absolute Y coordinate where the line to calculate start
137  * @param len length of the line to calculate (in pixel count)
138  * @param ids ID array of added buffers
139  * @param ids_count number of ID array
140  * @return One of these values:
141  * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
142  * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
143  * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
144  */
lv_draw_mask_apply_ids(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,const int16_t * ids,int16_t ids_count)145 lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x,
146                                                                 lv_coord_t abs_y, lv_coord_t len,
147                                                                 const int16_t * ids, int16_t ids_count)
148 {
149     bool changed = false;
150     _lv_draw_mask_common_dsc_t * dsc;
151 
152     for(int i = 0; i < ids_count; i++) {
153         int16_t id = ids[i];
154         if(id == LV_MASK_ID_INV) continue;
155         dsc = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
156         if(!dsc) continue;
157         lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER;
158         res = dsc->cb(mask_buf, abs_x, abs_y, len, dsc);
159         if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
160         else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true;
161     }
162 
163     return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
164 }
165 
166 /**
167  * Remove a mask with a given ID
168  * @param id the ID of the mask.  Returned by `lv_draw_mask_add`
169  * @return the parameter of the removed mask.
170  * If more masks have `custom_id` ID then the last mask's parameter will be returned
171  */
lv_draw_mask_remove_id(int16_t id)172 void * lv_draw_mask_remove_id(int16_t id)
173 {
174     _lv_draw_mask_common_dsc_t * p = NULL;
175 
176     if(id != LV_MASK_ID_INV) {
177         p = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
178         LV_GC_ROOT(_lv_draw_mask_list[id]).param = NULL;
179         LV_GC_ROOT(_lv_draw_mask_list[id]).custom_id = NULL;
180     }
181 
182     return p;
183 }
184 
185 /**
186  * Remove all mask with a given custom ID
187  * @param custom_id a pointer used in `lv_draw_mask_add`
188  * @return return the parameter of the removed mask.
189  * If more masks have `custom_id` ID then the last mask's parameter will be returned
190  */
lv_draw_mask_remove_custom(void * custom_id)191 void * lv_draw_mask_remove_custom(void * custom_id)
192 {
193     _lv_draw_mask_common_dsc_t * p = NULL;
194     uint8_t i;
195     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
196         if(LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id == custom_id) {
197             p = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
198             lv_draw_mask_remove_id(i);
199         }
200     }
201     return p;
202 }
203 
204 /**
205  * Free the data from the parameter.
206  * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
207  * Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
208  * and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
209  * @param p pointer to a mask parameter
210  */
lv_draw_mask_free_param(void * p)211 void lv_draw_mask_free_param(void * p)
212 {
213     _lv_draw_mask_common_dsc_t * pdsc = p;
214     if(pdsc->type == LV_DRAW_MASK_TYPE_RADIUS) {
215         lv_draw_mask_radius_param_t * radius_p = (lv_draw_mask_radius_param_t *) p;
216         if(radius_p->circle) {
217             if(radius_p->circle->life < 0) {
218                 lv_mem_free(radius_p->circle->cir_opa);
219                 lv_mem_free(radius_p->circle);
220             }
221             else {
222                 radius_p->circle->used_cnt--;
223             }
224         }
225     }
226     else if(pdsc->type == LV_DRAW_MASK_TYPE_POLYGON) {
227         lv_draw_mask_polygon_param_t * poly_p = (lv_draw_mask_polygon_param_t *) p;
228         lv_mem_free(poly_p->cfg.points);
229     }
230 }
231 
_lv_draw_mask_cleanup(void)232 void _lv_draw_mask_cleanup(void)
233 {
234     uint8_t i;
235     for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
236         if(LV_GC_ROOT(_lv_circle_cache[i]).buf) {
237             lv_mem_free(LV_GC_ROOT(_lv_circle_cache[i]).buf);
238         }
239         lv_memset_00(&LV_GC_ROOT(_lv_circle_cache[i]), sizeof(LV_GC_ROOT(_lv_circle_cache[i])));
240     }
241 }
242 
243 /**
244  * Count the currently added masks
245  * @return number of active masks
246  */
lv_draw_mask_get_cnt(void)247 uint8_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_get_cnt(void)
248 {
249     uint8_t cnt = 0;
250     uint8_t i;
251     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
252         if(LV_GC_ROOT(_lv_draw_mask_list[i]).param) cnt++;
253     }
254     return cnt;
255 }
256 
lv_draw_mask_is_any(const lv_area_t * a)257 bool lv_draw_mask_is_any(const lv_area_t * a)
258 {
259     if(a == NULL) return LV_GC_ROOT(_lv_draw_mask_list[0]).param ? true : false;
260 
261     uint8_t i;
262     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
263         _lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
264         if(comm_param == NULL) continue;
265         if(comm_param->type == LV_DRAW_MASK_TYPE_RADIUS) {
266             lv_draw_mask_radius_param_t * radius_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
267             if(radius_param->cfg.outer) {
268                 if(!_lv_area_is_out(a, &radius_param->cfg.rect, radius_param->cfg.radius)) return true;
269             }
270             else {
271                 if(!_lv_area_is_in(a, &radius_param->cfg.rect, radius_param->cfg.radius)) return true;
272             }
273         }
274         else {
275             return true;
276         }
277     }
278 
279     return false;
280 
281 }
282 
283 /**
284  *Initialize a line mask from two points.
285  * @param param pointer to a `lv_draw_mask_param_t` to initialize
286  * @param p1x X coordinate of the first point of the line
287  * @param p1y Y coordinate of the first point of the line
288  * @param p2x X coordinate of the second point of the line
289  * @param p2y y coordinate of the second point of the line
290  * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
291  * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
292  * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
293  */
lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param,lv_coord_t p1x,lv_coord_t p1y,lv_coord_t p2x,lv_coord_t p2y,lv_draw_mask_line_side_t side)294 void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
295                                    lv_coord_t p2y, lv_draw_mask_line_side_t side)
296 {
297     lv_memset_00(param, sizeof(lv_draw_mask_line_param_t));
298 
299     if(p1y == p2y && side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) {
300         p1y--;
301         p2y--;
302     }
303 
304     if(p1y > p2y) {
305         lv_coord_t t;
306         t = p2x;
307         p2x = p1x;
308         p1x = t;
309 
310         t = p2y;
311         p2y = p1y;
312         p1y = t;
313     }
314 
315     param->cfg.p1.x = p1x;
316     param->cfg.p1.y = p1y;
317     param->cfg.p2.x = p2x;
318     param->cfg.p2.y = p2y;
319     param->cfg.side = side;
320 
321     param->origo.x = p1x;
322     param->origo.y = p1y;
323     param->flat = (LV_ABS(p2x - p1x) > LV_ABS(p2y - p1y)) ? 1 : 0;
324     param->yx_steep = 0;
325     param->xy_steep = 0;
326     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_line;
327     param->dsc.type = LV_DRAW_MASK_TYPE_LINE;
328 
329     int32_t dx = p2x - p1x;
330     int32_t dy = p2y - p1y;
331 
332     if(param->flat) {
333         /*Normalize the steep. Delta x should be relative to delta x = 1024*/
334         int32_t m;
335 
336         if(dx) {
337             m = (1L << 20) / dx;  /*m is multiplier to normalize y (upscaled by 1024)*/
338             param->yx_steep = (m * dy) >> 10;
339         }
340 
341         if(dy) {
342             m = (1L << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
343             param->xy_steep = (m * dx) >> 10;
344         }
345         param->steep = param->yx_steep;
346     }
347     else {
348         /*Normalize the steep. Delta y should be relative to delta x = 1024*/
349         int32_t m;
350 
351         if(dy) {
352             m = (1L << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
353             param->xy_steep = (m * dx) >> 10;
354         }
355 
356         if(dx) {
357             m = (1L << 20) / dx;  /*m is multiplier to normalize x (upscaled by 1024)*/
358             param->yx_steep = (m * dy) >> 10;
359         }
360         param->steep = param->xy_steep;
361     }
362 
363     if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) param->inv = 0;
364     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) param->inv = 1;
365     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP) {
366         if(param->steep > 0) param->inv = 1;
367         else param->inv = 0;
368     }
369     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) {
370         if(param->steep > 0) param->inv = 0;
371         else param->inv = 1;
372     }
373 
374     param->spx = param->steep >> 2;
375     if(param->steep < 0) param->spx = -param->spx;
376 }
377 
378 /**
379  *Initialize a line mask from a point and an angle.
380  * @param param pointer to a `lv_draw_mask_param_t` to initialize
381  * @param px X coordinate of a point of the line
382  * @param py X coordinate of a point of the line
383  * @param angle right 0 deg, bottom: 90
384  * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
385  * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
386  * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
387  */
lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param,lv_coord_t p1x,lv_coord_t py,int16_t angle,lv_draw_mask_line_side_t side)388 void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
389                                   lv_draw_mask_line_side_t side)
390 {
391     /*Find an optimal degree.
392      *lv_mask_line_points_init will swap the points to keep the smaller y in p1
393      *Theoretically a line with `angle` or `angle+180` is the same only the points are swapped
394      *Find the degree which keeps the origo in place*/
395     if(angle > 180) angle -= 180; /*> 180 will swap the origo*/
396 
397     int32_t p2x;
398     int32_t p2y;
399 
400     p2x = (lv_trigo_sin(angle + 90) >> 5) + p1x;
401     p2y = (lv_trigo_sin(angle) >> 5) + py;
402 
403     lv_draw_mask_line_points_init(param, p1x, py, p2x, p2y, side);
404 }
405 
406 /**
407  * Initialize an angle mask.
408  * @param param pointer to a `lv_draw_mask_param_t` to initialize
409  * @param vertex_x X coordinate of the angle vertex (absolute coordinates)
410  * @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
411  * @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
412  * @param end_angle end angle
413  */
lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param,lv_coord_t vertex_x,lv_coord_t vertex_y,lv_coord_t start_angle,lv_coord_t end_angle)414 void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
415                              lv_coord_t start_angle, lv_coord_t end_angle)
416 {
417     lv_draw_mask_line_side_t start_side;
418     lv_draw_mask_line_side_t end_side;
419 
420     /*Constrain the input angles*/
421     if(start_angle < 0)
422         start_angle = 0;
423     else if(start_angle > 359)
424         start_angle = 359;
425 
426     if(end_angle < 0)
427         end_angle = 0;
428     else if(end_angle > 359)
429         end_angle = 359;
430 
431     if(end_angle < start_angle) {
432         param->delta_deg = 360 - start_angle + end_angle;
433     }
434     else {
435         param->delta_deg = LV_ABS(end_angle - start_angle);
436     }
437 
438     param->cfg.start_angle = start_angle;
439     param->cfg.end_angle = end_angle;
440     param->cfg.vertex_p.x = vertex_x;
441     param->cfg.vertex_p.y = vertex_y;
442     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_angle;
443     param->dsc.type = LV_DRAW_MASK_TYPE_ANGLE;
444 
445     LV_ASSERT_MSG(start_angle >= 0 && start_angle <= 360, "Unexpected start angle");
446 
447     if(start_angle >= 0 && start_angle < 180) {
448         start_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
449     }
450     else
451         start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
452 
453     LV_ASSERT_MSG(end_angle >= 0 && start_angle <= 360, "Unexpected end angle");
454 
455     if(end_angle >= 0 && end_angle < 180) {
456         end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT;
457     }
458     else if(end_angle >= 180 && end_angle < 360) {
459         end_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
460     }
461     else
462         end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
463 
464     lv_draw_mask_line_angle_init(&param->start_line, vertex_x, vertex_y, start_angle, start_side);
465     lv_draw_mask_line_angle_init(&param->end_line, vertex_x, vertex_y, end_angle, end_side);
466 }
467 
468 /**
469  * Initialize a fade mask.
470  * @param param pointer to an `lv_draw_mask_radius_param_t` to initialize
471  * @param rect coordinates of the rectangle to affect (absolute coordinates)
472  * @param radius radius of the rectangle
473  * @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
474  */
lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param,const lv_area_t * rect,lv_coord_t radius,bool inv)475 void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv)
476 {
477     lv_coord_t w = lv_area_get_width(rect);
478     lv_coord_t h = lv_area_get_height(rect);
479     int32_t short_side = LV_MIN(w, h);
480     if(radius > short_side >> 1) radius = short_side >> 1;
481     if(radius < 0) radius = 0;
482 
483     lv_area_copy(&param->cfg.rect, rect);
484     param->cfg.radius = radius;
485     param->cfg.outer = inv ? 1 : 0;
486     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_radius;
487     param->dsc.type = LV_DRAW_MASK_TYPE_RADIUS;
488 
489     if(radius == 0) {
490         param->circle = NULL;
491         return;
492     }
493 
494     uint32_t i;
495 
496     /*Try to reuse a circle cache entry*/
497     for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
498         if(LV_GC_ROOT(_lv_circle_cache[i]).radius == radius) {
499             LV_GC_ROOT(_lv_circle_cache[i]).used_cnt++;
500             CIRCLE_CACHE_AGING(LV_GC_ROOT(_lv_circle_cache[i]).life, radius);
501             param->circle = &LV_GC_ROOT(_lv_circle_cache[i]);
502             return;
503         }
504     }
505 
506     /*If not found find a free entry with lowest life*/
507     _lv_draw_mask_radius_circle_dsc_t * entry = NULL;
508     for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
509         if(LV_GC_ROOT(_lv_circle_cache[i]).used_cnt == 0) {
510             if(!entry) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
511             else if(LV_GC_ROOT(_lv_circle_cache[i]).life < entry->life) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
512         }
513     }
514 
515     if(!entry) {
516         entry = lv_mem_alloc(sizeof(_lv_draw_mask_radius_circle_dsc_t));
517         LV_ASSERT_MALLOC(entry);
518         lv_memset_00(entry, sizeof(_lv_draw_mask_radius_circle_dsc_t));
519         entry->life = -1;
520     }
521     else {
522         entry->used_cnt++;
523         entry->life = 0;
524         CIRCLE_CACHE_AGING(entry->life, radius);
525     }
526 
527     param->circle = entry;
528 
529     circ_calc_aa4(param->circle, radius);
530 }
531 
532 /**
533  * Initialize a fade mask.
534  * @param param pointer to a `lv_draw_mask_param_t` to initialize
535  * @param coords coordinates of the area to affect (absolute coordinates)
536  * @param opa_top opacity on the top
537  * @param y_top at which coordinate start to change to opacity to `opa_bottom`
538  * @param opa_bottom opacity at the bottom
539  * @param y_bottom at which coordinate reach `opa_bottom`.
540  */
lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param,const lv_area_t * coords,lv_opa_t opa_top,lv_coord_t y_top,lv_opa_t opa_bottom,lv_coord_t y_bottom)541 void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
542                             lv_coord_t y_top,
543                             lv_opa_t opa_bottom, lv_coord_t y_bottom)
544 {
545     lv_area_copy(&param->cfg.coords, coords);
546     param->cfg.opa_top = opa_top;
547     param->cfg.opa_bottom = opa_bottom;
548     param->cfg.y_top = y_top;
549     param->cfg.y_bottom = y_bottom;
550     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_fade;
551     param->dsc.type = LV_DRAW_MASK_TYPE_FADE;
552 }
553 
554 /**
555  * Initialize a map mask.
556  * @param param pointer to a `lv_draw_mask_param_t` to initialize
557  * @param coords coordinates of the map (absolute coordinates)
558  * @param map array of bytes with the mask values
559  */
lv_draw_mask_map_init(lv_draw_mask_map_param_t * param,const lv_area_t * coords,const lv_opa_t * map)560 void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map)
561 {
562     lv_area_copy(&param->cfg.coords, coords);
563     param->cfg.map = map;
564     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_map;
565     param->dsc.type = LV_DRAW_MASK_TYPE_MAP;
566 }
567 
lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param,const lv_point_t * points,uint16_t point_cnt)568 void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt)
569 {
570     /*Join adjacent points if they are on the same coordinate*/
571     lv_point_t * p = lv_mem_alloc(point_cnt * sizeof(lv_point_t));
572     if(p == NULL) return;
573     uint16_t i;
574     uint16_t pcnt = 0;
575     p[0] = points[0];
576     for(i = 0; i < point_cnt - 1; i++) {
577         if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
578             p[pcnt] = points[i];
579             pcnt++;
580         }
581     }
582     /*The first and the last points are also adjacent*/
583     if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
584         p[pcnt] = points[point_cnt - 1];
585         pcnt++;
586     }
587     param->cfg.points = p;
588     param->cfg.point_cnt = pcnt;
589     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_polygon;
590     param->dsc.type = LV_DRAW_MASK_TYPE_POLYGON;
591 }
592 
593 /**********************
594  *   STATIC FUNCTIONS
595  **********************/
596 
lv_draw_mask_line(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_line_param_t * p)597 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
598                                                                   lv_coord_t abs_y, lv_coord_t len,
599                                                                   lv_draw_mask_line_param_t * p)
600 {
601     /*Make to points relative to the vertex*/
602     abs_y -= p->origo.y;
603     abs_x -= p->origo.x;
604 
605     /*Handle special cases*/
606     if(p->steep == 0) {
607         /*Horizontal*/
608         if(p->flat) {
609             /*Non sense: Can't be on the right/left of a horizontal line*/
610             if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT ||
611                p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) return LV_DRAW_MASK_RES_FULL_COVER;
612             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP && abs_y + 1 < 0) return LV_DRAW_MASK_RES_FULL_COVER;
613             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM && abs_y > 0) return LV_DRAW_MASK_RES_FULL_COVER;
614             else {
615                 return LV_DRAW_MASK_RES_TRANSP;
616             }
617         }
618         /*Vertical*/
619         else {
620             /*Non sense: Can't be on the top/bottom of a vertical line*/
621             if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP ||
622                p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) return LV_DRAW_MASK_RES_FULL_COVER;
623             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT && abs_x > 0) return LV_DRAW_MASK_RES_FULL_COVER;
624             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) {
625                 if(abs_x + len < 0) return LV_DRAW_MASK_RES_FULL_COVER;
626                 else {
627                     int32_t k = - abs_x;
628                     if(k < 0) return LV_DRAW_MASK_RES_TRANSP;
629                     if(k >= 0 && k < len) lv_memset_00(&mask_buf[k], len - k);
630                     return  LV_DRAW_MASK_RES_CHANGED;
631                 }
632             }
633             else {
634                 if(abs_x + len < 0) return LV_DRAW_MASK_RES_TRANSP;
635                 else {
636                     int32_t k = - abs_x;
637                     if(k < 0) k = 0;
638                     if(k >= len) return LV_DRAW_MASK_RES_TRANSP;
639                     else if(k >= 0 && k < len) lv_memset_00(&mask_buf[0], k);
640                     return  LV_DRAW_MASK_RES_CHANGED;
641                 }
642             }
643         }
644     }
645 
646     lv_draw_mask_res_t res;
647     if(p->flat) {
648         res = line_mask_flat(mask_buf, abs_x, abs_y, len, p);
649     }
650     else {
651         res = line_mask_steep(mask_buf, abs_x, abs_y, len, p);
652     }
653 
654     return res;
655 }
656 
line_mask_flat(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_line_param_t * p)657 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM line_mask_flat(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
658                                                                lv_coord_t len,
659                                                                lv_draw_mask_line_param_t * p)
660 {
661 
662     int32_t y_at_x;
663     y_at_x = (int32_t)((int32_t)p->yx_steep * abs_x) >> 10;
664 
665     if(p->yx_steep > 0) {
666         if(y_at_x > abs_y) {
667             if(p->inv) {
668                 return LV_DRAW_MASK_RES_FULL_COVER;
669             }
670             else {
671                 return LV_DRAW_MASK_RES_TRANSP;
672             }
673         }
674     }
675     else {
676         if(y_at_x < abs_y) {
677             if(p->inv) {
678                 return LV_DRAW_MASK_RES_FULL_COVER;
679             }
680             else {
681                 return LV_DRAW_MASK_RES_TRANSP;
682             }
683         }
684     }
685 
686     /*At the end of the mask if the limit line is smaller than the mask's y.
687      *Then the mask is in the "good" area*/
688     y_at_x = (int32_t)((int32_t)p->yx_steep * (abs_x + len)) >> 10;
689     if(p->yx_steep > 0) {
690         if(y_at_x < abs_y) {
691             if(p->inv) {
692                 return LV_DRAW_MASK_RES_TRANSP;
693             }
694             else {
695                 return LV_DRAW_MASK_RES_FULL_COVER;
696             }
697         }
698     }
699     else {
700         if(y_at_x > abs_y) {
701             if(p->inv) {
702                 return LV_DRAW_MASK_RES_TRANSP;
703             }
704             else {
705                 return LV_DRAW_MASK_RES_FULL_COVER;
706             }
707         }
708     }
709 
710     int32_t xe;
711     if(p->yx_steep > 0) xe = ((abs_y * 256) * p->xy_steep) >> 10;
712     else xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
713 
714     int32_t xei = xe >> 8;
715     int32_t xef = xe & 0xFF;
716 
717     int32_t px_h;
718     if(xef == 0) px_h = 255;
719     else px_h = 255 - (((255 - xef) * p->spx) >> 8);
720     int32_t k = xei - abs_x;
721     lv_opa_t m;
722 
723     if(xef) {
724         if(k >= 0 && k < len) {
725             m = 255 - (((255 - xef) * (255 - px_h)) >> 9);
726             if(p->inv) m = 255 - m;
727             mask_buf[k] = mask_mix(mask_buf[k], m);
728         }
729         k++;
730     }
731 
732     while(px_h > p->spx) {
733         if(k >= 0 && k < len) {
734             m = px_h - (p->spx >> 1);
735             if(p->inv) m = 255 - m;
736             mask_buf[k] = mask_mix(mask_buf[k], m);
737         }
738         px_h -= p->spx;
739         k++;
740         if(k >= len) break;
741     }
742 
743     if(k < len && k >= 0) {
744         int32_t x_inters = (px_h * p->xy_steep) >> 10;
745         m = (x_inters * px_h) >> 9;
746         if(p->yx_steep < 0) m = 255 - m;
747         if(p->inv) m = 255 - m;
748         mask_buf[k] = mask_mix(mask_buf[k], m);
749     }
750 
751     if(p->inv) {
752         k = xei - abs_x;
753         if(k > len) {
754             return LV_DRAW_MASK_RES_TRANSP;
755         }
756         if(k >= 0) {
757             lv_memset_00(&mask_buf[0], k);
758         }
759     }
760     else {
761         k++;
762         if(k < 0) {
763             return LV_DRAW_MASK_RES_TRANSP;
764         }
765         if(k <= len) {
766             lv_memset_00(&mask_buf[k], len - k);
767         }
768     }
769 
770     return LV_DRAW_MASK_RES_CHANGED;
771 }
772 
line_mask_steep(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_line_param_t * p)773 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM line_mask_steep(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
774                                                                 lv_coord_t len,
775                                                                 lv_draw_mask_line_param_t * p)
776 {
777     int32_t k;
778     int32_t x_at_y;
779     /*At the beginning of the mask if the limit line is greater than the mask's y.
780      *Then the mask is in the "wrong" area*/
781     x_at_y = (int32_t)((int32_t)p->xy_steep * abs_y) >> 10;
782     if(p->xy_steep > 0) x_at_y++;
783     if(x_at_y < abs_x) {
784         if(p->inv) {
785             return LV_DRAW_MASK_RES_FULL_COVER;
786         }
787         else {
788             return LV_DRAW_MASK_RES_TRANSP;
789         }
790     }
791 
792     /*At the end of the mask if the limit line is smaller than the mask's y.
793      *Then the mask is in the "good" area*/
794     x_at_y = (int32_t)((int32_t)p->xy_steep * (abs_y)) >> 10;
795     if(x_at_y > abs_x + len) {
796         if(p->inv) {
797             return LV_DRAW_MASK_RES_TRANSP;
798         }
799         else {
800             return LV_DRAW_MASK_RES_FULL_COVER;
801         }
802     }
803 
804     /*X start*/
805     int32_t xs = ((abs_y * 256) * p->xy_steep) >> 10;
806     int32_t xsi = xs >> 8;
807     int32_t xsf = xs & 0xFF;
808 
809     /*X end*/
810     int32_t xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
811     int32_t xei = xe >> 8;
812     int32_t xef = xe & 0xFF;
813 
814     lv_opa_t m;
815 
816     k = xsi - abs_x;
817     if(xsi != xei && (p->xy_steep < 0 && xsf == 0)) {
818         xsf = 0xFF;
819         xsi = xei;
820         k--;
821     }
822 
823     if(xsi == xei) {
824         if(k >= 0 && k < len) {
825             m = (xsf + xef) >> 1;
826             if(p->inv) m = 255 - m;
827             mask_buf[k] = mask_mix(mask_buf[k], m);
828         }
829         k++;
830 
831         if(p->inv) {
832             k = xsi - abs_x;
833             if(k >= len) {
834                 return LV_DRAW_MASK_RES_TRANSP;
835             }
836             if(k >= 0) lv_memset_00(&mask_buf[0], k);
837 
838         }
839         else {
840             if(k > len) k = len;
841             if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
842             else if(k > 0) lv_memset_00(&mask_buf[k],  len - k);
843         }
844 
845     }
846     else {
847         int32_t y_inters;
848         if(p->xy_steep < 0) {
849             y_inters = (xsf * (-p->yx_steep)) >> 10;
850             if(k >= 0 && k < len) {
851                 m = (y_inters * xsf) >> 9;
852                 if(p->inv) m = 255 - m;
853                 mask_buf[k] = mask_mix(mask_buf[k], m);
854             }
855             k--;
856 
857             int32_t x_inters = ((255 - y_inters) * (-p->xy_steep)) >> 10;
858 
859             if(k >= 0 && k < len) {
860                 m = 255 - (((255 - y_inters) * x_inters) >> 9);
861                 if(p->inv) m = 255 - m;
862                 mask_buf[k] = mask_mix(mask_buf[k], m);
863             }
864 
865             k += 2;
866 
867             if(p->inv) {
868                 k = xsi - abs_x - 1;
869 
870                 if(k > len) k = len;
871                 else if(k > 0) lv_memset_00(&mask_buf[0],  k);
872 
873             }
874             else {
875                 if(k > len) return LV_DRAW_MASK_RES_FULL_COVER;
876                 if(k >= 0) lv_memset_00(&mask_buf[k],  len - k);
877             }
878 
879         }
880         else {
881             y_inters = ((255 - xsf) * p->yx_steep) >> 10;
882             if(k >= 0 && k < len) {
883                 m = 255 - ((y_inters * (255 - xsf)) >> 9);
884                 if(p->inv) m = 255 - m;
885                 mask_buf[k] = mask_mix(mask_buf[k], m);
886             }
887 
888             k++;
889 
890             int32_t x_inters = ((255 - y_inters) * p->xy_steep) >> 10;
891             if(k >= 0 && k < len) {
892                 m = ((255 - y_inters) * x_inters) >> 9;
893                 if(p->inv) m = 255 - m;
894                 mask_buf[k] = mask_mix(mask_buf[k], m);
895             }
896             k++;
897 
898             if(p->inv) {
899                 k = xsi - abs_x;
900                 if(k > len)  return LV_DRAW_MASK_RES_TRANSP;
901                 if(k >= 0) lv_memset_00(&mask_buf[0],  k);
902 
903             }
904             else {
905                 if(k > len) k = len;
906                 if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
907                 else if(k > 0) lv_memset_00(&mask_buf[k],  len - k);
908             }
909         }
910     }
911 
912     return LV_DRAW_MASK_RES_CHANGED;
913 }
914 
lv_draw_mask_angle(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_angle_param_t * p)915 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
916                                                                    lv_coord_t abs_y, lv_coord_t len,
917                                                                    lv_draw_mask_angle_param_t * p)
918 {
919     int32_t rel_y = abs_y - p->cfg.vertex_p.y;
920     int32_t rel_x = abs_x - p->cfg.vertex_p.x;
921 
922     if(p->cfg.start_angle < 180 && p->cfg.end_angle < 180 &&
923        p->cfg.start_angle != 0  && p->cfg.end_angle != 0 &&
924        p->cfg.start_angle > p->cfg.end_angle) {
925 
926         if(abs_y < p->cfg.vertex_p.y) {
927             return LV_DRAW_MASK_RES_FULL_COVER;
928         }
929 
930         /*Start angle mask can work only from the end of end angle mask*/
931         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
932         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
933 
934         /*Do not let the line end cross the vertex else it will affect the opposite part*/
935         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
936         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
937         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
938 
939         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
940         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
941         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
942 
943         int32_t dist = (end_angle_first - start_angle_last) >> 1;
944 
945         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
946         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
947 
948         int32_t tmp = start_angle_last + dist - rel_x;
949         if(tmp > len) tmp = len;
950         if(tmp > 0) {
951             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, &p->start_line);
952             if(res1 == LV_DRAW_MASK_RES_TRANSP) {
953                 lv_memset_00(&mask_buf[0], tmp);
954             }
955         }
956 
957         if(tmp > len) tmp = len;
958         if(tmp < 0) tmp = 0;
959         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, &p->end_line);
960         if(res2 == LV_DRAW_MASK_RES_TRANSP) {
961             lv_memset_00(&mask_buf[tmp], len - tmp);
962         }
963         if(res1 == res2) return res1;
964         else return LV_DRAW_MASK_RES_CHANGED;
965     }
966     else if(p->cfg.start_angle > 180 && p->cfg.end_angle > 180 && p->cfg.start_angle > p->cfg.end_angle) {
967 
968         if(abs_y > p->cfg.vertex_p.y) {
969             return LV_DRAW_MASK_RES_FULL_COVER;
970         }
971 
972         /*Start angle mask can work only from the end of end angle mask*/
973         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
974         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
975 
976         /*Do not let the line end cross the vertex else it will affect the opposite part*/
977         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
978         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
979         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
980 
981         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
982         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
983         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
984 
985         int32_t dist = (end_angle_first - start_angle_last) >> 1;
986 
987         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
988         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
989 
990         int32_t tmp = start_angle_last + dist - rel_x;
991         if(tmp > len) tmp = len;
992         if(tmp > 0) {
993             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, (lv_draw_mask_line_param_t *)&p->end_line);
994             if(res1 == LV_DRAW_MASK_RES_TRANSP) {
995                 lv_memset_00(&mask_buf[0], tmp);
996             }
997         }
998 
999         if(tmp > len) tmp = len;
1000         if(tmp < 0) tmp = 0;
1001         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, (lv_draw_mask_line_param_t *)&p->start_line);
1002         if(res2 == LV_DRAW_MASK_RES_TRANSP) {
1003             lv_memset_00(&mask_buf[tmp], len - tmp);
1004         }
1005         if(res1 == res2) return res1;
1006         else return LV_DRAW_MASK_RES_CHANGED;
1007     }
1008     else  {
1009 
1010         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
1011         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
1012 
1013         if(p->cfg.start_angle == 180) {
1014             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_FULL_COVER;
1015             else res1 = LV_DRAW_MASK_RES_UNKNOWN;
1016         }
1017         else if(p->cfg.start_angle == 0) {
1018             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_UNKNOWN;
1019             else res1 = LV_DRAW_MASK_RES_FULL_COVER;
1020         }
1021         else if((p->cfg.start_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
1022                 (p->cfg.start_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
1023             res1 = LV_DRAW_MASK_RES_UNKNOWN;
1024         }
1025         else  {
1026             res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->start_line);
1027         }
1028 
1029         if(p->cfg.end_angle == 180) {
1030             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_UNKNOWN;
1031             else res2 = LV_DRAW_MASK_RES_FULL_COVER;
1032         }
1033         else if(p->cfg.end_angle == 0) {
1034             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_FULL_COVER;
1035             else res2 = LV_DRAW_MASK_RES_UNKNOWN;
1036         }
1037         else if((p->cfg.end_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
1038                 (p->cfg.end_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
1039             res2 = LV_DRAW_MASK_RES_UNKNOWN;
1040         }
1041         else {
1042             res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->end_line);
1043         }
1044 
1045         if(res1 == LV_DRAW_MASK_RES_TRANSP || res2 == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
1046         else if(res1 == LV_DRAW_MASK_RES_UNKNOWN && res2 == LV_DRAW_MASK_RES_UNKNOWN) return LV_DRAW_MASK_RES_TRANSP;
1047         else if(res1 == LV_DRAW_MASK_RES_FULL_COVER &&  res2 == LV_DRAW_MASK_RES_FULL_COVER) return LV_DRAW_MASK_RES_FULL_COVER;
1048         else return LV_DRAW_MASK_RES_CHANGED;
1049     }
1050 }
1051 
1052 
1053 
lv_draw_mask_radius(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_radius_param_t * p)1054 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
1055                                                                     lv_coord_t abs_y, lv_coord_t len,
1056                                                                     lv_draw_mask_radius_param_t * p)
1057 {
1058     bool outer = p->cfg.outer;
1059     int32_t radius = p->cfg.radius;
1060     lv_area_t rect;
1061     lv_area_copy(&rect, &p->cfg.rect);
1062 
1063     if(outer == false) {
1064         if((abs_y < rect.y1 || abs_y > rect.y2)) {
1065             return LV_DRAW_MASK_RES_TRANSP;
1066         }
1067     }
1068     else {
1069         if(abs_y < rect.y1 || abs_y > rect.y2) {
1070             return LV_DRAW_MASK_RES_FULL_COVER;
1071         }
1072     }
1073 
1074     if((abs_x >= rect.x1 + radius && abs_x + len <= rect.x2 - radius) ||
1075        (abs_y >= rect.y1 + radius && abs_y <= rect.y2 - radius)) {
1076         if(outer == false) {
1077             /*Remove the edges*/
1078             int32_t last = rect.x1 - abs_x;
1079             if(last > len) return LV_DRAW_MASK_RES_TRANSP;
1080             if(last >= 0) {
1081                 lv_memset_00(&mask_buf[0], last);
1082             }
1083 
1084             int32_t first = rect.x2 - abs_x + 1;
1085             if(first <= 0) return LV_DRAW_MASK_RES_TRANSP;
1086             else if(first < len) {
1087                 lv_memset_00(&mask_buf[first], len - first);
1088             }
1089             if(last == 0 && first == len) return LV_DRAW_MASK_RES_FULL_COVER;
1090             else return LV_DRAW_MASK_RES_CHANGED;
1091         }
1092         else {
1093             int32_t first = rect.x1 - abs_x;
1094             if(first < 0) first = 0;
1095             if(first <= len) {
1096                 int32_t last = rect.x2 - abs_x - first + 1;
1097                 if(first + last > len) last = len - first;
1098                 if(last >= 0) {
1099                     lv_memset_00(&mask_buf[first], last);
1100                 }
1101             }
1102         }
1103         return LV_DRAW_MASK_RES_CHANGED;
1104     }
1105     //    printf("exec: x:%d.. %d, y:%d: r:%d, %s\n", abs_x, abs_x + len - 1, abs_y, p->cfg.radius, p->cfg.outer ? "inv" : "norm");
1106 
1107 
1108     //    if( abs_x == 276 && abs_x + len - 1 == 479 && abs_y == 63 && p->cfg.radius == 5 && p->cfg.outer == 1) {
1109     //        char x = 0;
1110     //    }
1111     //exec: x:276.. 479, y:63: r:5, inv)
1112 
1113     int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/
1114     int32_t w = lv_area_get_width(&rect);
1115     int32_t h = lv_area_get_height(&rect);
1116     abs_x -= rect.x1;
1117     abs_y -= rect.y1;
1118 
1119     lv_coord_t aa_len;
1120     lv_coord_t x_start;
1121     lv_coord_t cir_y;
1122     if(abs_y < radius) {
1123         cir_y = radius - abs_y - 1;
1124     }
1125     else {
1126         cir_y = abs_y - (h - radius);
1127     }
1128     lv_opa_t * aa_opa = get_next_line(p->circle, cir_y, &aa_len, &x_start);
1129     lv_coord_t cir_x_right = k + w - radius + x_start;
1130     lv_coord_t cir_x_left = k + radius - x_start - 1;
1131     lv_coord_t i;
1132 
1133     if(outer == false) {
1134         for(i = 0; i < aa_len; i++) {
1135             lv_opa_t opa = aa_opa[aa_len - i - 1];
1136             if(cir_x_right + i >= 0 && cir_x_right + i < len) {
1137                 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
1138             }
1139             if(cir_x_left - i >= 0 && cir_x_left - i < len) {
1140                 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
1141             }
1142         }
1143 
1144         /*Clean the right side*/
1145         cir_x_right = LV_CLAMP(0, cir_x_right + i, len);
1146         lv_memset_00(&mask_buf[cir_x_right], len - cir_x_right);
1147 
1148         /*Clean the left side*/
1149         cir_x_left = LV_CLAMP(0, cir_x_left - aa_len + 1, len);
1150         lv_memset_00(&mask_buf[0], cir_x_left);
1151     }
1152     else {
1153         for(i = 0; i < aa_len; i++) {
1154             lv_opa_t opa = 255 - (aa_opa[aa_len - 1 - i]);
1155             if(cir_x_right + i >= 0 && cir_x_right + i < len) {
1156                 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
1157             }
1158             if(cir_x_left - i >= 0 && cir_x_left - i < len) {
1159                 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
1160             }
1161         }
1162 
1163         lv_coord_t clr_start = LV_CLAMP(0, cir_x_left + 1, len);
1164         lv_coord_t clr_len = LV_CLAMP(0, cir_x_right - clr_start, len - clr_start);
1165         lv_memset_00(&mask_buf[clr_start], clr_len);
1166     }
1167 
1168     return LV_DRAW_MASK_RES_CHANGED;
1169 }
1170 
lv_draw_mask_fade(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_fade_param_t * p)1171 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
1172                                                                   lv_coord_t abs_y, lv_coord_t len,
1173                                                                   lv_draw_mask_fade_param_t * p)
1174 {
1175     if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
1176     if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
1177     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
1178     if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
1179 
1180     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
1181 
1182     if(abs_x < p->cfg.coords.x1) {
1183         int32_t x_ofs = 0;
1184         x_ofs = p->cfg.coords.x1 - abs_x;
1185         len -= x_ofs;
1186         mask_buf += x_ofs;
1187     }
1188 
1189     int32_t i;
1190 
1191     if(abs_y <= p->cfg.y_top) {
1192         for(i = 0; i < len; i++) {
1193             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_top);
1194         }
1195         return LV_DRAW_MASK_RES_CHANGED;
1196     }
1197     else if(abs_y >= p->cfg.y_bottom) {
1198         for(i = 0; i < len; i++) {
1199             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_bottom);
1200         }
1201         return LV_DRAW_MASK_RES_CHANGED;
1202     }
1203     else {
1204         /*Calculate the opa proportionally*/
1205         int16_t opa_diff = p->cfg.opa_bottom - p->cfg.opa_top;
1206         int32_t y_diff = p->cfg.y_bottom - p->cfg.y_top + 1;
1207         lv_opa_t opa_act = (int32_t)((int32_t)(abs_y - p->cfg.y_top) * opa_diff) / y_diff;
1208         opa_act += p->cfg.opa_top;
1209 
1210         for(i = 0; i < len; i++) {
1211             mask_buf[i] = mask_mix(mask_buf[i], opa_act);
1212         }
1213         return LV_DRAW_MASK_RES_CHANGED;
1214     }
1215 }
1216 
lv_draw_mask_map(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_map_param_t * p)1217 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
1218                                                                  lv_coord_t abs_y, lv_coord_t len,
1219                                                                  lv_draw_mask_map_param_t * p)
1220 {
1221     /*Handle out of the mask cases*/
1222     if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
1223     if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
1224     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
1225     if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
1226 
1227     /*Got to the current row in the map*/
1228     const lv_opa_t * map_tmp = p->cfg.map;
1229     map_tmp += (abs_y - p->cfg.coords.y1) * lv_area_get_width(&p->cfg.coords);
1230 
1231     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
1232 
1233     if(abs_x < p->cfg.coords.x1) {
1234         int32_t x_ofs = 0;
1235         x_ofs = p->cfg.coords.x1 - abs_x;
1236         len -= x_ofs;
1237         mask_buf += x_ofs;
1238     }
1239     else {
1240         map_tmp += (abs_x - p->cfg.coords.x1);
1241     }
1242 
1243     int32_t i;
1244     for(i = 0; i < len; i++) {
1245         mask_buf[i] = mask_mix(mask_buf[i], map_tmp[i]);
1246     }
1247 
1248     return LV_DRAW_MASK_RES_CHANGED;
1249 }
1250 
lv_draw_mask_polygon(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len,lv_draw_mask_polygon_param_t * param)1251 static lv_draw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_polygon(lv_opa_t * mask_buf, lv_coord_t abs_x,
1252                                                                      lv_coord_t abs_y, lv_coord_t len,
1253                                                                      lv_draw_mask_polygon_param_t * param)
1254 {
1255     uint16_t i;
1256     struct {
1257         lv_point_t p1;
1258         lv_point_t p2;
1259     } lines[2], tmp;
1260     uint16_t line_cnt = 0;
1261     lv_memset_00(&lines, sizeof(lines));
1262     int psign_prev = 0;
1263     for(i = 0; i < param->cfg.point_cnt; i++) {
1264         lv_point_t p1 = param->cfg.points[i];
1265         lv_point_t p2 = param->cfg.points[i + 1 < param->cfg.point_cnt ? i + 1 : 0];
1266         int pdiff = p1.y - p2.y, psign = pdiff / LV_ABS(pdiff);
1267         if(pdiff > 0) {
1268             if(abs_y > p1.y || abs_y < p2.y) continue;
1269             lines[line_cnt].p1 = p2;
1270             lines[line_cnt].p2 = p1;
1271         }
1272         else {
1273             if(abs_y < p1.y || abs_y > p2.y) continue;
1274             lines[line_cnt].p1 = p1;
1275             lines[line_cnt].p2 = p2;
1276         }
1277         if(psign_prev && psign_prev == psign) continue;
1278         psign_prev = psign;
1279         line_cnt++;
1280         if(line_cnt == 2) break;
1281     }
1282     if(line_cnt != 2) return LV_DRAW_MASK_RES_TRANSP;
1283     if(lines[0].p1.x > lines[1].p1.x || lines[0].p2.x > lines[1].p2.x) {
1284         tmp = lines[0];
1285         lines[0] = lines[1];
1286         lines[1] = tmp;
1287     }
1288     lv_draw_mask_line_param_t line_p;
1289     lv_draw_mask_line_points_init(&line_p, lines[0].p1.x, lines[0].p1.y, lines[0].p2.x, lines[0].p2.y,
1290                                   LV_DRAW_MASK_LINE_SIDE_RIGHT);
1291     if(line_p.steep == 0 && line_p.flat) {
1292         lv_coord_t x1 = LV_MIN(lines[0].p1.x, lines[0].p2.x);
1293         lv_coord_t x2 = LV_MAX(lines[0].p1.x, lines[0].p2.x);
1294         for(i = 0; i < len; i++) {
1295             mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF);
1296         }
1297         lv_draw_mask_free_param(&line_p);
1298         return LV_DRAW_MASK_RES_CHANGED;
1299     }
1300     lv_draw_mask_res_t res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p);
1301     lv_draw_mask_free_param(&line_p);
1302     if(res1 == LV_DRAW_MASK_RES_TRANSP) {
1303         return LV_DRAW_MASK_RES_TRANSP;
1304     }
1305     lv_draw_mask_line_points_init(&line_p, lines[1].p1.x, lines[1].p1.y, lines[1].p2.x, lines[1].p2.y,
1306                                   LV_DRAW_MASK_LINE_SIDE_LEFT);
1307     if(line_p.steep == 0 && line_p.flat) {
1308         lv_coord_t x1 = LV_MIN(lines[1].p1.x, lines[1].p2.x);
1309         lv_coord_t x2 = LV_MAX(lines[1].p1.x, lines[1].p2.x);
1310         for(i = 0; i < len; i++) {
1311             mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF);
1312         }
1313         lv_draw_mask_free_param(&line_p);
1314         return LV_DRAW_MASK_RES_CHANGED;
1315     }
1316     lv_draw_mask_res_t res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p);
1317     lv_draw_mask_free_param(&line_p);
1318     if(res2 == LV_DRAW_MASK_RES_TRANSP) {
1319         return LV_DRAW_MASK_RES_TRANSP;
1320     }
1321     if(res1 == LV_DRAW_MASK_RES_CHANGED || res2 == LV_DRAW_MASK_RES_CHANGED) return LV_DRAW_MASK_RES_CHANGED;
1322     return res1;
1323 }
1324 /**
1325  * Initialize the circle drawing
1326  * @param c pointer to a point. The coordinates will be calculated here
1327  * @param tmp point to a variable. It will store temporary data
1328  * @param radius radius of the circle
1329  */
circ_init(lv_point_t * c,lv_coord_t * tmp,lv_coord_t radius)1330 static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius)
1331 {
1332     c->x = radius;
1333     c->y = 0;
1334     *tmp = 1 - radius;
1335 }
1336 
1337 /**
1338  * Test the circle drawing is ready or not
1339  * @param c same as in circ_init
1340  * @return true if the circle is not ready yet
1341  */
circ_cont(lv_point_t * c)1342 static bool circ_cont(lv_point_t * c)
1343 {
1344     return c->y <= c->x ? true : false;
1345 }
1346 
1347 /**
1348  * Get the next point from the circle
1349  * @param c same as in circ_init. The next point stored here.
1350  * @param tmp same as in circ_init.
1351  */
circ_next(lv_point_t * c,lv_coord_t * tmp)1352 static void circ_next(lv_point_t * c, lv_coord_t * tmp)
1353 {
1354 
1355     if(*tmp <= 0) {
1356         (*tmp) += 2 * c->y + 3; /*Change in decision criterion for y -> y+1*/
1357     }
1358     else {
1359         (*tmp) += 2 * (c->y - c->x) + 5; /*Change for y -> y+1, x -> x-1*/
1360         c->x--;
1361     }
1362     c->y++;
1363 }
1364 
circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c,lv_coord_t radius)1365 static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius)
1366 {
1367     if(radius == 0) return;
1368     c->radius = radius;
1369 
1370     /*Allocate buffers*/
1371     if(c->buf) lv_mem_free(c->buf);
1372 
1373     c->buf = lv_mem_alloc(radius * 6 + 6);  /*Use uint16_t for opa_start_on_y and x_start_on_y*/
1374     LV_ASSERT_MALLOC(c->buf);
1375     c->cir_opa = c->buf;
1376     c->opa_start_on_y = (uint16_t *)(c->buf + 2 * radius + 2);
1377     c->x_start_on_y = (uint16_t *)(c->buf + 4 * radius + 4);
1378 
1379     /*Special case, handle manually*/
1380     if(radius == 1) {
1381         c->cir_opa[0] = 180;
1382         c->opa_start_on_y[0] = 0;
1383         c->opa_start_on_y[1] = 1;
1384         c->x_start_on_y[0] = 0;
1385         return;
1386     }
1387 
1388     lv_coord_t * cir_x = lv_mem_buf_get((radius + 1) * 2 * 2 * sizeof(lv_coord_t));
1389     lv_coord_t * cir_y = &cir_x[(radius + 1) * 2];
1390 
1391     uint32_t y_8th_cnt = 0;
1392     lv_point_t cp;
1393     lv_coord_t tmp;
1394     circ_init(&cp, &tmp, radius * 4);    /*Upscale by 4*/
1395     int32_t i;
1396 
1397     uint32_t x_int[4];
1398     uint32_t x_fract[4];
1399     lv_coord_t cir_size = 0;
1400     x_int[0] = cp.x >> 2;
1401     x_fract[0] = 0;
1402 
1403     /*Calculate an 1/8 circle*/
1404     while(circ_cont(&cp)) {
1405         /*Calculate 4 point of the circle */
1406         for(i = 0; i < 4; i++) {
1407             circ_next(&cp, &tmp);
1408             if(circ_cont(&cp) == false) break;
1409             x_int[i] = cp.x >> 2;
1410             x_fract[i] = cp.x & 0x3;
1411         }
1412         if(i != 4) break;
1413 
1414         /*All lines on the same x when downscaled*/
1415         if(x_int[0] == x_int[3]) {
1416             cir_x[cir_size] = x_int[0];
1417             cir_y[cir_size] = y_8th_cnt;
1418             c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2] + x_fract[3];
1419             c->cir_opa[cir_size] *= 16;
1420             cir_size++;
1421         }
1422         /*Second line on new x when downscaled*/
1423         else if(x_int[0] != x_int[1]) {
1424             cir_x[cir_size] = x_int[0];
1425             cir_y[cir_size] = y_8th_cnt;
1426             c->cir_opa[cir_size] = x_fract[0];
1427             c->cir_opa[cir_size] *= 16;
1428             cir_size++;
1429 
1430             cir_x[cir_size] = x_int[0] - 1;
1431             cir_y[cir_size] = y_8th_cnt;
1432             c->cir_opa[cir_size] = 1 * 4 + x_fract[1] + x_fract[2] + x_fract[3];;
1433             c->cir_opa[cir_size] *= 16;
1434             cir_size++;
1435         }
1436         /*Third line on new x when downscaled*/
1437         else if(x_int[0] != x_int[2]) {
1438             cir_x[cir_size] = x_int[0];
1439             cir_y[cir_size] = y_8th_cnt;
1440             c->cir_opa[cir_size] = x_fract[0] + x_fract[1];
1441             c->cir_opa[cir_size] *= 16;
1442             cir_size++;
1443 
1444             cir_x[cir_size] = x_int[0] - 1;
1445             cir_y[cir_size] = y_8th_cnt;
1446             c->cir_opa[cir_size] = 2 * 4 + x_fract[2] + x_fract[3];;
1447             c->cir_opa[cir_size] *= 16;
1448             cir_size++;
1449         }
1450         /*Forth line on new x when downscaled*/
1451         else {
1452             cir_x[cir_size] = x_int[0];
1453             cir_y[cir_size] = y_8th_cnt;
1454             c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2];
1455             c->cir_opa[cir_size] *= 16;
1456             cir_size++;
1457 
1458             cir_x[cir_size] = x_int[0] - 1;
1459             cir_y[cir_size] = y_8th_cnt;
1460             c->cir_opa[cir_size] = 3 * 4 + x_fract[3];;
1461             c->cir_opa[cir_size] *= 16;
1462             cir_size++;
1463         }
1464 
1465         y_8th_cnt++;
1466     }
1467 
1468     /*The point on the 1/8 circle is special, calculate it manually*/
1469     int32_t mid = radius * 723;
1470     int32_t mid_int = mid >> 10;
1471     if(cir_x[cir_size - 1] != mid_int || cir_y[cir_size - 1] != mid_int) {
1472         int32_t tmp_val = mid - (mid_int << 10);
1473         if(tmp_val <= 512) {
1474             tmp_val = tmp_val * tmp_val * 2;
1475             tmp_val = tmp_val >> (10 + 6);
1476         }
1477         else {
1478             tmp_val = 1024 - tmp_val;
1479             tmp_val = tmp_val * tmp_val * 2;
1480             tmp_val = tmp_val >> (10 + 6);
1481             tmp_val = 15 - tmp_val;
1482         }
1483 
1484         cir_x[cir_size] = mid_int;
1485         cir_y[cir_size] = mid_int;
1486         c->cir_opa[cir_size] = tmp_val;
1487         c->cir_opa[cir_size] *= 16;
1488         cir_size++;
1489     }
1490 
1491     /*Build the second octet by mirroring the first*/
1492     for(i = cir_size - 2; i >= 0; i--, cir_size++) {
1493         cir_x[cir_size] = cir_y[i];
1494         cir_y[cir_size] = cir_x[i];
1495         c->cir_opa[cir_size] = c->cir_opa[i];
1496     }
1497 
1498     lv_coord_t y = 0;
1499     i = 0;
1500     c->opa_start_on_y[0] = 0;
1501     while(i < cir_size) {
1502         c->opa_start_on_y[y] = i;
1503         c->x_start_on_y[y] = cir_x[i];
1504         for(; cir_y[i] == y && i < (int32_t)cir_size; i++) {
1505             c->x_start_on_y[y] = LV_MIN(c->x_start_on_y[y], cir_x[i]);
1506         }
1507         y++;
1508     }
1509 
1510     lv_mem_buf_release(cir_x);
1511 }
1512 
get_next_line(_lv_draw_mask_radius_circle_dsc_t * c,lv_coord_t y,lv_coord_t * len,lv_coord_t * x_start)1513 static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len,
1514                                 lv_coord_t * x_start)
1515 {
1516     *len = c->opa_start_on_y[y + 1] - c->opa_start_on_y[y];
1517     *x_start = c->x_start_on_y[y];
1518     return &c->cir_opa[c->opa_start_on_y[y]];
1519 }
1520 
1521 
mask_mix(lv_opa_t mask_act,lv_opa_t mask_new)1522 static inline lv_opa_t LV_ATTRIBUTE_FAST_MEM mask_mix(lv_opa_t mask_act, lv_opa_t mask_new)
1523 {
1524     if(mask_new >= LV_OPA_MAX) return mask_act;
1525     if(mask_new <= LV_OPA_MIN) return 0;
1526 
1527     return LV_UDIV255(mask_act * mask_new);// >> 8);
1528 }
1529 
1530 
1531 #endif /*LV_DRAW_COMPLEX*/
1532