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