acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_draw_mask.c (55286B)
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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 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 */ 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 */ 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 */ 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 450 start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/ 451 452 LV_ASSERT_MSG(end_angle >= 0 && start_angle <= 360, "Unexpected end angle"); 453 454 if(end_angle >= 0 && end_angle < 180) { 455 end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; 456 } 457 else if(end_angle >= 180 && end_angle < 360) { 458 end_side = LV_DRAW_MASK_LINE_SIDE_LEFT; 459 } 460 else 461 end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/ 462 463 lv_draw_mask_line_angle_init(¶m->start_line, vertex_x, vertex_y, start_angle, start_side); 464 lv_draw_mask_line_angle_init(¶m->end_line, vertex_x, vertex_y, end_angle, end_side); 465 } 466 467 /** 468 * Initialize a fade mask. 469 * @param param pointer to an `lv_draw_mask_radius_param_t` to initialize 470 * @param rect coordinates of the rectangle to affect (absolute coordinates) 471 * @param radius radius of the rectangle 472 * @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle 473 */ 474 void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv) 475 { 476 lv_coord_t w = lv_area_get_width(rect); 477 lv_coord_t h = lv_area_get_height(rect); 478 int32_t short_side = LV_MIN(w, h); 479 if(radius > short_side >> 1) radius = short_side >> 1; 480 if(radius < 0) radius = 0; 481 482 lv_area_copy(¶m->cfg.rect, rect); 483 param->cfg.radius = radius; 484 param->cfg.outer = inv ? 1 : 0; 485 param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_radius; 486 param->dsc.type = LV_DRAW_MASK_TYPE_RADIUS; 487 488 if(radius == 0) { 489 param->circle = NULL; 490 return; 491 } 492 493 uint32_t i; 494 495 /*Try to reuse a circle cache entry*/ 496 for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) { 497 if(LV_GC_ROOT(_lv_circle_cache[i]).radius == radius) { 498 LV_GC_ROOT(_lv_circle_cache[i]).used_cnt++; 499 CIRCLE_CACHE_AGING(LV_GC_ROOT(_lv_circle_cache[i]).life, radius); 500 param->circle = &LV_GC_ROOT(_lv_circle_cache[i]); 501 return; 502 } 503 } 504 505 /*If not found find a free entry with lowest life*/ 506 _lv_draw_mask_radius_circle_dsc_t * entry = NULL; 507 for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) { 508 if(LV_GC_ROOT(_lv_circle_cache[i]).used_cnt == 0) { 509 if(!entry) entry = &LV_GC_ROOT(_lv_circle_cache[i]); 510 else if(LV_GC_ROOT(_lv_circle_cache[i]).life < entry->life) entry = &LV_GC_ROOT(_lv_circle_cache[i]); 511 } 512 } 513 514 if(!entry) { 515 entry = lv_mem_alloc(sizeof(_lv_draw_mask_radius_circle_dsc_t)); 516 LV_ASSERT_MALLOC(entry); 517 lv_memset_00(entry, sizeof(_lv_draw_mask_radius_circle_dsc_t)); 518 entry->life = -1; 519 } 520 else { 521 entry->used_cnt++; 522 entry->life = 0; 523 CIRCLE_CACHE_AGING(entry->life, radius); 524 } 525 526 param->circle = entry; 527 528 circ_calc_aa4(param->circle, radius); 529 } 530 531 /** 532 * Initialize a fade mask. 533 * @param param pointer to a `lv_draw_mask_param_t` to initialize 534 * @param coords coordinates of the area to affect (absolute coordinates) 535 * @param opa_top opacity on the top 536 * @param y_top at which coordinate start to change to opacity to `opa_bottom` 537 * @param opa_bottom opacity at the bottom 538 * @param y_bottom at which coordinate reach `opa_bottom`. 539 */ 540 void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top, 541 lv_coord_t y_top, 542 lv_opa_t opa_bottom, lv_coord_t y_bottom) 543 { 544 lv_area_copy(¶m->cfg.coords, coords); 545 param->cfg.opa_top = opa_top; 546 param->cfg.opa_bottom = opa_bottom; 547 param->cfg.y_top = y_top; 548 param->cfg.y_bottom = y_bottom; 549 param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_fade; 550 param->dsc.type = LV_DRAW_MASK_TYPE_FADE; 551 } 552 553 /** 554 * Initialize a map mask. 555 * @param param pointer to a `lv_draw_mask_param_t` to initialize 556 * @param coords coordinates of the map (absolute coordinates) 557 * @param map array of bytes with the mask values 558 */ 559 void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map) 560 { 561 lv_area_copy(¶m->cfg.coords, coords); 562 param->cfg.map = map; 563 param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_map; 564 param->dsc.type = LV_DRAW_MASK_TYPE_MAP; 565 } 566 567 void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt) 568 { 569 /*Join adjacent points if they are on the same coordinate*/ 570 lv_point_t * p = lv_mem_alloc(point_cnt * sizeof(lv_point_t)); 571 if(p == NULL) return; 572 uint16_t i; 573 uint16_t pcnt = 0; 574 p[0] = points[0]; 575 for(i = 0; i < point_cnt - 1; i++) { 576 if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) { 577 p[pcnt] = points[i]; 578 pcnt++; 579 } 580 } 581 /*The first and the last points are also adjacent*/ 582 if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) { 583 p[pcnt] = points[point_cnt - 1]; 584 pcnt++; 585 } 586 param->cfg.points = p; 587 param->cfg.point_cnt = pcnt; 588 param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_polygon; 589 param->dsc.type = LV_DRAW_MASK_TYPE_POLYGON; 590 } 591 592 /********************** 593 * STATIC FUNCTIONS 594 **********************/ 595 596 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x, 597 lv_coord_t abs_y, lv_coord_t len, 598 lv_draw_mask_line_param_t * p) 599 { 600 /*Make to points relative to the vertex*/ 601 abs_y -= p->origo.y; 602 abs_x -= p->origo.x; 603 604 /*Handle special cases*/ 605 if(p->steep == 0) { 606 /*Horizontal*/ 607 if(p->flat) { 608 /*Non sense: Can't be on the right/left of a horizontal line*/ 609 if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT || 610 p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) return LV_DRAW_MASK_RES_FULL_COVER; 611 else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP && abs_y + 1 < 0) return LV_DRAW_MASK_RES_FULL_COVER; 612 else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM && abs_y > 0) return LV_DRAW_MASK_RES_FULL_COVER; 613 else { 614 return LV_DRAW_MASK_RES_TRANSP; 615 } 616 } 617 /*Vertical*/ 618 else { 619 /*Non sense: Can't be on the top/bottom of a vertical line*/ 620 if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP || 621 p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) return LV_DRAW_MASK_RES_FULL_COVER; 622 else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT && abs_x > 0) return LV_DRAW_MASK_RES_FULL_COVER; 623 else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) { 624 if(abs_x + len < 0) return LV_DRAW_MASK_RES_FULL_COVER; 625 else { 626 int32_t k = - abs_x; 627 if(k < 0) return LV_DRAW_MASK_RES_TRANSP; 628 if(k >= 0 && k < len) lv_memset_00(&mask_buf[k], len - k); 629 return LV_DRAW_MASK_RES_CHANGED; 630 } 631 } 632 else { 633 if(abs_x + len < 0) return LV_DRAW_MASK_RES_TRANSP; 634 else { 635 int32_t k = - abs_x; 636 if(k < 0) k = 0; 637 if(k >= len) return LV_DRAW_MASK_RES_TRANSP; 638 else if(k >= 0 && k < len) lv_memset_00(&mask_buf[0], k); 639 return LV_DRAW_MASK_RES_CHANGED; 640 } 641 } 642 } 643 } 644 645 lv_draw_mask_res_t res; 646 if(p->flat) { 647 res = line_mask_flat(mask_buf, abs_x, abs_y, len, p); 648 } 649 else { 650 res = line_mask_steep(mask_buf, abs_x, abs_y, len, p); 651 } 652 653 return res; 654 } 655 656 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, 657 lv_coord_t len, 658 lv_draw_mask_line_param_t * p) 659 { 660 661 int32_t y_at_x; 662 y_at_x = (int32_t)((int32_t)p->yx_steep * abs_x) >> 10; 663 664 if(p->yx_steep > 0) { 665 if(y_at_x > abs_y) { 666 if(p->inv) { 667 return LV_DRAW_MASK_RES_FULL_COVER; 668 } 669 else { 670 return LV_DRAW_MASK_RES_TRANSP; 671 } 672 } 673 } 674 else { 675 if(y_at_x < abs_y) { 676 if(p->inv) { 677 return LV_DRAW_MASK_RES_FULL_COVER; 678 } 679 else { 680 return LV_DRAW_MASK_RES_TRANSP; 681 } 682 } 683 } 684 685 /*At the end of the mask if the limit line is smaller than the mask's y. 686 *Then the mask is in the "good" area*/ 687 y_at_x = (int32_t)((int32_t)p->yx_steep * (abs_x + len)) >> 10; 688 if(p->yx_steep > 0) { 689 if(y_at_x < abs_y) { 690 if(p->inv) { 691 return LV_DRAW_MASK_RES_TRANSP; 692 } 693 else { 694 return LV_DRAW_MASK_RES_FULL_COVER; 695 } 696 } 697 } 698 else { 699 if(y_at_x > abs_y) { 700 if(p->inv) { 701 return LV_DRAW_MASK_RES_TRANSP; 702 } 703 else { 704 return LV_DRAW_MASK_RES_FULL_COVER; 705 } 706 } 707 } 708 709 int32_t xe; 710 if(p->yx_steep > 0) xe = ((abs_y * 256) * p->xy_steep) >> 10; 711 else xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10; 712 713 int32_t xei = xe >> 8; 714 int32_t xef = xe & 0xFF; 715 716 int32_t px_h; 717 if(xef == 0) px_h = 255; 718 else px_h = 255 - (((255 - xef) * p->spx) >> 8); 719 int32_t k = xei - abs_x; 720 lv_opa_t m; 721 722 if(xef) { 723 if(k >= 0 && k < len) { 724 m = 255 - (((255 - xef) * (255 - px_h)) >> 9); 725 if(p->inv) m = 255 - m; 726 mask_buf[k] = mask_mix(mask_buf[k], m); 727 } 728 k++; 729 } 730 731 while(px_h > p->spx) { 732 if(k >= 0 && k < len) { 733 m = px_h - (p->spx >> 1); 734 if(p->inv) m = 255 - m; 735 mask_buf[k] = mask_mix(mask_buf[k], m); 736 } 737 px_h -= p->spx; 738 k++; 739 if(k >= len) break; 740 } 741 742 if(k < len && k >= 0) { 743 int32_t x_inters = (px_h * p->xy_steep) >> 10; 744 m = (x_inters * px_h) >> 9; 745 if(p->yx_steep < 0) m = 255 - m; 746 if(p->inv) m = 255 - m; 747 mask_buf[k] = mask_mix(mask_buf[k], m); 748 } 749 750 if(p->inv) { 751 k = xei - abs_x; 752 if(k > len) { 753 return LV_DRAW_MASK_RES_TRANSP; 754 } 755 if(k >= 0) { 756 lv_memset_00(&mask_buf[0], k); 757 } 758 } 759 else { 760 k++; 761 if(k < 0) { 762 return LV_DRAW_MASK_RES_TRANSP; 763 } 764 if(k <= len) { 765 lv_memset_00(&mask_buf[k], len - k); 766 } 767 } 768 769 return LV_DRAW_MASK_RES_CHANGED; 770 } 771 772 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, 773 lv_coord_t len, 774 lv_draw_mask_line_param_t * p) 775 { 776 int32_t k; 777 int32_t x_at_y; 778 /*At the beginning of the mask if the limit line is greater than the mask's y. 779 *Then the mask is in the "wrong" area*/ 780 x_at_y = (int32_t)((int32_t)p->xy_steep * abs_y) >> 10; 781 if(p->xy_steep > 0) x_at_y++; 782 if(x_at_y < abs_x) { 783 if(p->inv) { 784 return LV_DRAW_MASK_RES_FULL_COVER; 785 } 786 else { 787 return LV_DRAW_MASK_RES_TRANSP; 788 } 789 } 790 791 /*At the end of the mask if the limit line is smaller than the mask's y. 792 *Then the mask is in the "good" area*/ 793 x_at_y = (int32_t)((int32_t)p->xy_steep * (abs_y)) >> 10; 794 if(x_at_y > abs_x + len) { 795 if(p->inv) { 796 return LV_DRAW_MASK_RES_TRANSP; 797 } 798 else { 799 return LV_DRAW_MASK_RES_FULL_COVER; 800 } 801 } 802 803 /*X start*/ 804 int32_t xs = ((abs_y * 256) * p->xy_steep) >> 10; 805 int32_t xsi = xs >> 8; 806 int32_t xsf = xs & 0xFF; 807 808 /*X end*/ 809 int32_t xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10; 810 int32_t xei = xe >> 8; 811 int32_t xef = xe & 0xFF; 812 813 lv_opa_t m; 814 815 k = xsi - abs_x; 816 if(xsi != xei && (p->xy_steep < 0 && xsf == 0)) { 817 xsf = 0xFF; 818 xsi = xei; 819 k--; 820 } 821 822 if(xsi == xei) { 823 if(k >= 0 && k < len) { 824 m = (xsf + xef) >> 1; 825 if(p->inv) m = 255 - m; 826 mask_buf[k] = mask_mix(mask_buf[k], m); 827 } 828 k++; 829 830 if(p->inv) { 831 k = xsi - abs_x; 832 if(k >= len) { 833 return LV_DRAW_MASK_RES_TRANSP; 834 } 835 if(k >= 0) lv_memset_00(&mask_buf[0], k); 836 837 } 838 else { 839 if(k > len) k = len; 840 if(k == 0) return LV_DRAW_MASK_RES_TRANSP; 841 else if(k > 0) lv_memset_00(&mask_buf[k], len - k); 842 } 843 844 } 845 else { 846 int32_t y_inters; 847 if(p->xy_steep < 0) { 848 y_inters = (xsf * (-p->yx_steep)) >> 10; 849 if(k >= 0 && k < len) { 850 m = (y_inters * xsf) >> 9; 851 if(p->inv) m = 255 - m; 852 mask_buf[k] = mask_mix(mask_buf[k], m); 853 } 854 k--; 855 856 int32_t x_inters = ((255 - y_inters) * (-p->xy_steep)) >> 10; 857 858 if(k >= 0 && k < len) { 859 m = 255 - (((255 - y_inters) * x_inters) >> 9); 860 if(p->inv) m = 255 - m; 861 mask_buf[k] = mask_mix(mask_buf[k], m); 862 } 863 864 k += 2; 865 866 if(p->inv) { 867 k = xsi - abs_x - 1; 868 869 if(k > len) k = len; 870 else if(k > 0) lv_memset_00(&mask_buf[0], k); 871 872 } 873 else { 874 if(k > len) return LV_DRAW_MASK_RES_FULL_COVER; 875 if(k >= 0) lv_memset_00(&mask_buf[k], len - k); 876 } 877 878 } 879 else { 880 y_inters = ((255 - xsf) * p->yx_steep) >> 10; 881 if(k >= 0 && k < len) { 882 m = 255 - ((y_inters * (255 - xsf)) >> 9); 883 if(p->inv) m = 255 - m; 884 mask_buf[k] = mask_mix(mask_buf[k], m); 885 } 886 887 k++; 888 889 int32_t x_inters = ((255 - y_inters) * p->xy_steep) >> 10; 890 if(k >= 0 && k < len) { 891 m = ((255 - y_inters) * x_inters) >> 9; 892 if(p->inv) m = 255 - m; 893 mask_buf[k] = mask_mix(mask_buf[k], m); 894 } 895 k++; 896 897 if(p->inv) { 898 k = xsi - abs_x; 899 if(k > len) return LV_DRAW_MASK_RES_TRANSP; 900 if(k >= 0) lv_memset_00(&mask_buf[0], k); 901 902 } 903 else { 904 if(k > len) k = len; 905 if(k == 0) return LV_DRAW_MASK_RES_TRANSP; 906 else if(k > 0) lv_memset_00(&mask_buf[k], len - k); 907 } 908 } 909 } 910 911 return LV_DRAW_MASK_RES_CHANGED; 912 } 913 914 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x, 915 lv_coord_t abs_y, lv_coord_t len, 916 lv_draw_mask_angle_param_t * p) 917 { 918 int32_t rel_y = abs_y - p->cfg.vertex_p.y; 919 int32_t rel_x = abs_x - p->cfg.vertex_p.x; 920 921 if(p->cfg.start_angle < 180 && p->cfg.end_angle < 180 && 922 p->cfg.start_angle != 0 && p->cfg.end_angle != 0 && 923 p->cfg.start_angle > p->cfg.end_angle) { 924 925 if(abs_y < p->cfg.vertex_p.y) { 926 return LV_DRAW_MASK_RES_FULL_COVER; 927 } 928 929 /*Start angle mask can work only from the end of end angle mask*/ 930 int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10; 931 int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10; 932 933 /*Do not let the line end cross the vertex else it will affect the opposite part*/ 934 if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0; 935 else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0; 936 else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0; 937 938 if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0; 939 else if(p->cfg.end_angle > 0 && p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0; 940 else if(p->cfg.end_angle > 90 && p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0; 941 942 int32_t dist = (end_angle_first - start_angle_last) >> 1; 943 944 lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER; 945 lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER; 946 947 int32_t tmp = start_angle_last + dist - rel_x; 948 if(tmp > len) tmp = len; 949 if(tmp > 0) { 950 res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, &p->start_line); 951 if(res1 == LV_DRAW_MASK_RES_TRANSP) { 952 lv_memset_00(&mask_buf[0], tmp); 953 } 954 } 955 956 if(tmp > len) tmp = len; 957 if(tmp < 0) tmp = 0; 958 res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, &p->end_line); 959 if(res2 == LV_DRAW_MASK_RES_TRANSP) { 960 lv_memset_00(&mask_buf[tmp], len - tmp); 961 } 962 if(res1 == res2) return res1; 963 else return LV_DRAW_MASK_RES_CHANGED; 964 } 965 else if(p->cfg.start_angle > 180 && p->cfg.end_angle > 180 && p->cfg.start_angle > p->cfg.end_angle) { 966 967 if(abs_y > p->cfg.vertex_p.y) { 968 return LV_DRAW_MASK_RES_FULL_COVER; 969 } 970 971 /*Start angle mask can work only from the end of end angle mask*/ 972 int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10; 973 int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10; 974 975 /*Do not let the line end cross the vertex else it will affect the opposite part*/ 976 if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0; 977 else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0; 978 else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0; 979 980 if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0; 981 else if(p->cfg.end_angle > 0 && p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0; 982 else if(p->cfg.end_angle > 90 && p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0; 983 984 int32_t dist = (end_angle_first - start_angle_last) >> 1; 985 986 lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER; 987 lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER; 988 989 int32_t tmp = start_angle_last + dist - rel_x; 990 if(tmp > len) tmp = len; 991 if(tmp > 0) { 992 res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, (lv_draw_mask_line_param_t *)&p->end_line); 993 if(res1 == LV_DRAW_MASK_RES_TRANSP) { 994 lv_memset_00(&mask_buf[0], tmp); 995 } 996 } 997 998 if(tmp > len) tmp = len; 999 if(tmp < 0) tmp = 0; 1000 res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, (lv_draw_mask_line_param_t *)&p->start_line); 1001 if(res2 == LV_DRAW_MASK_RES_TRANSP) { 1002 lv_memset_00(&mask_buf[tmp], len - tmp); 1003 } 1004 if(res1 == res2) return res1; 1005 else return LV_DRAW_MASK_RES_CHANGED; 1006 } 1007 else { 1008 1009 lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER; 1010 lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER; 1011 1012 if(p->cfg.start_angle == 180) { 1013 if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_FULL_COVER; 1014 else res1 = LV_DRAW_MASK_RES_UNKNOWN; 1015 } 1016 else if(p->cfg.start_angle == 0) { 1017 if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_UNKNOWN; 1018 else res1 = LV_DRAW_MASK_RES_FULL_COVER; 1019 } 1020 else if((p->cfg.start_angle < 180 && abs_y < p->cfg.vertex_p.y) || 1021 (p->cfg.start_angle > 180 && abs_y >= p->cfg.vertex_p.y)) { 1022 res1 = LV_DRAW_MASK_RES_UNKNOWN; 1023 } 1024 else { 1025 res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->start_line); 1026 } 1027 1028 if(p->cfg.end_angle == 180) { 1029 if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_UNKNOWN; 1030 else res2 = LV_DRAW_MASK_RES_FULL_COVER; 1031 } 1032 else if(p->cfg.end_angle == 0) { 1033 if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_FULL_COVER; 1034 else res2 = LV_DRAW_MASK_RES_UNKNOWN; 1035 } 1036 else if((p->cfg.end_angle < 180 && abs_y < p->cfg.vertex_p.y) || 1037 (p->cfg.end_angle > 180 && abs_y >= p->cfg.vertex_p.y)) { 1038 res2 = LV_DRAW_MASK_RES_UNKNOWN; 1039 } 1040 else { 1041 res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->end_line); 1042 } 1043 1044 if(res1 == LV_DRAW_MASK_RES_TRANSP || res2 == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP; 1045 else if(res1 == LV_DRAW_MASK_RES_UNKNOWN && res2 == LV_DRAW_MASK_RES_UNKNOWN) return LV_DRAW_MASK_RES_TRANSP; 1046 else if(res1 == LV_DRAW_MASK_RES_FULL_COVER && res2 == LV_DRAW_MASK_RES_FULL_COVER) return LV_DRAW_MASK_RES_FULL_COVER; 1047 else return LV_DRAW_MASK_RES_CHANGED; 1048 } 1049 } 1050 1051 1052 1053 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x, 1054 lv_coord_t abs_y, lv_coord_t len, 1055 lv_draw_mask_radius_param_t * p) 1056 { 1057 bool outer = p->cfg.outer; 1058 int32_t radius = p->cfg.radius; 1059 lv_area_t rect; 1060 lv_area_copy(&rect, &p->cfg.rect); 1061 1062 if(outer == false) { 1063 if((abs_y < rect.y1 || abs_y > rect.y2)) { 1064 return LV_DRAW_MASK_RES_TRANSP; 1065 } 1066 } 1067 else { 1068 if(abs_y < rect.y1 || abs_y > rect.y2) { 1069 return LV_DRAW_MASK_RES_FULL_COVER; 1070 } 1071 } 1072 1073 if((abs_x >= rect.x1 + radius && abs_x + len <= rect.x2 - radius) || 1074 (abs_y >= rect.y1 + radius && abs_y <= rect.y2 - radius)) { 1075 if(outer == false) { 1076 /*Remove the edges*/ 1077 int32_t last = rect.x1 - abs_x; 1078 if(last > len) return LV_DRAW_MASK_RES_TRANSP; 1079 if(last >= 0) { 1080 lv_memset_00(&mask_buf[0], last); 1081 } 1082 1083 int32_t first = rect.x2 - abs_x + 1; 1084 if(first <= 0) return LV_DRAW_MASK_RES_TRANSP; 1085 else if(first < len) { 1086 lv_memset_00(&mask_buf[first], len - first); 1087 } 1088 if(last == 0 && first == len) return LV_DRAW_MASK_RES_FULL_COVER; 1089 else return LV_DRAW_MASK_RES_CHANGED; 1090 } 1091 else { 1092 int32_t first = rect.x1 - abs_x; 1093 if(first < 0) first = 0; 1094 if(first <= len) { 1095 int32_t last = rect.x2 - abs_x - first + 1; 1096 if(first + last > len) last = len - first; 1097 if(last >= 0) { 1098 lv_memset_00(&mask_buf[first], last); 1099 } 1100 } 1101 } 1102 return LV_DRAW_MASK_RES_CHANGED; 1103 } 1104 // 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"); 1105 1106 1107 // if( abs_x == 276 && abs_x + len - 1 == 479 && abs_y == 63 && p->cfg.radius == 5 && p->cfg.outer == 1) { 1108 // char x = 0; 1109 // } 1110 //exec: x:276.. 479, y:63: r:5, inv) 1111 1112 int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/ 1113 int32_t w = lv_area_get_width(&rect); 1114 int32_t h = lv_area_get_height(&rect); 1115 abs_x -= rect.x1; 1116 abs_y -= rect.y1; 1117 1118 lv_coord_t aa_len; 1119 lv_coord_t x_start; 1120 lv_coord_t cir_y; 1121 if(abs_y < radius) { 1122 cir_y = radius - abs_y - 1; 1123 } 1124 else { 1125 cir_y = abs_y - (h - radius); 1126 } 1127 lv_opa_t * aa_opa = get_next_line(p->circle, cir_y, &aa_len, &x_start); 1128 lv_coord_t cir_x_right = k + w - radius + x_start; 1129 lv_coord_t cir_x_left = k + radius - x_start - 1; 1130 lv_coord_t i; 1131 1132 if(outer == false) { 1133 for(i = 0; i < aa_len; i++) { 1134 lv_opa_t opa = aa_opa[aa_len - i - 1]; 1135 if(cir_x_right + i >= 0 && cir_x_right + i < len) { 1136 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]); 1137 } 1138 if(cir_x_left - i >= 0 && cir_x_left - i < len) { 1139 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]); 1140 } 1141 } 1142 1143 /*Clean the right side*/ 1144 cir_x_right = LV_CLAMP(0, cir_x_right + i, len); 1145 lv_memset_00(&mask_buf[cir_x_right], len - cir_x_right); 1146 1147 /*Clean the left side*/ 1148 cir_x_left = LV_CLAMP(0, cir_x_left - aa_len + 1, len); 1149 lv_memset_00(&mask_buf[0], cir_x_left); 1150 } 1151 else { 1152 for(i = 0; i < aa_len; i++) { 1153 lv_opa_t opa = 255 - (aa_opa[aa_len - 1 - i]); 1154 if(cir_x_right + i >= 0 && cir_x_right + i < len) { 1155 mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]); 1156 } 1157 if(cir_x_left - i >= 0 && cir_x_left - i < len) { 1158 mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]); 1159 } 1160 } 1161 1162 lv_coord_t clr_start = LV_CLAMP(0, cir_x_left + 1, len); 1163 lv_coord_t clr_len = LV_CLAMP(0, cir_x_right - clr_start, len - clr_start); 1164 lv_memset_00(&mask_buf[clr_start], clr_len); 1165 } 1166 1167 return LV_DRAW_MASK_RES_CHANGED; 1168 } 1169 1170 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x, 1171 lv_coord_t abs_y, lv_coord_t len, 1172 lv_draw_mask_fade_param_t * p) 1173 { 1174 if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER; 1175 if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER; 1176 if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER; 1177 if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER; 1178 1179 if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1; 1180 1181 if(abs_x < p->cfg.coords.x1) { 1182 int32_t x_ofs = 0; 1183 x_ofs = p->cfg.coords.x1 - abs_x; 1184 len -= x_ofs; 1185 mask_buf += x_ofs; 1186 } 1187 1188 int32_t i; 1189 1190 if(abs_y <= p->cfg.y_top) { 1191 for(i = 0; i < len; i++) { 1192 mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_top); 1193 } 1194 return LV_DRAW_MASK_RES_CHANGED; 1195 } 1196 else if(abs_y >= p->cfg.y_bottom) { 1197 for(i = 0; i < len; i++) { 1198 mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_bottom); 1199 } 1200 return LV_DRAW_MASK_RES_CHANGED; 1201 } 1202 else { 1203 /*Calculate the opa proportionally*/ 1204 int16_t opa_diff = p->cfg.opa_bottom - p->cfg.opa_top; 1205 int32_t y_diff = p->cfg.y_bottom - p->cfg.y_top + 1; 1206 lv_opa_t opa_act = (int32_t)((int32_t)(abs_y - p->cfg.y_top) * opa_diff) / y_diff; 1207 opa_act += p->cfg.opa_top; 1208 1209 for(i = 0; i < len; i++) { 1210 mask_buf[i] = mask_mix(mask_buf[i], opa_act); 1211 } 1212 return LV_DRAW_MASK_RES_CHANGED; 1213 } 1214 } 1215 1216 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x, 1217 lv_coord_t abs_y, lv_coord_t len, 1218 lv_draw_mask_map_param_t * p) 1219 { 1220 /*Handle out of the mask cases*/ 1221 if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER; 1222 if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER; 1223 if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER; 1224 if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER; 1225 1226 /*Got to the current row in the map*/ 1227 const lv_opa_t * map_tmp = p->cfg.map; 1228 map_tmp += (abs_y - p->cfg.coords.y1) * lv_area_get_width(&p->cfg.coords); 1229 1230 if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1; 1231 1232 if(abs_x < p->cfg.coords.x1) { 1233 int32_t x_ofs = 0; 1234 x_ofs = p->cfg.coords.x1 - abs_x; 1235 len -= x_ofs; 1236 mask_buf += x_ofs; 1237 } 1238 else { 1239 map_tmp += (abs_x - p->cfg.coords.x1); 1240 } 1241 1242 int32_t i; 1243 for(i = 0; i < len; i++) { 1244 mask_buf[i] = mask_mix(mask_buf[i], map_tmp[i]); 1245 } 1246 1247 return LV_DRAW_MASK_RES_CHANGED; 1248 } 1249 1250 LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_polygon(lv_opa_t * mask_buf, lv_coord_t abs_x, 1251 lv_coord_t abs_y, lv_coord_t len, 1252 lv_draw_mask_polygon_param_t * param) 1253 { 1254 uint16_t i; 1255 struct { 1256 lv_point_t p1; 1257 lv_point_t p2; 1258 } lines[2], tmp; 1259 uint16_t line_cnt = 0; 1260 lv_memset_00(&lines, sizeof(lines)); 1261 int psign_prev = 0; 1262 for(i = 0; i < param->cfg.point_cnt; i++) { 1263 lv_point_t p1 = param->cfg.points[i]; 1264 lv_point_t p2 = param->cfg.points[i + 1 < param->cfg.point_cnt ? i + 1 : 0]; 1265 int pdiff = p1.y - p2.y, psign = pdiff / LV_ABS(pdiff); 1266 if(pdiff > 0) { 1267 if(abs_y > p1.y || abs_y < p2.y) continue; 1268 lines[line_cnt].p1 = p2; 1269 lines[line_cnt].p2 = p1; 1270 } 1271 else { 1272 if(abs_y < p1.y || abs_y > p2.y) continue; 1273 lines[line_cnt].p1 = p1; 1274 lines[line_cnt].p2 = p2; 1275 } 1276 if(psign_prev && psign_prev == psign) continue; 1277 psign_prev = psign; 1278 line_cnt++; 1279 if(line_cnt == 2) break; 1280 } 1281 if(line_cnt != 2) return LV_DRAW_MASK_RES_TRANSP; 1282 if(lines[0].p1.x > lines[1].p1.x || lines[0].p2.x > lines[1].p2.x) { 1283 tmp = lines[0]; 1284 lines[0] = lines[1]; 1285 lines[1] = tmp; 1286 } 1287 lv_draw_mask_line_param_t line_p; 1288 lv_draw_mask_line_points_init(&line_p, lines[0].p1.x, lines[0].p1.y, lines[0].p2.x, lines[0].p2.y, 1289 LV_DRAW_MASK_LINE_SIDE_RIGHT); 1290 if(line_p.steep == 0 && line_p.flat) { 1291 lv_coord_t x1 = LV_MIN(lines[0].p1.x, lines[0].p2.x); 1292 lv_coord_t x2 = LV_MAX(lines[0].p1.x, lines[0].p2.x); 1293 for(i = 0; i < len; i++) { 1294 mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF); 1295 } 1296 lv_draw_mask_free_param(&line_p); 1297 return LV_DRAW_MASK_RES_CHANGED; 1298 } 1299 lv_draw_mask_res_t res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p); 1300 lv_draw_mask_free_param(&line_p); 1301 if(res1 == LV_DRAW_MASK_RES_TRANSP) { 1302 return LV_DRAW_MASK_RES_TRANSP; 1303 } 1304 lv_draw_mask_line_points_init(&line_p, lines[1].p1.x, lines[1].p1.y, lines[1].p2.x, lines[1].p2.y, 1305 LV_DRAW_MASK_LINE_SIDE_LEFT); 1306 if(line_p.steep == 0 && line_p.flat) { 1307 lv_coord_t x1 = LV_MIN(lines[1].p1.x, lines[1].p2.x); 1308 lv_coord_t x2 = LV_MAX(lines[1].p1.x, lines[1].p2.x); 1309 for(i = 0; i < len; i++) { 1310 mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF); 1311 } 1312 lv_draw_mask_free_param(&line_p); 1313 return LV_DRAW_MASK_RES_CHANGED; 1314 } 1315 lv_draw_mask_res_t res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p); 1316 lv_draw_mask_free_param(&line_p); 1317 if(res2 == LV_DRAW_MASK_RES_TRANSP) { 1318 return LV_DRAW_MASK_RES_TRANSP; 1319 } 1320 if(res1 == LV_DRAW_MASK_RES_CHANGED || res2 == LV_DRAW_MASK_RES_CHANGED) return LV_DRAW_MASK_RES_CHANGED; 1321 return res1; 1322 } 1323 /** 1324 * Initialize the circle drawing 1325 * @param c pointer to a point. The coordinates will be calculated here 1326 * @param tmp point to a variable. It will store temporary data 1327 * @param radius radius of the circle 1328 */ 1329 static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius) 1330 { 1331 c->x = radius; 1332 c->y = 0; 1333 *tmp = 1 - radius; 1334 } 1335 1336 /** 1337 * Test the circle drawing is ready or not 1338 * @param c same as in circ_init 1339 * @return true if the circle is not ready yet 1340 */ 1341 static bool circ_cont(lv_point_t * c) 1342 { 1343 return c->y <= c->x ? true : false; 1344 } 1345 1346 /** 1347 * Get the next point from the circle 1348 * @param c same as in circ_init. The next point stored here. 1349 * @param tmp same as in circ_init. 1350 */ 1351 static void circ_next(lv_point_t * c, lv_coord_t * tmp) 1352 { 1353 1354 if(*tmp <= 0) { 1355 (*tmp) += 2 * c->y + 3; /*Change in decision criterion for y -> y+1*/ 1356 } 1357 else { 1358 (*tmp) += 2 * (c->y - c->x) + 5; /*Change for y -> y+1, x -> x-1*/ 1359 c->x--; 1360 } 1361 c->y++; 1362 } 1363 1364 static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius) 1365 { 1366 if(radius == 0) return; 1367 c->radius = radius; 1368 1369 /*Allocate buffers*/ 1370 if(c->buf) lv_mem_free(c->buf); 1371 1372 c->buf = lv_mem_alloc(radius * 6 + 6); /*Use uint16_t for opa_start_on_y and x_start_on_y*/ 1373 LV_ASSERT_MALLOC(c->buf); 1374 c->cir_opa = c->buf; 1375 c->opa_start_on_y = (uint16_t *)(c->buf + 2 * radius + 2); 1376 c->x_start_on_y = (uint16_t *)(c->buf + 4 * radius + 4); 1377 1378 /*Special case, handle manually*/ 1379 if(radius == 1) { 1380 c->cir_opa[0] = 180; 1381 c->opa_start_on_y[0] = 0; 1382 c->opa_start_on_y[1] = 1; 1383 c->x_start_on_y[0] = 0; 1384 return; 1385 } 1386 1387 lv_coord_t * cir_x = lv_mem_buf_get((radius + 1) * 2 * 2 * sizeof(lv_coord_t)); 1388 lv_coord_t * cir_y = &cir_x[(radius + 1) * 2]; 1389 1390 uint32_t y_8th_cnt = 0; 1391 lv_point_t cp; 1392 lv_coord_t tmp; 1393 circ_init(&cp, &tmp, radius * 4); /*Upscale by 4*/ 1394 int32_t i; 1395 1396 uint32_t x_int[4]; 1397 uint32_t x_fract[4]; 1398 lv_coord_t cir_size = 0; 1399 x_int[0] = cp.x >> 2; 1400 x_fract[0] = 0; 1401 1402 /*Calculate an 1/8 circle*/ 1403 while(circ_cont(&cp)) { 1404 /*Calculate 4 point of the circle */ 1405 for(i = 0; i < 4; i++) { 1406 circ_next(&cp, &tmp); 1407 if(circ_cont(&cp) == false) break; 1408 x_int[i] = cp.x >> 2; 1409 x_fract[i] = cp.x & 0x3; 1410 } 1411 if(i != 4) break; 1412 1413 /*All lines on the same x when downscaled*/ 1414 if(x_int[0] == x_int[3]) { 1415 cir_x[cir_size] = x_int[0]; 1416 cir_y[cir_size] = y_8th_cnt; 1417 c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2] + x_fract[3]; 1418 c->cir_opa[cir_size] *= 16; 1419 cir_size++; 1420 } 1421 /*Second line on new x when downscaled*/ 1422 else if(x_int[0] != x_int[1]) { 1423 cir_x[cir_size] = x_int[0]; 1424 cir_y[cir_size] = y_8th_cnt; 1425 c->cir_opa[cir_size] = x_fract[0]; 1426 c->cir_opa[cir_size] *= 16; 1427 cir_size++; 1428 1429 cir_x[cir_size] = x_int[0] - 1; 1430 cir_y[cir_size] = y_8th_cnt; 1431 c->cir_opa[cir_size] = 1 * 4 + x_fract[1] + x_fract[2] + x_fract[3];; 1432 c->cir_opa[cir_size] *= 16; 1433 cir_size++; 1434 } 1435 /*Third line on new x when downscaled*/ 1436 else if(x_int[0] != x_int[2]) { 1437 cir_x[cir_size] = x_int[0]; 1438 cir_y[cir_size] = y_8th_cnt; 1439 c->cir_opa[cir_size] = x_fract[0] + x_fract[1]; 1440 c->cir_opa[cir_size] *= 16; 1441 cir_size++; 1442 1443 cir_x[cir_size] = x_int[0] - 1; 1444 cir_y[cir_size] = y_8th_cnt; 1445 c->cir_opa[cir_size] = 2 * 4 + x_fract[2] + x_fract[3];; 1446 c->cir_opa[cir_size] *= 16; 1447 cir_size++; 1448 } 1449 /*Forth line on new x when downscaled*/ 1450 else { 1451 cir_x[cir_size] = x_int[0]; 1452 cir_y[cir_size] = y_8th_cnt; 1453 c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2]; 1454 c->cir_opa[cir_size] *= 16; 1455 cir_size++; 1456 1457 cir_x[cir_size] = x_int[0] - 1; 1458 cir_y[cir_size] = y_8th_cnt; 1459 c->cir_opa[cir_size] = 3 * 4 + x_fract[3];; 1460 c->cir_opa[cir_size] *= 16; 1461 cir_size++; 1462 } 1463 1464 y_8th_cnt++; 1465 } 1466 1467 /*The point on the 1/8 circle is special, calculate it manually*/ 1468 int32_t mid = radius * 723; 1469 int32_t mid_int = mid >> 10; 1470 if(cir_x[cir_size - 1] != mid_int || cir_y[cir_size - 1] != mid_int) { 1471 int32_t tmp_val = mid - (mid_int << 10); 1472 if(tmp_val <= 512) { 1473 tmp_val = tmp_val * tmp_val * 2; 1474 tmp_val = tmp_val >> (10 + 6); 1475 } 1476 else { 1477 tmp_val = 1024 - tmp_val; 1478 tmp_val = tmp_val * tmp_val * 2; 1479 tmp_val = tmp_val >> (10 + 6); 1480 tmp_val = 15 - tmp_val; 1481 } 1482 1483 cir_x[cir_size] = mid_int; 1484 cir_y[cir_size] = mid_int; 1485 c->cir_opa[cir_size] = tmp_val; 1486 c->cir_opa[cir_size] *= 16; 1487 cir_size++; 1488 } 1489 1490 /*Build the second octet by mirroring the first*/ 1491 for(i = cir_size - 2; i >= 0; i--, cir_size++) { 1492 cir_x[cir_size] = cir_y[i]; 1493 cir_y[cir_size] = cir_x[i]; 1494 c->cir_opa[cir_size] = c->cir_opa[i]; 1495 } 1496 1497 lv_coord_t y = 0; 1498 i = 0; 1499 c->opa_start_on_y[0] = 0; 1500 while(i < cir_size) { 1501 c->opa_start_on_y[y] = i; 1502 c->x_start_on_y[y] = cir_x[i]; 1503 for(; cir_y[i] == y && i < (int32_t)cir_size; i++) { 1504 c->x_start_on_y[y] = LV_MIN(c->x_start_on_y[y], cir_x[i]); 1505 } 1506 y++; 1507 } 1508 1509 lv_mem_buf_release(cir_x); 1510 } 1511 1512 static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len, 1513 lv_coord_t * x_start) 1514 { 1515 *len = c->opa_start_on_y[y + 1] - c->opa_start_on_y[y]; 1516 *x_start = c->x_start_on_y[y]; 1517 return &c->cir_opa[c->opa_start_on_y[y]]; 1518 } 1519 1520 1521 LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new) 1522 { 1523 if(mask_new >= LV_OPA_MAX) return mask_act; 1524 if(mask_new <= LV_OPA_MIN) return 0; 1525 1526 return LV_UDIV255(mask_act * mask_new);// >> 8); 1527 } 1528 1529 1530 #endif /*LV_DRAW_COMPLEX*/