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