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