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(&param->start_line, vertex_x, vertex_y, start_angle, start_side);
    464     lv_draw_mask_line_angle_init(&param->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(&param->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(&param->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(&param->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*/