1 /**
2  * @file lv_draw_sw_mask.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_sw_mask_private.h"
10 #include "../lv_draw_mask_private.h"
11 #include "../lv_draw.h"
12 
13 #if LV_DRAW_SW_COMPLEX
14 #include "../../core/lv_global.h"
15 #include "../../misc/lv_math.h"
16 #include "../../misc/lv_log.h"
17 #include "../../misc/lv_assert.h"
18 #include "../../osal/lv_os.h"
19 #include "../../stdlib/lv_string.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 #define CIRCLE_CACHE_LIFE_MAX           1000
25 #define CIRCLE_CACHE_AGING(life, r)     life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
26 #define circle_cache_mutex              LV_GLOBAL_DEFAULT()->draw_info.circle_cache_mutex
27 #define _circle_cache                   LV_GLOBAL_DEFAULT()->sw_circle_cache
28 
29 /**********************
30  *      TYPEDEFS
31  **********************/
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_line(lv_opa_t * mask_buf, int32_t abs_x,
37                                                                            int32_t abs_y, int32_t len,
38                                                                            lv_draw_sw_mask_line_param_t * param);
39 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_radius(lv_opa_t * mask_buf, int32_t abs_x,
40                                                                              int32_t abs_y, int32_t len,
41                                                                              lv_draw_sw_mask_radius_param_t * param);
42 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_angle(lv_opa_t * mask_buf, int32_t abs_x,
43                                                                             int32_t abs_y, int32_t len,
44                                                                             lv_draw_sw_mask_angle_param_t * param);
45 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_fade(lv_opa_t * mask_buf, int32_t abs_x,
46                                                                            int32_t abs_y, int32_t len,
47                                                                            lv_draw_sw_mask_fade_param_t * param);
48 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_map(lv_opa_t * mask_buf, int32_t abs_x,
49                                                                           int32_t abs_y, int32_t len,
50                                                                           lv_draw_sw_mask_map_param_t * param);
51 
52 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ line_mask_flat(lv_opa_t * mask_buf, int32_t abs_x,
53                                                                         int32_t abs_y,
54                                                                         int32_t len,
55                                                                         lv_draw_sw_mask_line_param_t * p);
56 static lv_draw_sw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ line_mask_steep(lv_opa_t * mask_buf, int32_t abs_x,
57                                                                          int32_t abs_y,
58                                                                          int32_t len,
59                                                                          lv_draw_sw_mask_line_param_t * p);
60 
61 static void circ_init(lv_point_t * c, int32_t * tmp, int32_t radius);
62 static bool circ_cont(lv_point_t * c);
63 static void circ_next(lv_point_t * c, int32_t * tmp);
64 static void circ_calc_aa4(lv_draw_sw_mask_radius_circle_dsc_t * c, int32_t radius);
65 static lv_opa_t * get_next_line(lv_draw_sw_mask_radius_circle_dsc_t * c, int32_t y, int32_t * len,
66                                 int32_t * x_start);
67 static inline lv_opa_t /* LV_ATTRIBUTE_FAST_MEM */ mask_mix(lv_opa_t mask_act, lv_opa_t mask_new);
68 
69 /**********************
70  *  STATIC VARIABLES
71  **********************/
72 
73 /**********************
74  *      MACROS
75  **********************/
76 
77 /**********************
78  *   GLOBAL FUNCTIONS
79  **********************/
80 
lv_draw_sw_mask_init(void)81 void lv_draw_sw_mask_init(void)
82 {
83     lv_mutex_init(&circle_cache_mutex);
84 }
85 
lv_draw_sw_mask_deinit(void)86 void lv_draw_sw_mask_deinit(void)
87 {
88     lv_mutex_delete(&circle_cache_mutex);
89 }
90 
lv_draw_sw_mask_apply(void * masks[],lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len)91 lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_sw_mask_apply(void * masks[], lv_opa_t * mask_buf, int32_t abs_x,
92                                                                   int32_t abs_y,
93                                                                   int32_t len)
94 {
95     bool changed = false;
96     lv_draw_sw_mask_common_dsc_t * dsc;
97 
98     uint32_t i;
99     for(i = 0; masks[i]; i++) {
100         dsc = masks[i];
101         lv_draw_sw_mask_res_t res = LV_DRAW_SW_MASK_RES_FULL_COVER;
102         res = dsc->cb(mask_buf, abs_x, abs_y, len, masks[i]);
103         if(res == LV_DRAW_SW_MASK_RES_TRANSP) return LV_DRAW_SW_MASK_RES_TRANSP;
104         else if(res == LV_DRAW_SW_MASK_RES_CHANGED) changed = true;
105     }
106 
107     return changed ? LV_DRAW_SW_MASK_RES_CHANGED : LV_DRAW_SW_MASK_RES_FULL_COVER;
108 }
109 
lv_draw_sw_mask_free_param(void * p)110 void lv_draw_sw_mask_free_param(void * p)
111 {
112     lv_mutex_lock(&circle_cache_mutex);
113     lv_draw_sw_mask_common_dsc_t * pdsc = p;
114     if(pdsc->type == LV_DRAW_SW_MASK_TYPE_RADIUS) {
115         lv_draw_sw_mask_radius_param_t * radius_p = (lv_draw_sw_mask_radius_param_t *) p;
116         if(radius_p->circle) {
117             if(radius_p->circle->life < 0) {
118                 lv_free(radius_p->circle->cir_opa);
119                 lv_free(radius_p->circle);
120             }
121             else {
122                 radius_p->circle->used_cnt--;
123             }
124         }
125     }
126 
127     lv_mutex_unlock(&circle_cache_mutex);
128 }
129 
lv_draw_sw_mask_cleanup(void)130 void lv_draw_sw_mask_cleanup(void)
131 {
132     uint8_t i;
133     for(i = 0; i < LV_DRAW_SW_CIRCLE_CACHE_SIZE; i++) {
134         if(_circle_cache[i].buf) {
135             lv_free(_circle_cache[i].buf);
136         }
137         lv_memzero(&(_circle_cache[i]), sizeof(_circle_cache[i]));
138     }
139 }
140 
lv_draw_sw_mask_line_points_init(lv_draw_sw_mask_line_param_t * param,int32_t p1x,int32_t p1y,int32_t p2x,int32_t p2y,lv_draw_sw_mask_line_side_t side)141 void lv_draw_sw_mask_line_points_init(lv_draw_sw_mask_line_param_t * param, int32_t p1x, int32_t p1y,
142                                       int32_t p2x,
143                                       int32_t p2y, lv_draw_sw_mask_line_side_t side)
144 {
145     lv_memzero(param, sizeof(lv_draw_sw_mask_line_param_t));
146 
147     if(p1y == p2y && side == LV_DRAW_SW_MASK_LINE_SIDE_BOTTOM) {
148         p1y--;
149         p2y--;
150     }
151 
152     if(p1y > p2y) {
153         int32_t t;
154         t = p2x;
155         p2x = p1x;
156         p1x = t;
157 
158         t = p2y;
159         p2y = p1y;
160         p1y = t;
161     }
162 
163     lv_point_set(&param->cfg.p1, p1x, p1y);
164     lv_point_set(&param->cfg.p2, p2x, p2y);
165     param->cfg.side = side;
166 
167     lv_point_set(&param->origo, p1x, p1y);
168     param->flat = (LV_ABS(p2x - p1x) > LV_ABS(p2y - p1y)) ? 1 : 0;
169     param->yx_steep = 0;
170     param->xy_steep = 0;
171     param->dsc.cb = (lv_draw_sw_mask_xcb_t)lv_draw_mask_line;
172     param->dsc.type = LV_DRAW_SW_MASK_TYPE_LINE;
173 
174     int32_t dx = p2x - p1x;
175     int32_t dy = p2y - p1y;
176 
177     if(param->flat) {
178         /*Normalize the steep. Delta x should be relative to delta x = 1024*/
179         int32_t m;
180 
181         if(dx) {
182             m = (1L << 20) / dx;  /*m is multiplier to normalize y (upscaled by 1024)*/
183             param->yx_steep = (m * dy) >> 10;
184         }
185 
186         if(dy) {
187             m = (1L << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
188             param->xy_steep = (m * dx) >> 10;
189         }
190         param->steep = param->yx_steep;
191     }
192     else {
193         /*Normalize the steep. Delta y should be relative to delta x = 1024*/
194         int32_t m;
195 
196         if(dy) {
197             m = (1L << 20) / dy;  /*m is multiplier to normalize x (upscaled by 1024)*/
198             param->xy_steep = (m * dx) >> 10;
199         }
200 
201         if(dx) {
202             m = (1L << 20) / dx;  /*m is multiplier to normalize x (upscaled by 1024)*/
203             param->yx_steep = (m * dy) >> 10;
204         }
205         param->steep = param->xy_steep;
206     }
207 
208     if(param->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_LEFT) param->inv = 0;
209     else if(param->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_RIGHT) param->inv = 1;
210     else if(param->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_TOP) {
211         if(param->steep > 0) param->inv = 1;
212         else param->inv = 0;
213     }
214     else if(param->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_BOTTOM) {
215         if(param->steep > 0) param->inv = 0;
216         else param->inv = 1;
217     }
218 
219     param->spx = param->steep >> 2;
220     if(param->steep < 0) param->spx = -param->spx;
221 }
222 
lv_draw_sw_mask_line_angle_init(lv_draw_sw_mask_line_param_t * param,int32_t p1x,int32_t py,int16_t angle,lv_draw_sw_mask_line_side_t side)223 void lv_draw_sw_mask_line_angle_init(lv_draw_sw_mask_line_param_t * param, int32_t p1x, int32_t py, int16_t angle,
224                                      lv_draw_sw_mask_line_side_t side)
225 {
226     /*Find an optimal degree.
227      *lv_mask_line_points_init will swap the points to keep the smaller y in p1
228      *Theoretically a line with `angle` or `angle+180` is the same only the points are swapped
229      *Find the degree which keeps the origo in place*/
230     if(angle > 180) angle -= 180; /*> 180 will swap the origo*/
231 
232     int32_t p2x;
233     int32_t p2y;
234 
235     p2x = (lv_trigo_sin(angle + 90) >> 5) + p1x;
236     p2y = (lv_trigo_sin(angle) >> 5) + py;
237 
238     lv_draw_sw_mask_line_points_init(param, p1x, py, p2x, p2y, side);
239 }
240 
lv_draw_sw_mask_angle_init(lv_draw_sw_mask_angle_param_t * param,int32_t vertex_x,int32_t vertex_y,int32_t start_angle,int32_t end_angle)241 void lv_draw_sw_mask_angle_init(lv_draw_sw_mask_angle_param_t * param, int32_t vertex_x, int32_t vertex_y,
242                                 int32_t start_angle, int32_t end_angle)
243 {
244     lv_draw_sw_mask_line_side_t start_side;
245     lv_draw_sw_mask_line_side_t end_side;
246 
247     /*Constrain the input angles*/
248     if(start_angle < 0)
249         start_angle = 0;
250     else if(start_angle > 359)
251         start_angle = 359;
252 
253     if(end_angle < 0)
254         end_angle = 0;
255     else if(end_angle > 359)
256         end_angle = 359;
257 
258     if(end_angle < start_angle) {
259         param->delta_deg = 360 - start_angle + end_angle;
260     }
261     else {
262         param->delta_deg = LV_ABS(end_angle - start_angle);
263     }
264 
265     param->cfg.start_angle = start_angle;
266     param->cfg.end_angle = end_angle;
267     lv_point_set(&param->cfg.vertex_p, vertex_x, vertex_y);
268     param->dsc.cb = (lv_draw_sw_mask_xcb_t)lv_draw_mask_angle;
269     param->dsc.type = LV_DRAW_SW_MASK_TYPE_ANGLE;
270 
271     LV_ASSERT_MSG(start_angle >= 0 && start_angle <= 360, "Unexpected start angle");
272 
273     if(start_angle >= 0 && start_angle < 180) {
274         start_side = LV_DRAW_SW_MASK_LINE_SIDE_LEFT;
275     }
276     else
277         start_side = LV_DRAW_SW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
278 
279     LV_ASSERT_MSG(end_angle >= 0 && start_angle <= 360, "Unexpected end angle");
280 
281     if(end_angle >= 0 && end_angle < 180) {
282         end_side = LV_DRAW_SW_MASK_LINE_SIDE_RIGHT;
283     }
284     else if(end_angle >= 180 && end_angle < 360) {
285         end_side = LV_DRAW_SW_MASK_LINE_SIDE_LEFT;
286     }
287     else
288         end_side = LV_DRAW_SW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
289 
290     lv_draw_sw_mask_line_angle_init(&param->start_line, vertex_x, vertex_y, start_angle, start_side);
291     lv_draw_sw_mask_line_angle_init(&param->end_line, vertex_x, vertex_y, end_angle, end_side);
292 }
293 
lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param,const lv_area_t * rect,int32_t radius,bool inv)294 void lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param, const lv_area_t * rect, int32_t radius,
295                                  bool inv)
296 {
297     int32_t w = lv_area_get_width(rect);
298     int32_t h = lv_area_get_height(rect);
299     int32_t short_side = LV_MIN(w, h);
300     if(radius > short_side >> 1) radius = short_side >> 1;
301     if(radius < 0) radius = 0;
302 
303     lv_area_copy(&param->cfg.rect, rect);
304     param->cfg.radius = radius;
305     param->cfg.outer = inv ? 1 : 0;
306     param->dsc.cb = (lv_draw_sw_mask_xcb_t)lv_draw_mask_radius;
307     param->dsc.type = LV_DRAW_SW_MASK_TYPE_RADIUS;
308 
309     if(radius == 0) {
310         param->circle = NULL;
311         return;
312     }
313 
314     lv_mutex_lock(&circle_cache_mutex);
315 
316     uint32_t i;
317 
318     /*Try to reuse a circle cache entry*/
319     for(i = 0; i < LV_DRAW_SW_CIRCLE_CACHE_SIZE; i++) {
320         if(_circle_cache[i].radius == radius) {
321             _circle_cache[i].used_cnt++;
322             CIRCLE_CACHE_AGING(_circle_cache[i].life, radius);
323             param->circle = &(_circle_cache[i]);
324             lv_mutex_unlock(&circle_cache_mutex);
325             return;
326         }
327     }
328 
329     /*If not cached use the free entry with lowest life*/
330     lv_draw_sw_mask_radius_circle_dsc_t * entry = NULL;
331     for(i = 0; i < LV_DRAW_SW_CIRCLE_CACHE_SIZE; i++) {
332         if(_circle_cache[i].used_cnt == 0) {
333             if(!entry) entry = &(_circle_cache[i]);
334             else if(_circle_cache[i].life < entry->life) entry = &(_circle_cache[i]);
335         }
336     }
337 
338     /*There is no unused entry. Allocate one temporarily*/
339     if(!entry) {
340         entry = lv_malloc_zeroed(sizeof(lv_draw_sw_mask_radius_circle_dsc_t));
341         LV_ASSERT_MALLOC(entry);
342         entry->life = -1;
343     }
344     else {
345         entry->used_cnt++;
346         entry->life = 0;
347         CIRCLE_CACHE_AGING(entry->life, radius);
348     }
349 
350     param->circle = entry;
351 
352     circ_calc_aa4(param->circle, radius);
353     lv_mutex_unlock(&circle_cache_mutex);
354 
355 }
356 
lv_draw_sw_mask_fade_init(lv_draw_sw_mask_fade_param_t * param,const lv_area_t * coords,lv_opa_t opa_top,int32_t y_top,lv_opa_t opa_bottom,int32_t y_bottom)357 void lv_draw_sw_mask_fade_init(lv_draw_sw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
358                                int32_t y_top,
359                                lv_opa_t opa_bottom, int32_t y_bottom)
360 {
361     lv_area_copy(&param->cfg.coords, coords);
362     param->cfg.opa_top = opa_top;
363     param->cfg.opa_bottom = opa_bottom;
364     param->cfg.y_top = y_top;
365     param->cfg.y_bottom = y_bottom;
366     param->dsc.cb = (lv_draw_sw_mask_xcb_t)lv_draw_mask_fade;
367     param->dsc.type = LV_DRAW_SW_MASK_TYPE_FADE;
368 }
369 
lv_draw_sw_mask_map_init(lv_draw_sw_mask_map_param_t * param,const lv_area_t * coords,const lv_opa_t * map)370 void lv_draw_sw_mask_map_init(lv_draw_sw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map)
371 {
372     lv_area_copy(&param->cfg.coords, coords);
373     param->cfg.map = map;
374     param->dsc.cb = (lv_draw_sw_mask_xcb_t)lv_draw_mask_map;
375     param->dsc.type = LV_DRAW_SW_MASK_TYPE_MAP;
376 }
377 
378 /**********************
379  *   STATIC FUNCTIONS
380  **********************/
381 
lv_draw_mask_line(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_line_param_t * p)382 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_line(lv_opa_t * mask_buf, int32_t abs_x,
383                                                                      int32_t abs_y, int32_t len,
384                                                                      lv_draw_sw_mask_line_param_t * p)
385 {
386     /*Make to points relative to the vertex*/
387     abs_y -= p->origo.y;
388     abs_x -= p->origo.x;
389 
390     /*Handle special cases*/
391     if(p->steep == 0) {
392         /*Horizontal*/
393         if(p->flat) {
394             /*Non sense: Can't be on the right/left of a horizontal line*/
395             if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_LEFT ||
396                p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_RIGHT) return LV_DRAW_SW_MASK_RES_FULL_COVER;
397             else if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_TOP && abs_y < 0) return LV_DRAW_SW_MASK_RES_FULL_COVER;
398             else if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_BOTTOM && abs_y > 0) return LV_DRAW_SW_MASK_RES_FULL_COVER;
399             else {
400                 return LV_DRAW_SW_MASK_RES_TRANSP;
401             }
402         }
403         /*Vertical*/
404         else {
405             /*Non sense: Can't be on the top/bottom of a vertical line*/
406             if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_TOP ||
407                p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_BOTTOM) return LV_DRAW_SW_MASK_RES_FULL_COVER;
408             else if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_RIGHT && abs_x > 0) return LV_DRAW_SW_MASK_RES_FULL_COVER;
409             else if(p->cfg.side == LV_DRAW_SW_MASK_LINE_SIDE_LEFT) {
410                 if(abs_x + len < 0) return LV_DRAW_SW_MASK_RES_FULL_COVER;
411                 else {
412                     int32_t k = - abs_x;
413                     if(k < 0) return LV_DRAW_SW_MASK_RES_TRANSP;
414                     if(k >= 0 && k < len) lv_memzero(&mask_buf[k], len - k);
415                     return  LV_DRAW_SW_MASK_RES_CHANGED;
416                 }
417             }
418             else {
419                 if(abs_x + len < 0) return LV_DRAW_SW_MASK_RES_TRANSP;
420                 else {
421                     int32_t k = - abs_x;
422                     if(k < 0) k = 0;
423                     if(k >= len) return LV_DRAW_SW_MASK_RES_TRANSP;
424                     else if(k >= 0 && k < len) lv_memzero(&mask_buf[0], k);
425                     return  LV_DRAW_SW_MASK_RES_CHANGED;
426                 }
427             }
428         }
429     }
430 
431     lv_draw_sw_mask_res_t res;
432     if(p->flat) {
433         res = line_mask_flat(mask_buf, abs_x, abs_y, len, p);
434     }
435     else {
436         res = line_mask_steep(mask_buf, abs_x, abs_y, len, p);
437     }
438 
439     return res;
440 }
441 
line_mask_flat(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_line_param_t * p)442 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM line_mask_flat(lv_opa_t * mask_buf, int32_t abs_x,
443                                                                   int32_t abs_y,
444                                                                   int32_t len,
445                                                                   lv_draw_sw_mask_line_param_t * p)
446 {
447 
448     int32_t y_at_x;
449     y_at_x = (int32_t)((int32_t)p->yx_steep * abs_x) >> 10;
450 
451     if(p->yx_steep > 0) {
452         if(y_at_x > abs_y) {
453             if(p->inv) {
454                 return LV_DRAW_SW_MASK_RES_FULL_COVER;
455             }
456             else {
457                 return LV_DRAW_SW_MASK_RES_TRANSP;
458             }
459         }
460     }
461     else {
462         if(y_at_x < abs_y) {
463             if(p->inv) {
464                 return LV_DRAW_SW_MASK_RES_FULL_COVER;
465             }
466             else {
467                 return LV_DRAW_SW_MASK_RES_TRANSP;
468             }
469         }
470     }
471 
472     /*At the end of the mask if the limit line is smaller than the mask's y.
473      *Then the mask is in the "good" area*/
474     y_at_x = (int32_t)((int32_t)p->yx_steep * (abs_x + len)) >> 10;
475     if(p->yx_steep > 0) {
476         if(y_at_x < abs_y) {
477             if(p->inv) {
478                 return LV_DRAW_SW_MASK_RES_TRANSP;
479             }
480             else {
481                 return LV_DRAW_SW_MASK_RES_FULL_COVER;
482             }
483         }
484     }
485     else {
486         if(y_at_x > abs_y) {
487             if(p->inv) {
488                 return LV_DRAW_SW_MASK_RES_TRANSP;
489             }
490             else {
491                 return LV_DRAW_SW_MASK_RES_FULL_COVER;
492             }
493         }
494     }
495 
496     int32_t xe;
497     if(p->yx_steep > 0) xe = ((abs_y * 256) * p->xy_steep) >> 10;
498     else xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
499 
500     int32_t xei = xe >> 8;
501     int32_t xef = xe & 0xFF;
502 
503     int32_t px_h;
504     if(xef == 0) px_h = 255;
505     else px_h = 255 - (((255 - xef) * p->spx) >> 8);
506     int32_t k = xei - abs_x;
507     lv_opa_t m;
508 
509     if(xef) {
510         if(k >= 0 && k < len) {
511             m = 255 - (((255 - xef) * (255 - px_h)) >> 9);
512             if(p->inv) m = 255 - m;
513             mask_buf[k] = mask_mix(mask_buf[k], m);
514         }
515         k++;
516     }
517 
518     while(px_h > p->spx) {
519         if(k >= 0 && k < len) {
520             m = px_h - (p->spx >> 1);
521             if(p->inv) m = 255 - m;
522             mask_buf[k] = mask_mix(mask_buf[k], m);
523         }
524         px_h -= p->spx;
525         k++;
526         if(k >= len) break;
527     }
528 
529     if(k < len && k >= 0) {
530         int32_t x_inters = (px_h * p->xy_steep) >> 10;
531         m = (x_inters * px_h) >> 9;
532         if(p->yx_steep < 0) m = 255 - m;
533         if(p->inv) m = 255 - m;
534         mask_buf[k] = mask_mix(mask_buf[k], m);
535     }
536 
537     if(p->inv) {
538         k = xei - abs_x;
539         if(k > len) {
540             return LV_DRAW_SW_MASK_RES_TRANSP;
541         }
542         if(k >= 0) {
543             lv_memzero(&mask_buf[0], k);
544         }
545     }
546     else {
547         k++;
548         if(k < 0) {
549             return LV_DRAW_SW_MASK_RES_TRANSP;
550         }
551         if(k <= len) {
552             lv_memzero(&mask_buf[k], len - k);
553         }
554     }
555 
556     return LV_DRAW_SW_MASK_RES_CHANGED;
557 }
558 
line_mask_steep(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_line_param_t * p)559 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM line_mask_steep(lv_opa_t * mask_buf, int32_t abs_x,
560                                                                    int32_t abs_y,
561                                                                    int32_t len,
562                                                                    lv_draw_sw_mask_line_param_t * p)
563 {
564     int32_t k;
565     int32_t x_at_y;
566     /*At the beginning of the mask if the limit line is greater than the mask's y.
567      *Then the mask is in the "wrong" area*/
568     x_at_y = (int32_t)((int32_t)p->xy_steep * abs_y) >> 10;
569     if(p->xy_steep > 0) x_at_y++;
570     if(x_at_y < abs_x) {
571         if(p->inv) {
572             return LV_DRAW_SW_MASK_RES_FULL_COVER;
573         }
574         else {
575             return LV_DRAW_SW_MASK_RES_TRANSP;
576         }
577     }
578 
579     /*At the end of the mask if the limit line is smaller than the mask's y.
580      *Then the mask is in the "good" area*/
581     x_at_y = (int32_t)((int32_t)p->xy_steep * (abs_y)) >> 10;
582     if(x_at_y > abs_x + len) {
583         if(p->inv) {
584             return LV_DRAW_SW_MASK_RES_TRANSP;
585         }
586         else {
587             return LV_DRAW_SW_MASK_RES_FULL_COVER;
588         }
589     }
590 
591     /*X start*/
592     int32_t xs = ((abs_y * 256) * p->xy_steep) >> 10;
593     int32_t xsi = xs >> 8;
594     int32_t xsf = xs & 0xFF;
595 
596     /*X end*/
597     int32_t xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
598     int32_t xei = xe >> 8;
599     int32_t xef = xe & 0xFF;
600 
601     lv_opa_t m;
602 
603     k = xsi - abs_x;
604     if(xsi != xei && (p->xy_steep < 0 && xsf == 0)) {
605         xsf = 0xFF;
606         xsi = xei;
607         k--;
608     }
609 
610     if(xsi == xei) {
611         if(k >= 0 && k < len) {
612             m = (xsf + xef) >> 1;
613             if(p->inv) m = 255 - m;
614             mask_buf[k] = mask_mix(mask_buf[k], m);
615         }
616         k++;
617 
618         if(p->inv) {
619             k = xsi - abs_x;
620             if(k >= len) {
621                 return LV_DRAW_SW_MASK_RES_TRANSP;
622             }
623             if(k >= 0) lv_memzero(&mask_buf[0], k);
624 
625         }
626         else {
627             if(k > len) k = len;
628             if(k == 0) return LV_DRAW_SW_MASK_RES_TRANSP;
629             else if(k > 0) lv_memzero(&mask_buf[k],  len - k);
630         }
631 
632     }
633     else {
634         int32_t y_inters;
635         if(p->xy_steep < 0) {
636             y_inters = (xsf * (-p->yx_steep)) >> 10;
637             if(k >= 0 && k < len) {
638                 m = (y_inters * xsf) >> 9;
639                 if(p->inv) m = 255 - m;
640                 mask_buf[k] = mask_mix(mask_buf[k], m);
641             }
642             k--;
643 
644             int32_t x_inters = ((255 - y_inters) * (-p->xy_steep)) >> 10;
645 
646             if(k >= 0 && k < len) {
647                 m = 255 - (((255 - y_inters) * x_inters) >> 9);
648                 if(p->inv) m = 255 - m;
649                 mask_buf[k] = mask_mix(mask_buf[k], m);
650             }
651 
652             k += 2;
653 
654             if(p->inv) {
655                 k = xsi - abs_x - 1;
656 
657                 if(k > len) k = len;
658                 else if(k > 0) lv_memzero(&mask_buf[0],  k);
659 
660             }
661             else {
662                 if(k > len) return LV_DRAW_SW_MASK_RES_FULL_COVER;
663                 if(k >= 0) lv_memzero(&mask_buf[k],  len - k);
664             }
665 
666         }
667         else {
668             y_inters = ((255 - xsf) * p->yx_steep) >> 10;
669             if(k >= 0 && k < len) {
670                 m = 255 - ((y_inters * (255 - xsf)) >> 9);
671                 if(p->inv) m = 255 - m;
672                 mask_buf[k] = mask_mix(mask_buf[k], m);
673             }
674 
675             k++;
676 
677             int32_t x_inters = ((255 - y_inters) * p->xy_steep) >> 10;
678             if(k >= 0 && k < len) {
679                 m = ((255 - y_inters) * x_inters) >> 9;
680                 if(p->inv) m = 255 - m;
681                 mask_buf[k] = mask_mix(mask_buf[k], m);
682             }
683             k++;
684 
685             if(p->inv) {
686                 k = xsi - abs_x;
687                 if(k > len)  return LV_DRAW_SW_MASK_RES_TRANSP;
688                 if(k >= 0) lv_memzero(&mask_buf[0],  k);
689 
690             }
691             else {
692                 if(k > len) k = len;
693                 if(k == 0) return LV_DRAW_SW_MASK_RES_TRANSP;
694                 else if(k > 0) lv_memzero(&mask_buf[k],  len - k);
695             }
696         }
697     }
698 
699     return LV_DRAW_SW_MASK_RES_CHANGED;
700 }
701 
lv_draw_mask_angle(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_angle_param_t * p)702 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_angle(lv_opa_t * mask_buf, int32_t abs_x,
703                                                                       int32_t abs_y, int32_t len,
704                                                                       lv_draw_sw_mask_angle_param_t * p)
705 {
706     int32_t rel_y = abs_y - p->cfg.vertex_p.y;
707     int32_t rel_x = abs_x - p->cfg.vertex_p.x;
708 
709     if(p->cfg.start_angle < 180 && p->cfg.end_angle < 180 &&
710        p->cfg.start_angle != 0  && p->cfg.end_angle != 0 &&
711        p->cfg.start_angle > p->cfg.end_angle) {
712 
713         if(abs_y < p->cfg.vertex_p.y) {
714             return LV_DRAW_SW_MASK_RES_FULL_COVER;
715         }
716 
717         /*Start angle mask can work only from the end of end angle mask*/
718         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
719         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
720 
721         /*Do not let the line end cross the vertex else it will affect the opposite part*/
722         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
723         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
724         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
725 
726         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
727         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
728         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
729 
730         int32_t dist = (end_angle_first - start_angle_last) >> 1;
731 
732         lv_draw_sw_mask_res_t res1 = LV_DRAW_SW_MASK_RES_FULL_COVER;
733         lv_draw_sw_mask_res_t res2 = LV_DRAW_SW_MASK_RES_FULL_COVER;
734 
735         int32_t tmp = start_angle_last + dist - rel_x;
736         if(tmp > len) tmp = len;
737         if(tmp > 0) {
738             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, &p->start_line);
739             if(res1 == LV_DRAW_SW_MASK_RES_TRANSP) {
740                 lv_memzero(&mask_buf[0], tmp);
741             }
742         }
743 
744         if(tmp > len) tmp = len;
745         if(tmp < 0) tmp = 0;
746         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, &p->end_line);
747         if(res2 == LV_DRAW_SW_MASK_RES_TRANSP) {
748             lv_memzero(&mask_buf[tmp], len - tmp);
749         }
750         if(res1 == res2) return res1;
751         else return LV_DRAW_SW_MASK_RES_CHANGED;
752     }
753     else if(p->cfg.start_angle > 180 && p->cfg.end_angle > 180 && p->cfg.start_angle > p->cfg.end_angle) {
754 
755         if(abs_y > p->cfg.vertex_p.y) {
756             return LV_DRAW_SW_MASK_RES_FULL_COVER;
757         }
758 
759         /*Start angle mask can work only from the end of end angle mask*/
760         int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
761         int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
762 
763         /*Do not let the line end cross the vertex else it will affect the opposite part*/
764         if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
765         else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
766         else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
767 
768         if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
769         else if(p->cfg.end_angle > 0 &&   p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
770         else if(p->cfg.end_angle > 90 &&  p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
771 
772         int32_t dist = (end_angle_first - start_angle_last) >> 1;
773 
774         lv_draw_sw_mask_res_t res1 = LV_DRAW_SW_MASK_RES_FULL_COVER;
775         lv_draw_sw_mask_res_t res2 = LV_DRAW_SW_MASK_RES_FULL_COVER;
776 
777         int32_t tmp = start_angle_last + dist - rel_x;
778         if(tmp > len) tmp = len;
779         if(tmp > 0) {
780             res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, (lv_draw_sw_mask_line_param_t *)&p->end_line);
781             if(res1 == LV_DRAW_SW_MASK_RES_TRANSP) {
782                 lv_memzero(&mask_buf[0], tmp);
783             }
784         }
785 
786         if(tmp > len) tmp = len;
787         if(tmp < 0) tmp = 0;
788         res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, (lv_draw_sw_mask_line_param_t *)&p->start_line);
789         if(res2 == LV_DRAW_SW_MASK_RES_TRANSP) {
790             lv_memzero(&mask_buf[tmp], len - tmp);
791         }
792         if(res1 == res2) return res1;
793         else return LV_DRAW_SW_MASK_RES_CHANGED;
794     }
795     else  {
796 
797         lv_draw_sw_mask_res_t res1 = LV_DRAW_SW_MASK_RES_FULL_COVER;
798         lv_draw_sw_mask_res_t res2 = LV_DRAW_SW_MASK_RES_FULL_COVER;
799 
800         if(p->cfg.start_angle == 180) {
801             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_SW_MASK_RES_FULL_COVER;
802             else res1 = LV_DRAW_SW_MASK_RES_UNKNOWN;
803         }
804         else if(p->cfg.start_angle == 0) {
805             if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_SW_MASK_RES_UNKNOWN;
806             else res1 = LV_DRAW_SW_MASK_RES_FULL_COVER;
807         }
808         else if((p->cfg.start_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
809                 (p->cfg.start_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
810             res1 = LV_DRAW_SW_MASK_RES_UNKNOWN;
811         }
812         else  {
813             res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->start_line);
814         }
815 
816         if(p->cfg.end_angle == 180) {
817             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_SW_MASK_RES_UNKNOWN;
818             else res2 = LV_DRAW_SW_MASK_RES_FULL_COVER;
819         }
820         else if(p->cfg.end_angle == 0) {
821             if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_SW_MASK_RES_FULL_COVER;
822             else res2 = LV_DRAW_SW_MASK_RES_UNKNOWN;
823         }
824         else if((p->cfg.end_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
825                 (p->cfg.end_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
826             res2 = LV_DRAW_SW_MASK_RES_UNKNOWN;
827         }
828         else {
829             res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->end_line);
830         }
831 
832         if(res1 == LV_DRAW_SW_MASK_RES_TRANSP || res2 == LV_DRAW_SW_MASK_RES_TRANSP) return LV_DRAW_SW_MASK_RES_TRANSP;
833         else if(res1 == LV_DRAW_SW_MASK_RES_UNKNOWN && res2 == LV_DRAW_SW_MASK_RES_UNKNOWN) return LV_DRAW_SW_MASK_RES_TRANSP;
834         else if(res1 == LV_DRAW_SW_MASK_RES_FULL_COVER &&
835                 res2 == LV_DRAW_SW_MASK_RES_FULL_COVER) return LV_DRAW_SW_MASK_RES_FULL_COVER;
836         else return LV_DRAW_SW_MASK_RES_CHANGED;
837     }
838 }
839 
lv_draw_mask_radius(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_radius_param_t * p)840 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_radius(lv_opa_t * mask_buf, int32_t abs_x,
841                                                                        int32_t abs_y, int32_t len,
842                                                                        lv_draw_sw_mask_radius_param_t * p)
843 {
844     bool outer = p->cfg.outer;
845     int32_t radius = p->cfg.radius;
846     lv_area_t rect;
847     lv_area_copy(&rect, &p->cfg.rect);
848 
849     if(outer == false) {
850         if((abs_y < rect.y1 || abs_y > rect.y2)) {
851             return LV_DRAW_SW_MASK_RES_TRANSP;
852         }
853     }
854     else {
855         if(abs_y < rect.y1 || abs_y > rect.y2) {
856             return LV_DRAW_SW_MASK_RES_FULL_COVER;
857         }
858     }
859 
860     if((abs_x >= rect.x1 + radius && abs_x + len <= rect.x2 - radius) ||
861        (abs_y >= rect.y1 + radius && abs_y <= rect.y2 - radius)) {
862         if(outer == false) {
863             /*Remove the edges*/
864             int32_t last = rect.x1 - abs_x;
865             if(last > len) return LV_DRAW_SW_MASK_RES_TRANSP;
866             if(last >= 0) {
867                 lv_memzero(&mask_buf[0], last);
868             }
869 
870             int32_t first = rect.x2 - abs_x + 1;
871             if(first <= 0) return LV_DRAW_SW_MASK_RES_TRANSP;
872             else if(first < len) {
873                 lv_memzero(&mask_buf[first], len - first);
874             }
875             if(last == 0 && first == len) return LV_DRAW_SW_MASK_RES_FULL_COVER;
876             else return LV_DRAW_SW_MASK_RES_CHANGED;
877         }
878         else {
879             int32_t first = rect.x1 - abs_x;
880             if(first < 0) first = 0;
881             if(first <= len) {
882                 int32_t last = rect.x2 - abs_x - first + 1;
883                 if(first + last > len) last = len - first;
884                 if(last >= 0) {
885                     lv_memzero(&mask_buf[first], last);
886                 }
887             }
888         }
889         return LV_DRAW_SW_MASK_RES_CHANGED;
890     }
891 
892     int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/
893     int32_t w = lv_area_get_width(&rect);
894     int32_t h = lv_area_get_height(&rect);
895     abs_x -= rect.x1;
896     abs_y -= rect.y1;
897 
898     int32_t aa_len;
899     int32_t x_start;
900     int32_t cir_y;
901     if(abs_y < radius) {
902         cir_y = radius - abs_y - 1;
903     }
904     else {
905         cir_y = abs_y - (h - radius);
906     }
907     lv_opa_t * aa_opa = get_next_line(p->circle, cir_y, &aa_len, &x_start);
908     int32_t cir_x_right = k + w - radius + x_start;
909     int32_t cir_x_left = k + radius - x_start - 1;
910     int32_t i;
911 
912     if(outer == false) {
913         for(i = 0; i < aa_len; i++) {
914             lv_opa_t opa = aa_opa[aa_len - i - 1];
915             if(cir_x_right + i >= 0 && cir_x_right + i < len) {
916                 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
917             }
918             if(cir_x_left - i >= 0 && cir_x_left - i < len) {
919                 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
920             }
921         }
922 
923         /*Clean the right side*/
924         cir_x_right = LV_CLAMP(0, cir_x_right + i, len);
925         lv_memzero(&mask_buf[cir_x_right], len - cir_x_right);
926 
927         /*Clean the left side*/
928         cir_x_left = LV_CLAMP(0, cir_x_left - aa_len + 1, len);
929         lv_memzero(&mask_buf[0], cir_x_left);
930     }
931     else {
932         for(i = 0; i < aa_len; i++) {
933             lv_opa_t opa = 255 - (aa_opa[aa_len - 1 - i]);
934             if(cir_x_right + i >= 0 && cir_x_right + i < len) {
935                 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
936             }
937             if(cir_x_left - i >= 0 && cir_x_left - i < len) {
938                 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
939             }
940         }
941 
942         int32_t clr_start = LV_CLAMP(0, cir_x_left + 1, len);
943         int32_t clr_len = LV_CLAMP(0, cir_x_right - clr_start, len - clr_start);
944         lv_memzero(&mask_buf[clr_start], clr_len);
945     }
946 
947     return LV_DRAW_SW_MASK_RES_CHANGED;
948 }
949 
lv_draw_mask_fade(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_fade_param_t * p)950 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_fade(lv_opa_t * mask_buf, int32_t abs_x,
951                                                                      int32_t abs_y, int32_t len,
952                                                                      lv_draw_sw_mask_fade_param_t * p)
953 {
954     if(abs_y < p->cfg.coords.y1) return LV_DRAW_SW_MASK_RES_FULL_COVER;
955     if(abs_y > p->cfg.coords.y2) return LV_DRAW_SW_MASK_RES_FULL_COVER;
956     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_SW_MASK_RES_FULL_COVER;
957     if(abs_x > p->cfg.coords.x2) return LV_DRAW_SW_MASK_RES_FULL_COVER;
958 
959     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
960 
961     if(abs_x < p->cfg.coords.x1) {
962         int32_t x_ofs = 0;
963         x_ofs = p->cfg.coords.x1 - abs_x;
964         len -= x_ofs;
965         mask_buf += x_ofs;
966     }
967 
968     int32_t i;
969 
970     if(abs_y <= p->cfg.y_top) {
971         for(i = 0; i < len; i++) {
972             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_top);
973         }
974         return LV_DRAW_SW_MASK_RES_CHANGED;
975     }
976     else if(abs_y >= p->cfg.y_bottom) {
977         for(i = 0; i < len; i++) {
978             mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_bottom);
979         }
980         return LV_DRAW_SW_MASK_RES_CHANGED;
981     }
982     else {
983         /*Calculate the opa proportionally*/
984         int16_t opa_diff = p->cfg.opa_bottom - p->cfg.opa_top;
985         int32_t y_diff = p->cfg.y_bottom - p->cfg.y_top + 1;
986         lv_opa_t opa_act = LV_OPA_MIX2(abs_y - p->cfg.y_top, opa_diff) / y_diff;
987         opa_act += p->cfg.opa_top;
988 
989         for(i = 0; i < len; i++) {
990             mask_buf[i] = mask_mix(mask_buf[i], opa_act);
991         }
992         return LV_DRAW_SW_MASK_RES_CHANGED;
993     }
994 }
995 
lv_draw_mask_map(lv_opa_t * mask_buf,int32_t abs_x,int32_t abs_y,int32_t len,lv_draw_sw_mask_map_param_t * p)996 static lv_draw_sw_mask_res_t LV_ATTRIBUTE_FAST_MEM lv_draw_mask_map(lv_opa_t * mask_buf, int32_t abs_x,
997                                                                     int32_t abs_y, int32_t len,
998                                                                     lv_draw_sw_mask_map_param_t * p)
999 {
1000     /*Handle out of the mask cases*/
1001     if(abs_y < p->cfg.coords.y1) return LV_DRAW_SW_MASK_RES_FULL_COVER;
1002     if(abs_y > p->cfg.coords.y2) return LV_DRAW_SW_MASK_RES_FULL_COVER;
1003     if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_SW_MASK_RES_FULL_COVER;
1004     if(abs_x > p->cfg.coords.x2) return LV_DRAW_SW_MASK_RES_FULL_COVER;
1005 
1006     /*Got to the current row in the map*/
1007     const lv_opa_t * map_tmp = p->cfg.map;
1008     map_tmp += (abs_y - p->cfg.coords.y1) * lv_area_get_width(&p->cfg.coords);
1009 
1010     if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
1011 
1012     if(abs_x < p->cfg.coords.x1) {
1013         int32_t x_ofs = 0;
1014         x_ofs = p->cfg.coords.x1 - abs_x;
1015         len -= x_ofs;
1016         mask_buf += x_ofs;
1017     }
1018     else {
1019         map_tmp += (abs_x - p->cfg.coords.x1);
1020     }
1021 
1022     int32_t i;
1023     for(i = 0; i < len; i++) {
1024         mask_buf[i] = mask_mix(mask_buf[i], map_tmp[i]);
1025     }
1026 
1027     return LV_DRAW_SW_MASK_RES_CHANGED;
1028 }
1029 
1030 /**
1031  * Initialize the circle drawing
1032  * @param c pointer to a point. The coordinates will be calculated here
1033  * @param tmp point to a variable. It will store temporary data
1034  * @param radius radius of the circle
1035  */
circ_init(lv_point_t * c,int32_t * tmp,int32_t radius)1036 static void circ_init(lv_point_t * c, int32_t * tmp, int32_t radius)
1037 {
1038     c->x = radius;
1039     c->y = 0;
1040     *tmp = 1 - radius;
1041 }
1042 
1043 /**
1044  * Test the circle drawing is ready or not
1045  * @param c same as in circ_init
1046  * @return true if the circle is not ready yet
1047  */
circ_cont(lv_point_t * c)1048 static bool circ_cont(lv_point_t * c)
1049 {
1050     return c->y <= c->x;
1051 }
1052 
1053 /**
1054  * Get the next point from the circle
1055  * @param c same as in circ_init. The next point stored here.
1056  * @param tmp same as in circ_init.
1057  */
circ_next(lv_point_t * c,int32_t * tmp)1058 static void circ_next(lv_point_t * c, int32_t * tmp)
1059 {
1060 
1061     if(*tmp <= 0) {
1062         (*tmp) += 2 * c->y + 3; /*Change in decision criterion for y -> y+1*/
1063     }
1064     else {
1065         (*tmp) += 2 * (c->y - c->x) + 5; /*Change for y -> y+1, x -> x-1*/
1066         c->x--;
1067     }
1068     c->y++;
1069 }
1070 
circ_calc_aa4(lv_draw_sw_mask_radius_circle_dsc_t * c,int32_t radius)1071 static void circ_calc_aa4(lv_draw_sw_mask_radius_circle_dsc_t * c, int32_t radius)
1072 {
1073     if(radius == 0) return;
1074     c->radius = radius;
1075 
1076     /*Allocate buffers*/
1077     if(c->buf) lv_free(c->buf);
1078 
1079     c->buf = lv_malloc(radius * 6 + 6);  /*Use uint16_t for opa_start_on_y and x_start_on_y*/
1080     LV_ASSERT_MALLOC(c->buf);
1081     c->cir_opa = c->buf;
1082     c->opa_start_on_y = (uint16_t *)(c->buf + 2 * radius + 2);
1083     c->x_start_on_y = (uint16_t *)(c->buf + 4 * radius + 4);
1084 
1085     /*Special case, handle manually*/
1086     if(radius == 1) {
1087         c->cir_opa[0] = 180;
1088         c->opa_start_on_y[0] = 0;
1089         c->opa_start_on_y[1] = 1;
1090         c->x_start_on_y[0] = 0;
1091         return;
1092     }
1093 
1094     const size_t cir_xy_size = (radius + 1) * 2 * 2 * sizeof(int32_t);
1095     int32_t * cir_x = lv_malloc_zeroed(cir_xy_size);
1096     int32_t * cir_y = &cir_x[(radius + 1) * 2];
1097 
1098     uint32_t y_8th_cnt = 0;
1099     lv_point_t cp;
1100     int32_t tmp;
1101     circ_init(&cp, &tmp, radius * 4);    /*Upscale by 4*/
1102     int32_t i;
1103 
1104     uint32_t x_int[4];
1105     uint32_t x_fract[4];
1106     int32_t cir_size = 0;
1107     x_int[0] = cp.x >> 2;
1108     x_fract[0] = 0;
1109 
1110     /*Calculate an 1/8 circle*/
1111     while(circ_cont(&cp)) {
1112         /*Calculate 4 point of the circle */
1113         for(i = 0; i < 4; i++) {
1114             circ_next(&cp, &tmp);
1115             if(circ_cont(&cp) == false) break;
1116             x_int[i] = cp.x >> 2;
1117             x_fract[i] = cp.x & 0x3;
1118         }
1119         if(i != 4) break;
1120 
1121         /*All lines on the same x when downscaled*/
1122         if(x_int[0] == x_int[3]) {
1123             cir_x[cir_size] = x_int[0];
1124             cir_y[cir_size] = y_8th_cnt;
1125             c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2] + x_fract[3];
1126             c->cir_opa[cir_size] *= 16;
1127             cir_size++;
1128         }
1129         /*Second line on new x when downscaled*/
1130         else if(x_int[0] != x_int[1]) {
1131             cir_x[cir_size] = x_int[0];
1132             cir_y[cir_size] = y_8th_cnt;
1133             c->cir_opa[cir_size] = x_fract[0];
1134             c->cir_opa[cir_size] *= 16;
1135             cir_size++;
1136 
1137             cir_x[cir_size] = x_int[0] - 1;
1138             cir_y[cir_size] = y_8th_cnt;
1139             c->cir_opa[cir_size] = 1 * 4 + x_fract[1] + x_fract[2] + x_fract[3];;
1140             c->cir_opa[cir_size] *= 16;
1141             cir_size++;
1142         }
1143         /*Third line on new x when downscaled*/
1144         else if(x_int[0] != x_int[2]) {
1145             cir_x[cir_size] = x_int[0];
1146             cir_y[cir_size] = y_8th_cnt;
1147             c->cir_opa[cir_size] = x_fract[0] + x_fract[1];
1148             c->cir_opa[cir_size] *= 16;
1149             cir_size++;
1150 
1151             cir_x[cir_size] = x_int[0] - 1;
1152             cir_y[cir_size] = y_8th_cnt;
1153             c->cir_opa[cir_size] = 2 * 4 + x_fract[2] + x_fract[3];;
1154             c->cir_opa[cir_size] *= 16;
1155             cir_size++;
1156         }
1157         /*Forth line on new x when downscaled*/
1158         else {
1159             cir_x[cir_size] = x_int[0];
1160             cir_y[cir_size] = y_8th_cnt;
1161             c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2];
1162             c->cir_opa[cir_size] *= 16;
1163             cir_size++;
1164 
1165             cir_x[cir_size] = x_int[0] - 1;
1166             cir_y[cir_size] = y_8th_cnt;
1167             c->cir_opa[cir_size] = 3 * 4 + x_fract[3];;
1168             c->cir_opa[cir_size] *= 16;
1169             cir_size++;
1170         }
1171 
1172         y_8th_cnt++;
1173     }
1174 
1175     /*The point on the 1/8 circle is special, calculate it manually*/
1176     int32_t mid = radius * 723;
1177     int32_t mid_int = mid >> 10;
1178     if(cir_x[cir_size - 1] != mid_int || cir_y[cir_size - 1] != mid_int) {
1179         int32_t tmp_val = mid - (mid_int << 10);
1180         if(tmp_val <= 512) {
1181             tmp_val = tmp_val * tmp_val * 2;
1182             tmp_val = tmp_val >> (10 + 6);
1183         }
1184         else {
1185             tmp_val = 1024 - tmp_val;
1186             tmp_val = tmp_val * tmp_val * 2;
1187             tmp_val = tmp_val >> (10 + 6);
1188             tmp_val = 15 - tmp_val;
1189         }
1190 
1191         cir_x[cir_size] = mid_int;
1192         cir_y[cir_size] = mid_int;
1193         c->cir_opa[cir_size] = tmp_val;
1194         c->cir_opa[cir_size] *= 16;
1195         cir_size++;
1196     }
1197 
1198     /*Build the second octet by mirroring the first*/
1199     for(i = cir_size - 2; i >= 0; i--, cir_size++) {
1200         cir_x[cir_size] = cir_y[i];
1201         cir_y[cir_size] = cir_x[i];
1202         c->cir_opa[cir_size] = c->cir_opa[i];
1203     }
1204 
1205     int32_t y = 0;
1206     i = 0;
1207     c->opa_start_on_y[0] = 0;
1208     while(i < cir_size) {
1209         c->opa_start_on_y[y] = i;
1210         c->x_start_on_y[y] = cir_x[i];
1211         for(; cir_y[i] == y && i < (int32_t)cir_size; i++) {
1212             c->x_start_on_y[y] = LV_MIN(c->x_start_on_y[y], cir_x[i]);
1213         }
1214         y++;
1215     }
1216 
1217     lv_free(cir_x);
1218 }
1219 
get_next_line(lv_draw_sw_mask_radius_circle_dsc_t * c,int32_t y,int32_t * len,int32_t * x_start)1220 static lv_opa_t * get_next_line(lv_draw_sw_mask_radius_circle_dsc_t * c, int32_t y, int32_t * len,
1221                                 int32_t * x_start)
1222 {
1223     *len = c->opa_start_on_y[y + 1] - c->opa_start_on_y[y];
1224     *x_start = c->x_start_on_y[y];
1225     return &c->cir_opa[c->opa_start_on_y[y]];
1226 }
1227 
mask_mix(lv_opa_t mask_act,lv_opa_t mask_new)1228 static inline lv_opa_t LV_ATTRIBUTE_FAST_MEM mask_mix(lv_opa_t mask_act, lv_opa_t mask_new)
1229 {
1230     if(mask_new >= LV_OPA_MAX) return mask_act;
1231     if(mask_new <= LV_OPA_MIN) return 0;
1232 
1233     return LV_UDIV255(mask_act * mask_new);
1234 }
1235 
1236 #endif /*LV_DRAW_SW_COMPLEX*/
1237