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