1 /**
2  * @file lv_mask.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_mask.h"
10 #include "../lv_misc/lv_math.h"
11 #include "../lv_misc/lv_log.h"
12 #include "../lv_misc/lv_debug.h"
13 #include "../lv_misc/lv_gc.h"
14 
15 #if defined(LV_GC_INCLUDE)
16     #include LV_GC_INCLUDE
17 #endif /* LV_ENABLE_GC */
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 
27 /**********************
28  *  STATIC PROTOTYPES
29  **********************/
30 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
31                                                                   lv_coord_t abs_y, lv_coord_t len,
32                                                                   lv_draw_mask_line_param_t * param);
33 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
34                                                                     lv_coord_t abs_y, lv_coord_t len,
35                                                                     lv_draw_mask_radius_param_t * param);
36 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
37                                                                    lv_coord_t abs_y, lv_coord_t len,
38                                                                    lv_draw_mask_angle_param_t * param);
39 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
40                                                                   lv_coord_t abs_y, lv_coord_t len,
41                                                                   lv_draw_mask_fade_param_t * param);
42 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
43                                                                  lv_coord_t abs_y, lv_coord_t len,
44                                                                  lv_draw_mask_map_param_t * param);
45 
46 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,
47                                                                lv_coord_t len,
48                                                                lv_draw_mask_line_param_t * p);
49 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,
50                                                                 lv_coord_t len,
51                                                                 lv_draw_mask_line_param_t * p);
52 
53 LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new);
54 LV_ATTRIBUTE_FAST_MEM static inline void sqrt_approx(lv_sqrt_res_t * q, lv_sqrt_res_t * ref, uint32_t x);
55 
56 /**********************
57  *  STATIC VARIABLES
58  **********************/
59 
60 /**********************
61  *      MACROS
62  **********************/
63 
64 /**********************
65  *   GLOBAL FUNCTIONS
66  **********************/
67 
68 /**
69  * Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
70  * @param param an initialized mask parameter. Only the pointer is saved.
71  * @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
72  * @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
73  */
lv_draw_mask_add(void * param,void * custom_id)74 int16_t lv_draw_mask_add(void * param, void * custom_id)
75 {
76     /*Look for a free entry*/
77     uint8_t i;
78     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
79         if(LV_GC_ROOT(_lv_draw_mask_list[i]).param == NULL) break;
80     }
81 
82     if(i >= _LV_MASK_MAX_NUM) {
83         LV_LOG_WARN("lv_mask_add: no place to add the mask");
84         return LV_MASK_ID_INV;
85     }
86 
87     LV_GC_ROOT(_lv_draw_mask_list[i]).param = param;
88     LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id = custom_id;
89 
90     return i;
91 }
92 
93 /**
94  * Apply the added buffers on a line. Used internally by the library's drawing routines.
95  * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
96  * @param abs_x absolute X coordinate where the line to calculate start
97  * @param abs_y absolute Y coordinate where the line to calculate start
98  * @param len length of the line to calculate (in pixel count)
99  * @return One of these values:
100  * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
101  * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
102  * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
103  */
lv_draw_mask_apply(lv_opa_t * mask_buf,lv_coord_t abs_x,lv_coord_t abs_y,lv_coord_t len)104 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,
105                                                             lv_coord_t len)
106 {
107     bool changed = false;
108     lv_draw_mask_common_dsc_t * dsc;
109 
110     _lv_draw_mask_saved_t * m = LV_GC_ROOT(_lv_draw_mask_list);
111 
112     while(m->param) {
113         dsc = m->param;
114         lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER;
115         res = dsc->cb(mask_buf, abs_x, abs_y, len, (void *)m->param);
116         if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
117         else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true;
118 
119         m++;
120     }
121 
122     return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
123 }
124 
125 /**
126  * Remove a mask with a given ID
127  * @param id the ID of the mask.  Returned by `lv_draw_mask_add`
128  * @return the parameter of the removed mask.
129  * If more masks have `custom_id` ID then the last mask's parameter will be returned
130  */
lv_draw_mask_remove_id(int16_t id)131 void * lv_draw_mask_remove_id(int16_t id)
132 {
133     void * p = NULL;
134 
135     if(id != LV_MASK_ID_INV) {
136         p = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
137         LV_GC_ROOT(_lv_draw_mask_list[id]).param = NULL;
138         LV_GC_ROOT(_lv_draw_mask_list[id]).custom_id = NULL;
139     }
140 
141     return p;
142 }
143 
144 /**
145  * Remove all mask with a given custom ID
146  * @param custom_id a pointer used in `lv_draw_mask_add`
147  * @return return the parameter of the removed mask.
148  * If more masks have `custom_id` ID then the last mask's parameter will be returned
149  */
lv_draw_mask_remove_custom(void * custom_id)150 void * lv_draw_mask_remove_custom(void * custom_id)
151 {
152     void * p = NULL;
153     uint8_t i;
154     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
155         if(LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id == custom_id) {
156             p = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
157             LV_GC_ROOT(_lv_draw_mask_list[i]).param = NULL;
158             LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id = NULL;
159         }
160     }
161     return p;
162 }
163 
164 /**
165  * Count the currently added masks
166  * @return number of active masks
167  */
lv_draw_mask_get_cnt(void)168 LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void)
169 {
170     uint8_t cnt = 0;
171     uint8_t i;
172     for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
173         if(LV_GC_ROOT(_lv_draw_mask_list[i]).param) cnt++;
174     }
175     return cnt;
176 }
177 
178 /**
179  *Initialize a line mask from two points.
180  * @param param pointer to a `lv_draw_mask_param_t` to initialize
181  * @param p1x X coordinate of the first point of the line
182  * @param p1y Y coordinate of the first point of the line
183  * @param p2x X coordinate of the second point of the line
184  * @param p2y y coordinate of the second point of the line
185  * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
186  * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
187  * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
188  */
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)189 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,
190                                    lv_coord_t p2y, lv_draw_mask_line_side_t side)
191 {
192     _lv_memset_00(param, sizeof(lv_draw_mask_line_param_t));
193 
194     if(p1y > p2y) {
195         lv_coord_t t;
196         t = p2x;
197         p2x = p1x;
198         p1x = t;
199 
200         t = p2y;
201         p2y = p1y;
202         p1y = t;
203     }
204 
205     param->cfg.p1.x = p1x;
206     param->cfg.p1.y = p1y;
207     param->cfg.p2.x = p2x;
208     param->cfg.p2.y = p2y;
209     param->cfg.side = side;
210 
211     param->origo.x = p1x;
212     param->origo.y = p1y;
213     param->flat = (LV_MATH_ABS(p2x - p1x) > LV_MATH_ABS(p2y - p1y)) ? 1 : 0;
214     param->yx_steep = 0;
215     param->xy_steep = 0;
216     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_line;
217     param->dsc.type = LV_DRAW_MASK_TYPE_LINE;
218 
219     int32_t dx = p2x - p1x;
220     int32_t dy = p2y - p1y;
221 
222     if(param->flat) {
223         /*Normalize the steep. Delta x should be relative to delta x = 1024*/
224         int32_t m;
225 
226         if(dx) {
227             m = (1 << 20) / dx;  /*m is multiplier to normalize y (upscaled by 1024)*/
228             param->yx_steep = (m * dy) >> 10;
229         }
230 
231         if(dy) {
232             m = (1 << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
233             param->xy_steep = (m * dx) >> 10;
234         }
235         param->steep = param->yx_steep;
236     }
237     else {
238         /*Normalize the steep. Delta y should be relative to delta x = 1024*/
239         int32_t m;
240 
241         if(dy) {
242             m = (1 << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
243             param->xy_steep = (m * dx) >> 10;
244         }
245 
246         if(dx) {
247             m = (1 << 20) / dx;  /*m is multiplier to normalize x (upscaled by 1024)*/
248             param->yx_steep = (m * dy) >> 10;
249         }
250         param->steep = param->xy_steep;
251     }
252 
253     if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) param->inv = 0;
254     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) param->inv = 1;
255     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP) {
256         if(param->steep > 0) param->inv = 1;
257         else param->inv = 0;
258     }
259     else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) {
260         if(param->steep > 0) param->inv = 0;
261         else param->inv = 1;
262     }
263 
264     param->spx = param->steep >> 2;
265     if(param->steep < 0) param->spx = -param->spx;
266 }
267 
268 /**
269  *Initialize a line mask from a point and an angle.
270  * @param param pointer to a `lv_draw_mask_param_t` to initialize
271  * @param px X coordinate of a point of the line
272  * @param py X coordinate of a point of the line
273  * @param angle right 0 deg, bottom: 90
274  * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
275  * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
276  * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
277  */
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)278 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,
279                                   lv_draw_mask_line_side_t side)
280 {
281     /* Find an optimal degree.
282      * lv_mask_line_points_init will swap the points to keep the smaller y in p1
283      * Theoretically a line with `angle` or `angle+180` is the same only the points are swapped
284      * Find the degree which keeps the origo in place */
285     if(angle > 180) angle -= 180; /*> 180 will swap the origo*/
286 
287 
288     int32_t p2x;
289     int32_t p2y;
290 
291     p2x = (_lv_trigo_sin(angle + 90) >> 5) + p1x;
292     p2y = (_lv_trigo_sin(angle) >> 5) + py;
293 
294     lv_draw_mask_line_points_init(param, p1x, py, p2x, p2y, side);
295 }
296 
297 
298 /**
299  * Initialize an angle mask.
300  * @param param pointer to a `lv_draw_mask_param_t` to initialize
301  * @param vertex_x X coordinate of the angle vertex (absolute coordinates)
302  * @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
303  * @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
304  * @param end_angle end angle
305  */
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)306 void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
307                              lv_coord_t start_angle, lv_coord_t end_angle)
308 {
309     lv_draw_mask_line_side_t start_side;
310     lv_draw_mask_line_side_t end_side;
311 
312     /* Constrain the input angles */
313     if(start_angle < 0)
314         start_angle = 0;
315     else if(start_angle > 359)
316         start_angle = 359;
317 
318     if(end_angle < 0)
319         end_angle = 0;
320     else if(end_angle > 359)
321         end_angle = 359;
322 
323     if(end_angle < start_angle) {
324         param->delta_deg = 360 - start_angle + end_angle;
325     }
326     else {
327         param->delta_deg = LV_MATH_ABS(end_angle - start_angle);
328     }
329 
330     param->cfg.start_angle = start_angle;
331     param->cfg.end_angle = end_angle;
332     param->cfg.vertex_p.x = vertex_x;
333     param->cfg.vertex_p.y = vertex_y;
334     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_angle;
335     param->dsc.type = LV_DRAW_MASK_TYPE_ANGLE;
336 
337     if(start_angle >= 0 && start_angle < 180) {
338         start_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
339     }
340     else if(start_angle >= 180 && start_angle < 360) {
341         start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT;
342     }
343     else {
344         LV_DEBUG_ASSERT(false, "Unexpected start_angle", start_angle);
345         return;
346     }
347 
348     if(end_angle >= 0 && end_angle < 180) {
349         end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT;
350     }
351     else if(end_angle >= 180 && end_angle < 360) {
352         end_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
353     }
354     else {
355         LV_DEBUG_ASSERT(false, "Unexpected end_angle", end_angle);
356         return;
357     }
358 
359     lv_draw_mask_line_angle_init(&param->start_line, vertex_x, vertex_y, start_angle, start_side);
360     lv_draw_mask_line_angle_init(&param->end_line, vertex_x, vertex_y, end_angle, end_side);
361 }
362 
363 
364 /**
365  * Initialize a fade mask.
366  * @param param param pointer to a `lv_draw_mask_param_t` to initialize
367  * @param rect coordinates of the rectangle to affect (absolute coordinates)
368  * @param radius radius of the rectangle
369  * @param inv: true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
370  */
lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param,const lv_area_t * rect,lv_coord_t radius,bool inv)371 void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv)
372 {
373     lv_coord_t w = lv_area_get_width(rect);
374     lv_coord_t h = lv_area_get_height(rect);
375     int32_t short_side = LV_MATH_MIN(w, h);
376     if(radius > short_side >> 1) radius = short_side >> 1;
377 
378     lv_area_copy(&param->cfg.rect, rect);
379     param->cfg.radius = radius;
380     param->cfg.outer = inv ? 1 : 0;
381     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_radius;
382     param->dsc.type = LV_DRAW_MASK_TYPE_RADIUS;
383     param->y_prev = INT32_MIN;
384     param->y_prev_x.f = 0;
385     param->y_prev_x.i = 0;
386 }
387 
388 
389 /**
390  * Initialize a fade mask.
391  * @param param pointer to a `lv_draw_mask_param_t` to initialize
392  * @param coords coordinates of the area to affect (absolute coordinates)
393  * @param opa_top opacity on the top
394  * @param y_top at which coordinate start to change to opacity to `opa_bottom`
395  * @param opa_bottom opacity at the bottom
396  * @param y_bottom at which coordinate reach `opa_bottom`.
397  */
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)398 void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
399                             lv_coord_t y_top,
400                             lv_opa_t opa_bottom, lv_coord_t y_bottom)
401 {
402     lv_area_copy(&param->cfg.coords, coords);
403     param->cfg.opa_top = opa_top;
404     param->cfg.opa_bottom = opa_bottom;
405     param->cfg.y_top = y_top;
406     param->cfg.y_bottom = y_bottom;
407     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_fade;
408     param->dsc.type = LV_DRAW_MASK_TYPE_FADE;
409 }
410 
411 
412 /**
413  * Initialize a map mask.
414  * @param param pointer to a `lv_draw_mask_param_t` to initialize
415  * @param coords coordinates of the map (absolute coordinates)
416  * @param map array of bytes with the mask values
417  */
lv_draw_mask_map_init(lv_draw_mask_map_param_t * param,const lv_area_t * coords,const lv_opa_t * map)418 void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map)
419 {
420     lv_area_copy(&param->cfg.coords, coords);
421     param->cfg.map = map;
422     param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_map;
423     param->dsc.type = LV_DRAW_MASK_TYPE_MAP;
424 }
425 
426 
427 /**********************
428  *   STATIC FUNCTIONS
429  **********************/
430 
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)431 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
432                                                                   lv_coord_t abs_y, lv_coord_t len,
433                                                                   lv_draw_mask_line_param_t * p)
434 {
435     /*Make to points relative to the vertex*/
436     abs_y -= p->origo.y;
437     abs_x -= p->origo.x;
438 
439     /*Handle special cases*/
440     if(p->steep == 0) {
441         /*Horizontal*/
442         if(p->flat) {
443             /*Non sense: Can't be on the right/left of a horizontal line*/
444             if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT ||
445                p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) return LV_DRAW_MASK_RES_FULL_COVER;
446             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP && abs_y + 1 < 0) return LV_DRAW_MASK_RES_FULL_COVER;
447             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM && abs_y > 0) return LV_DRAW_MASK_RES_FULL_COVER;
448             else {
449                 return LV_DRAW_MASK_RES_TRANSP;
450             }
451         }
452         /*Vertical*/
453         else {
454             /*Non sense: Can't be on the top/bottom of a vertical line*/
455             if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP ||
456                p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) return LV_DRAW_MASK_RES_FULL_COVER;
457             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT && abs_x > 0) return LV_DRAW_MASK_RES_FULL_COVER;
458             else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) {
459                 if(abs_x + len < 0) return LV_DRAW_MASK_RES_FULL_COVER;
460                 else {
461                     int32_t k = - abs_x;
462                     if(k < 0) return LV_DRAW_MASK_RES_TRANSP;
463                     if(k >= 0 && k < len) _lv_memset_00(&mask_buf[k], len - k);
464                     return  LV_DRAW_MASK_RES_CHANGED;
465                 }
466             }
467             else {
468                 if(abs_x + len < 0) return LV_DRAW_MASK_RES_TRANSP;
469                 else {
470                     int32_t k = - abs_x;
471                     if(k < 0) k = 0;
472                     if(k >= len) return LV_DRAW_MASK_RES_TRANSP;
473                     else if(k >= 0 && k < len) _lv_memset_00(&mask_buf[0], k);
474                     return  LV_DRAW_MASK_RES_CHANGED;
475                 }
476             }
477         }
478     }
479 
480     lv_draw_mask_res_t res;
481     if(p->flat) {
482         res = line_mask_flat(mask_buf, abs_x, abs_y, len, p);
483     }
484     else {
485         res = line_mask_steep(mask_buf, abs_x, abs_y, len, p);
486     }
487 
488     return res;
489 }
490 
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)491 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,
492                                                                lv_coord_t len,
493                                                                lv_draw_mask_line_param_t * p)
494 {
495     int32_t y_at_x;
496     y_at_x = (int32_t)((int32_t)p->yx_steep * abs_x) >> 10;
497 
498     if(p->yx_steep > 0) {
499         if(y_at_x > abs_y) {
500             if(p->inv) {
501                 return LV_DRAW_MASK_RES_FULL_COVER;
502             }
503             else {
504                 return LV_DRAW_MASK_RES_TRANSP;
505             }
506         }
507     }
508     else {
509         if(y_at_x < abs_y) {
510             if(p->inv) {
511                 return LV_DRAW_MASK_RES_FULL_COVER;
512             }
513             else {
514                 return LV_DRAW_MASK_RES_TRANSP;
515             }
516         }
517     }
518 
519     /* At the end of the mask if the limit line is smaller then the mask's y.
520      * Then the mask is in the "good" area*/
521     y_at_x = (int32_t)((int32_t)p->yx_steep * (abs_x + len)) >> 10;
522     if(p->yx_steep > 0) {
523         if(y_at_x < abs_y) {
524             if(p->inv) {
525                 return LV_DRAW_MASK_RES_TRANSP;
526             }
527             else {
528                 return LV_DRAW_MASK_RES_FULL_COVER;
529             }
530         }
531     }
532     else {
533         if(y_at_x > abs_y) {
534             if(p->inv) {
535                 return LV_DRAW_MASK_RES_TRANSP;
536             }
537             else {
538                 return LV_DRAW_MASK_RES_FULL_COVER;
539             }
540         }
541     }
542 
543     int32_t xe;
544     if(p->yx_steep > 0) xe = ((abs_y << 8) * p->xy_steep) >> 10;
545     else xe = (((abs_y + 1) << 8) * p->xy_steep) >> 10;
546 
547     int32_t xei = xe >> 8;
548     int32_t xef = xe & 0xFF;
549 
550     int32_t px_h;
551     if(xef == 0) px_h = 255;
552     else px_h = 255 - (((255 - xef) * p->spx) >> 8);
553     int32_t k = xei - abs_x;
554     lv_opa_t m;
555 
556     if(xef) {
557         if(k >= 0 && k < len) {
558             m = 255 - (((255 - xef) * (255 - px_h)) >> 9);
559             if(p->inv) m = 255 - m;
560             mask_buf[k] = mask_mix(mask_buf[k], m);
561         }
562         k++;
563     }
564 
565     while(px_h > p->spx) {
566         if(k >= 0 && k < len) {
567             m = px_h - (p->spx >> 1);
568             if(p->inv) m = 255 - m;
569             mask_buf[k] = mask_mix(mask_buf[k], m);
570         }
571         px_h -= p->spx;
572         k++;
573         if(k >= len) break;
574     }
575 
576 
577     if(k < len && k >= 0) {
578         int32_t x_inters = (px_h * p->xy_steep) >> 10;
579         m = (x_inters * px_h) >> 9;
580         if(p->yx_steep < 0) m = 255 - m;
581         if(p->inv) m = 255 - m;
582         mask_buf[k] = mask_mix(mask_buf[k], m);
583     }
584 
585 
586     if(p->inv) {
587         k = xei - abs_x;
588         if(k > len) {
589             return LV_DRAW_MASK_RES_TRANSP;
590         }
591         if(k >= 0) {
592             _lv_memset_00(&mask_buf[0], k);
593         }
594     }
595     else {
596         k++;
597         if(k < 0) {
598             return LV_DRAW_MASK_RES_TRANSP;
599         }
600         if(k <= len) {
601             _lv_memset_00(&mask_buf[k], len - k);
602         }
603     }
604 
605     return LV_DRAW_MASK_RES_CHANGED;
606 }
607 
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)608 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,
609                                                                 lv_coord_t len,
610                                                                 lv_draw_mask_line_param_t * p)
611 {
612     int32_t k;
613     int32_t x_at_y;
614     /* At the beginning of the mask if the limit line is greater then the mask's y.
615      * Then the mask is in the "wrong" area*/
616     x_at_y = (int32_t)((int32_t)p->xy_steep * abs_y) >> 10;
617     if(p->xy_steep > 0) x_at_y++;
618     if(x_at_y < abs_x) {
619         if(p->inv) {
620             return LV_DRAW_MASK_RES_FULL_COVER;
621         }
622         else {
623             return LV_DRAW_MASK_RES_TRANSP;
624         }
625     }
626 
627     /* At the end of the mask if the limit line is smaller then the mask's y.
628      * Then the mask is in the "good" area*/
629     x_at_y = (int32_t)((int32_t)p->xy_steep * (abs_y)) >> 10;
630     if(x_at_y > abs_x + len) {
631         if(p->inv) {
632             return LV_DRAW_MASK_RES_TRANSP;
633         }
634         else {
635             return LV_DRAW_MASK_RES_FULL_COVER;
636         }
637     }
638 
639     /*X start*/
640     int32_t xs = ((abs_y << 8) * p->xy_steep) >> 10;
641     int32_t xsi = xs >> 8;
642     int32_t xsf = xs & 0xFF;
643 
644     /*X end*/
645     int32_t xe = (((abs_y + 1) << 8) * p->xy_steep) >> 10;
646     int32_t xei = xe >> 8;
647     int32_t xef = xe & 0xFF;
648 
649     lv_opa_t m;
650 
651     k = xsi - abs_x;
652     if(xsi != xei && (p->xy_steep < 0 && xsf == 0)) {
653         xsf = 0xFF;
654         xsi = xei;
655         k--;
656     }
657 
658     if(xsi == xei) {
659         if(k >= 0 && k < len) {
660             m = (xsf + xef) >> 1;
661             if(p->inv) m = 255 - m;
662             mask_buf[k] = mask_mix(mask_buf[k], m);
663         }
664         k++;
665 
666         if(p->inv) {
667             k = xsi - abs_x;
668             if(k >= len) {
669                 return LV_DRAW_MASK_RES_TRANSP;
670             }
671             if(k >= 0) _lv_memset_00(&mask_buf[0], k);
672 
673         }
674         else {
675             if(k > len) k = len;
676             if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
677             else if(k > 0) _lv_memset_00(&mask_buf[k],  len - k);
678         }
679 
680     }
681     else {
682         int32_t y_inters;
683         if(p->xy_steep < 0) {
684             y_inters = (xsf * (-p->yx_steep)) >> 10;
685             if(k >= 0 && k < len) {
686                 m = (y_inters * xsf) >> 9;
687                 if(p->inv) m = 255 - m;
688                 mask_buf[k] = mask_mix(mask_buf[k], m);
689             }
690             k--;
691 
692             int32_t x_inters = ((255 - y_inters) * (-p->xy_steep)) >> 10;
693 
694             if(k >= 0 && k < len) {
695                 m = 255 - (((255 - y_inters) * x_inters) >> 9);
696                 if(p->inv) m = 255 - m;
697                 mask_buf[k] = mask_mix(mask_buf[k], m);
698             }
699 
700             k += 2;
701 
702             if(p->inv) {
703                 k = xsi - abs_x - 1;
704 
705                 if(k > len) k = len;
706                 else if(k > 0) _lv_memset_00(&mask_buf[0],  k);
707 
708             }
709             else {
710                 if(k > len) return LV_DRAW_MASK_RES_FULL_COVER;
711                 if(k >= 0) _lv_memset_00(&mask_buf[k],  len - k);
712             }
713 
714         }
715         else {
716             y_inters = ((255 - xsf) * p->yx_steep) >> 10;
717             if(k >= 0 && k < len) {
718                 m = 255 - ((y_inters * (255 - xsf)) >> 9);
719                 if(p->inv) m = 255 - m;
720                 mask_buf[k] = mask_mix(mask_buf[k], m);
721             }
722 
723             k++;
724 
725             int32_t x_inters = ((255 - y_inters) * p->xy_steep) >> 10;
726             if(k >= 0 && k < len) {
727                 m = ((255 - y_inters) * x_inters) >> 9;
728                 if(p->inv) m = 255 - m;
729                 mask_buf[k] = mask_mix(mask_buf[k], m);
730             }
731             k++;
732 
733             if(p->inv) {
734                 k = xsi - abs_x;
735                 if(k > len)  return LV_DRAW_MASK_RES_TRANSP;
736                 if(k >= 0) _lv_memset_00(&mask_buf[0],  k);
737 
738             }
739             else {
740                 if(k > len) k = len;
741                 if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
742                 else if(k > 0) _lv_memset_00(&mask_buf[k],  len - k);
743             }
744         }
745     }
746 
747     return LV_DRAW_MASK_RES_CHANGED;
748 }
749 
750 
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)751 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
752                                                                    lv_coord_t abs_y, lv_coord_t len,
753                                                                    lv_draw_mask_angle_param_t * p)
754 {
755     int32_t rel_y = abs_y - p->cfg.vertex_p.y;
756     int32_t rel_x = abs_x - p->cfg.vertex_p.x;
757 
758 
759     if(p->cfg.start_angle < 180 && p->cfg.end_angle < 180 &&
760        p->cfg.start_angle != 0  && p->cfg.end_angle != 0 &&
761        p->cfg.start_angle > p->cfg.end_angle) {
762 
763         if(abs_y < p->cfg.vertex_p.y) {
764             return LV_DRAW_MASK_RES_FULL_COVER;
765         }
766 
767         /*Start angle mask can work only from the end of end angle mask */
768         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
769         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
770 
771 
772         /*Do not let the line end cross the vertex else it will affect the opposite part*/
773         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
774         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
775         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
776 
777         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
778         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
779         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
780 
781 
782         int32_t dist = (end_angle_first - start_angle_last) >> 1;
783 
784         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
785         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
786 
787         int32_t tmp = start_angle_last + dist - rel_x;
788         if(tmp > len) tmp = len;
789         if(tmp > 0) {
790             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, &p->start_line);
791             if(res1 == LV_DRAW_MASK_RES_TRANSP) {
792                 _lv_memset_00(&mask_buf[0], tmp);
793             }
794         }
795 
796         if(tmp > len) tmp = len;
797         if(tmp < 0) tmp = 0;
798         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, &p->end_line);
799         if(res2 == LV_DRAW_MASK_RES_TRANSP) {
800             _lv_memset_00(&mask_buf[tmp], len - tmp);
801         }
802         if(res1 == res2) return res1;
803         else return LV_DRAW_MASK_RES_CHANGED;
804     }
805     else if(p->cfg.start_angle > 180 && p->cfg.end_angle > 180 && p->cfg.start_angle > p->cfg.end_angle) {
806 
807         if(abs_y > p->cfg.vertex_p.y) {
808             return LV_DRAW_MASK_RES_FULL_COVER;
809         }
810 
811         /*Start angle mask can work only from the end of end angle mask */
812         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
813         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
814 
815         /*Do not let the line end cross the vertex else it will affect the opposite part*/
816         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
817         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
818         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
819 
820         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
821         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
822         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
823 
824         int32_t dist = (end_angle_first - start_angle_last) >> 1;
825 
826         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
827         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
828 
829         int32_t tmp = start_angle_last + dist - rel_x;
830         if(tmp > len) tmp = len;
831         if(tmp > 0) {
832             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, (lv_draw_mask_line_param_t *)&p->end_line);
833             if(res1 == LV_DRAW_MASK_RES_TRANSP) {
834                 _lv_memset_00(&mask_buf[0], tmp);
835             }
836         }
837 
838         if(tmp > len) tmp = len;
839         if(tmp < 0) tmp = 0;
840         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, (lv_draw_mask_line_param_t *)&p->start_line);
841         if(res2 == LV_DRAW_MASK_RES_TRANSP) {
842             _lv_memset_00(&mask_buf[tmp], len - tmp);
843         }
844         if(res1 == res2) return res1;
845         else return LV_DRAW_MASK_RES_CHANGED;
846     }
847     else  {
848 
849         lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
850         lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
851 
852         if(p->cfg.start_angle == 180) {
853             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_FULL_COVER;
854             else res1 = LV_DRAW_MASK_RES_UNKNOWN;
855         }
856         else if(p->cfg.start_angle == 0) {
857             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_UNKNOWN;
858             else res1 = LV_DRAW_MASK_RES_FULL_COVER;
859         }
860         else if((p->cfg.start_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
861                 (p->cfg.start_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
862             res1 = LV_DRAW_MASK_RES_UNKNOWN;
863         }
864         else  {
865             res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->start_line);
866         }
867 
868         if(p->cfg.end_angle == 180) {
869             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_UNKNOWN;
870             else res2 = LV_DRAW_MASK_RES_FULL_COVER;
871         }
872         else if(p->cfg.end_angle == 0) {
873             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_FULL_COVER;
874             else res2 = LV_DRAW_MASK_RES_UNKNOWN;
875         }
876         else if((p->cfg.end_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
877                 (p->cfg.end_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
878             res2 = LV_DRAW_MASK_RES_UNKNOWN;
879         }
880         else {
881             res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->end_line);
882         }
883 
884         if(res1 == LV_DRAW_MASK_RES_TRANSP || res2 == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
885         else if(res1 == LV_DRAW_MASK_RES_UNKNOWN && res2 == LV_DRAW_MASK_RES_UNKNOWN) return LV_DRAW_MASK_RES_TRANSP;
886         else if(res1 == LV_DRAW_MASK_RES_FULL_COVER &&  res2 == LV_DRAW_MASK_RES_FULL_COVER) return LV_DRAW_MASK_RES_FULL_COVER;
887         else return LV_DRAW_MASK_RES_CHANGED;
888     }
889 }
890 
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)891 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
892                                                                     lv_coord_t abs_y, lv_coord_t len,
893                                                                     lv_draw_mask_radius_param_t * p)
894 {
895     bool outer = p->cfg.outer;
896     int32_t radius = p->cfg.radius;
897     lv_area_t rect;
898     lv_area_copy(&rect, &p->cfg.rect);
899 
900     if(outer == false) {
901         if(abs_y < rect.y1 || abs_y > rect.y2) {
902             return LV_DRAW_MASK_RES_TRANSP;
903         }
904     }
905     else {
906         if(abs_y < rect.y1 || abs_y > rect.y2) {
907             return LV_DRAW_MASK_RES_FULL_COVER;
908         }
909     }
910 
911     if((abs_x >= rect.x1 + radius && abs_x + len <= rect.x2 - radius) ||
912        (abs_y >= rect.y1 + radius && abs_y <= rect.y2 - radius)) {
913         if(outer == false) {
914             /*Remove the edges*/
915             int32_t last =  rect.x1 - abs_x;
916             if(last > len) return LV_DRAW_MASK_RES_TRANSP;
917             if(last >= 0) {
918                 _lv_memset_00(&mask_buf[0], last);
919             }
920 
921             int32_t first = rect.x2 - abs_x + 1;
922             if(first <= 0) return LV_DRAW_MASK_RES_TRANSP;
923             else if(first < len) {
924                 _lv_memset_00(&mask_buf[first], len - first);
925             }
926             if(last == 0 && first == len) return LV_DRAW_MASK_RES_FULL_COVER;
927             else return LV_DRAW_MASK_RES_CHANGED;
928         }
929         else {
930             int32_t first = rect.x1 - abs_x;
931             if(first < 0) first = 0;
932             if(first <= len) {
933                 int32_t last =  rect.x2 - abs_x - first + 1;
934                 if(first + last > len) last = len - first;
935                 if(last >= 0) {
936                     _lv_memset_00(&mask_buf[first], last);
937                 }
938             }
939         }
940         return LV_DRAW_MASK_RES_CHANGED;
941     }
942 
943     int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/
944     int32_t w = lv_area_get_width(&rect);
945     int32_t h = lv_area_get_height(&rect);
946     abs_x -= rect.x1;
947     abs_y -= rect.y1;
948 
949     uint32_t r2 = p->cfg.radius * p->cfg.radius;
950 
951     /*Handle corner areas*/
952     if(abs_y < radius || abs_y > h - radius - 1) {
953 
954         uint32_t sqrt_mask;
955         if(radius <= 32) sqrt_mask = 0x200;
956         if(radius <= 256) sqrt_mask = 0x800;
957         else sqrt_mask = 0x8000;
958 
959         lv_sqrt_res_t x0;
960         lv_sqrt_res_t x1;
961         /* y = 0 should mean the top of the circle */
962         int32_t y;
963         if(abs_y < radius) {
964             y = radius - abs_y;
965 
966             /* Get the x intersection points for `abs_y` and `abs_y-1`
967              * Use the circle's equation x = sqrt(r^2 - y^2)
968              * Try to use the values from the previous run*/
969             if(y == p->y_prev) {
970                 x0.f = p->y_prev_x.f;
971                 x0.i = p->y_prev_x.i;
972             }
973             else {
974                 _lv_sqrt(r2 - (y * y), &x0, sqrt_mask);
975             }
976             _lv_sqrt(r2 - ((y - 1) * (y - 1)), &x1, sqrt_mask);
977             p->y_prev = y - 1;
978             p->y_prev_x.f = x1.f;
979             p->y_prev_x.i = x1.i;
980         }
981         else {
982             y = radius - (h - abs_y) + 1;
983 
984             /* Get the x intersection points for `abs_y` and `abs_y-1`
985              * Use the circle's equation x = sqrt(r^2 - y^2)
986              * Try to use the values from the previous run*/
987             if((y - 1) == p->y_prev) {
988                 x1.f = p->y_prev_x.f;
989                 x1.i = p->y_prev_x.i;
990             }
991             else {
992                 _lv_sqrt(r2 - ((y - 1) * (y - 1)), &x1, sqrt_mask);
993             }
994 
995             _lv_sqrt(r2 - (y * y), &x0, sqrt_mask);
996             p->y_prev = y;
997             p->y_prev_x.f = x0.f;
998             p->y_prev_x.i = x0.i;
999         }
1000 
1001         /* If x1 is on the next round coordinate (e.g. x0: 3.5, x1:4.0)
1002          * then treat x1 as x1: 3.99 to handle them as they were on the same pixel*/
1003         if(x0.i == x1.i - 1 && x1.f == 0) {
1004             x1.i--;
1005             x1.f = 0xFF;
1006         }
1007 
1008         /*If the two x intersections are on the same x then just get average of the fractions*/
1009         if(x0.i == x1.i) {
1010             lv_opa_t m = (x0.f + x1.f) >> 1;
1011             if(outer) m = 255 - m;
1012             int32_t ofs = radius - x0.i - 1;
1013 
1014             /*Left corner*/
1015             int32_t kl = k + ofs;
1016 
1017             if(kl >= 0 && kl < len) {
1018                 mask_buf[kl] = mask_mix(mask_buf[kl], m);
1019             }
1020 
1021             /*Right corner*/
1022             int32_t kr = k + (w - ofs - 1);
1023             if(kr >= 0 && kr < len) {
1024                 mask_buf[kr] = mask_mix(mask_buf[kr], m);
1025             }
1026 
1027             /*Clear the unused parts*/
1028             if(outer == false) {
1029                 kr++;
1030                 if(kl > len)  {
1031                     return LV_DRAW_MASK_RES_TRANSP;
1032                 }
1033                 if(kl >= 0) {
1034                     _lv_memset_00(&mask_buf[0], kl);
1035                 }
1036                 if(kr < 0) {
1037                     return LV_DRAW_MASK_RES_TRANSP;
1038                 }
1039                 if(kr <= len) {
1040                     _lv_memset_00(&mask_buf[kr], len - kr);
1041                 }
1042             }
1043             else {
1044                 kl++;
1045                 int32_t first = kl;
1046                 if(first < 0) first = 0;
1047 
1048                 int32_t len_tmp = kr - first;
1049                 if(len_tmp + first > len) len_tmp = len - first;
1050                 if(first < len && len_tmp >= 0) {
1051                     _lv_memset_00(&mask_buf[first], len_tmp);
1052                 }
1053             }
1054         }
1055         /*Multiple pixels are affected. Get y intersection of the pixels*/
1056         else {
1057             int32_t ofs = radius - (x0.i + 1);
1058             int32_t kl = k + ofs;
1059             int32_t kr = k + (w - ofs - 1);
1060 
1061             if(outer) {
1062                 int32_t first = kl + 1;
1063                 if(first < 0) first = 0;
1064 
1065                 int32_t len_tmp = kr - first;
1066                 if(len_tmp + first > len) len_tmp = len - first;
1067                 if(first < len && len_tmp >= 0) {
1068                     _lv_memset_00(&mask_buf[first], len_tmp);
1069                 }
1070             }
1071 
1072             uint32_t i = x0.i + 1;
1073             lv_opa_t m;
1074             lv_sqrt_res_t y_prev;
1075             lv_sqrt_res_t y_next;
1076 
1077             _lv_sqrt(r2 - (x0.i * x0.i), &y_prev, sqrt_mask);
1078 
1079             if(y_prev.f == 0) {
1080                 y_prev.i--;
1081                 y_prev.f = 0xFF;
1082             }
1083 
1084             /*The first y intersection is special as it might be in the previous line*/
1085             if(y_prev.i >= y) {
1086                 _lv_sqrt(r2 - (i * i), &y_next, sqrt_mask);
1087                 m = 255 - (((255 - x0.f) * (255 - y_next.f)) >> 9);
1088 
1089                 if(outer) m = 255 - m;
1090                 if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
1091                 if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
1092                 kl--;
1093                 kr++;
1094                 y_prev.f = y_next.f;
1095                 i++;
1096             }
1097 
1098             /*Set all points which are crossed by the circle*/
1099             for(; i <= x1.i; i++) {
1100                 /* These values are very close to each other. It's enough to approximate sqrt
1101                  * The non-approximated version is lv_sqrt(r2 - (i * i), &y_next, sqrt_mask); */
1102                 sqrt_approx(&y_next, &y_prev, r2 - (i * i));
1103 
1104                 m = (y_prev.f + y_next.f) >> 1;
1105                 if(outer) m = 255 - m;
1106                 if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
1107                 if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
1108                 kl--;
1109                 kr++;
1110                 y_prev.f = y_next.f;
1111             }
1112 
1113             /*If the last pixel was left in its middle therefore
1114              * the circle still has parts on the next one*/
1115             if(y_prev.f) {
1116                 m = (y_prev.f * x1.f) >> 9;
1117                 if(outer) m = 255 - m;
1118                 if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
1119                 if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
1120                 kl--;
1121                 kr++;
1122             }
1123 
1124             if(outer == 0) {
1125                 kl++;
1126                 if(kl > len) {
1127                     return LV_DRAW_MASK_RES_TRANSP;
1128                 }
1129                 if(kl >= 0) _lv_memset_00(&mask_buf[0], kl);
1130 
1131                 if(kr < 0) {
1132                     return LV_DRAW_MASK_RES_TRANSP;
1133                 }
1134                 if(kr < len) _lv_memset_00(&mask_buf[kr], len - kr);
1135             }
1136         }
1137     }
1138 
1139     return LV_DRAW_MASK_RES_CHANGED;
1140 }
1141 
1142 
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)1143 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
1144                                                                   lv_coord_t abs_y, lv_coord_t len,
1145                                                                   lv_draw_mask_fade_param_t * p)
1146 {
1147     if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
1148     if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
1149     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
1150     if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
1151 
1152     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
1153 
1154     if(abs_x < p->cfg.coords.x1) {
1155         int32_t x_ofs = 0;
1156         x_ofs = p->cfg.coords.x1 - abs_x;
1157         len -= x_ofs;
1158         mask_buf += x_ofs;
1159     }
1160 
1161     int32_t i;
1162 
1163     if(abs_y <= p->cfg.y_top) {
1164         for(i = 0; i < len; i++) {
1165             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_top);
1166         }
1167         return LV_DRAW_MASK_RES_CHANGED;
1168     }
1169     else if(abs_y >= p->cfg.y_bottom) {
1170         for(i = 0; i < len; i++) {
1171             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_bottom);
1172         }
1173         return LV_DRAW_MASK_RES_CHANGED;
1174     }
1175     else {
1176         /*Calculate the opa proportionally*/
1177         int16_t opa_diff = p->cfg.opa_bottom - p->cfg.opa_top;
1178         int32_t y_diff = p->cfg.y_bottom - p->cfg.y_top + 1;
1179         lv_opa_t opa_act = (int32_t)((int32_t)(abs_y - p->cfg.y_top) * opa_diff) / y_diff;
1180         opa_act += p->cfg.opa_top;
1181 
1182         for(i = 0; i < len; i++) {
1183             mask_buf[i] = mask_mix(mask_buf[i], opa_act);
1184         }
1185         return LV_DRAW_MASK_RES_CHANGED;
1186     }
1187 }
1188 
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)1189 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
1190                                                                  lv_coord_t abs_y, lv_coord_t len,
1191                                                                  lv_draw_mask_map_param_t * p)
1192 {
1193     /*Handle out of the mask cases*/
1194     if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
1195     if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
1196     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
1197     if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
1198 
1199     /*Got to the current row in the map*/
1200     const lv_opa_t * map_tmp = p->cfg.map;
1201     map_tmp += (abs_y - p->cfg.coords.y1) * lv_area_get_width(&p->cfg.coords);
1202 
1203 
1204     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
1205 
1206     if(abs_x < p->cfg.coords.x1) {
1207         int32_t x_ofs = 0;
1208         x_ofs = p->cfg.coords.x1 - abs_x;
1209         len -= x_ofs;
1210         mask_buf += x_ofs;
1211     }
1212     else {
1213         map_tmp += (abs_x - p->cfg.coords.x1);
1214     }
1215 
1216     int32_t i;
1217     for(i = 0; i < len; i++) {
1218         mask_buf[i] = mask_mix(mask_buf[i], map_tmp[i]);
1219     }
1220 
1221     return LV_DRAW_MASK_RES_CHANGED;
1222 }
1223 
1224 
mask_mix(lv_opa_t mask_act,lv_opa_t mask_new)1225 LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new)
1226 {
1227     if(mask_new >= LV_OPA_MAX) return mask_act;
1228     if(mask_new <= LV_OPA_MIN) return 0;
1229 
1230     return LV_MATH_UDIV255(mask_act * mask_new);// >> 8);
1231 }
1232 
1233 /**
1234  * Approximate the sqrt near to an already calculated value
1235  * @param q store the result here
1236  * @param ref the reference point (already calculated sqrt)
1237  * @param x the value which sqrt should be approximated
1238  */
sqrt_approx(lv_sqrt_res_t * q,lv_sqrt_res_t * ref,uint32_t x)1239 LV_ATTRIBUTE_FAST_MEM static inline void sqrt_approx(lv_sqrt_res_t * q, lv_sqrt_res_t * ref, uint32_t x)
1240 {
1241     x = x << 8; /*Upscale for extra precision*/
1242 
1243     uint32_t raw = (ref->i << 4) + (ref->f >> 4);
1244     uint32_t raw2 = raw * raw;
1245 
1246     int32_t d = x - raw2;
1247     d = (int32_t)d / (int32_t)(2 * raw) + raw;
1248 
1249     q->i = d >> 4;
1250     q->f = (d & 0xF) << 4;
1251 }
1252