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