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_sw_polygon.c (6165B)

      1 /**
      2  * @file lv_draw_sw_polygon.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_draw_sw.h"
     10 #include "../../misc/lv_math.h"
     11 #include "../../misc/lv_mem.h"
     12 #include "../../misc/lv_area.h"
     13 #include "../../misc/lv_color.h"
     14 #include "../lv_draw_rect.h"
     15 
     16 /*********************
     17  *      DEFINES
     18  *********************/
     19 
     20 /**********************
     21  *      TYPEDEFS
     22  **********************/
     23 
     24 /**********************
     25  *  STATIC PROTOTYPES
     26  **********************/
     27 
     28 /**********************
     29  *  STATIC VARIABLES
     30  **********************/
     31 
     32 /**********************
     33  *      MACROS
     34  **********************/
     35 
     36 /**********************
     37  *   GLOBAL FUNCTIONS
     38  **********************/
     39 
     40 /**
     41  * Draw a polygon. Only convex polygons are supported
     42  * @param points an array of points
     43  * @param point_cnt number of points
     44  * @param clip_area polygon will be drawn only in this area
     45  * @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
     46  */
     47 void lv_draw_sw_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
     48                         uint16_t point_cnt)
     49 {
     50 #if LV_DRAW_COMPLEX
     51     if(point_cnt < 3) return;
     52     if(points == NULL) return;
     53 
     54     /*Join adjacent points if they are on the same coordinate*/
     55     lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t));
     56     if(p == NULL) return;
     57     uint16_t i;
     58     uint16_t pcnt = 0;
     59     p[0] = points[0];
     60     for(i = 0; i < point_cnt - 1; i++) {
     61         if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
     62             p[pcnt] = points[i];
     63             pcnt++;
     64         }
     65     }
     66     /*The first and the last points are also adjacent*/
     67     if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
     68         p[pcnt] = points[point_cnt - 1];
     69         pcnt++;
     70     }
     71 
     72     point_cnt = pcnt;
     73     if(point_cnt < 3) {
     74         lv_mem_buf_release(p);
     75         return;
     76     }
     77 
     78     lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
     79 
     80     for(i = 0; i < point_cnt; i++) {
     81         poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x);
     82         poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y);
     83         poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x);
     84         poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y);
     85     }
     86 
     87     bool is_common;
     88     lv_area_t clip_area;
     89     is_common = _lv_area_intersect(&clip_area, &poly_coords, draw_ctx->clip_area);
     90     if(!is_common) {
     91         lv_mem_buf_release(p);
     92         return;
     93     }
     94 
     95     const lv_area_t * clip_area_ori = draw_ctx->clip_area;
     96     draw_ctx->clip_area = &clip_area;
     97 
     98     /*Find the lowest point*/
     99     lv_coord_t y_min = p[0].y;
    100     int16_t y_min_i = 0;
    101 
    102     for(i = 1; i < point_cnt; i++) {
    103         if(p[i].y < y_min) {
    104             y_min = p[i].y;
    105             y_min_i = i;
    106         }
    107     }
    108 
    109     lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt);
    110     lv_draw_mask_line_param_t * mp_next = mp;
    111 
    112     int32_t i_prev_left = y_min_i;
    113     int32_t i_prev_right = y_min_i;
    114     int32_t i_next_left;
    115     int32_t i_next_right;
    116     uint32_t mask_cnt = 0;
    117 
    118     /*Get the index of the left and right points*/
    119     i_next_left = y_min_i - 1;
    120     if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
    121 
    122     i_next_right = y_min_i + 1;
    123     if(i_next_right > point_cnt - 1) i_next_right = 0;
    124 
    125     /**
    126      * Check if the order of points is inverted or not.
    127      * The normal case is when the left point is on `y_min_i - 1`
    128      * Explanation:
    129      *   if angle(p_left) < angle(p_right) -> inverted
    130      *   dy_left/dx_left < dy_right/dx_right
    131      *   dy_left * dx_right < dy_right * dx_left
    132      */
    133     lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x;
    134     lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x;
    135     lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y;
    136     lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y;
    137 
    138     bool inv = false;
    139     if(dyl * dxr < dyr * dxl) inv = true;
    140 
    141     do {
    142         if(!inv) {
    143             i_next_left = i_prev_left - 1;
    144             if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
    145 
    146             i_next_right = i_prev_right + 1;
    147             if(i_next_right > point_cnt - 1) i_next_right = 0;
    148         }
    149         else {
    150             i_next_left = i_prev_left + 1;
    151             if(i_next_left > point_cnt - 1) i_next_left = 0;
    152 
    153             i_next_right = i_prev_right - 1;
    154             if(i_next_right < 0) i_next_right = point_cnt + i_next_right;
    155         }
    156 
    157         if(p[i_next_left].y >= p[i_prev_left].y) {
    158             if(p[i_next_left].y != p[i_prev_left].y &&
    159                p[i_next_left].x != p[i_prev_left].x) {
    160                 lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y,
    161                                               p[i_next_left].x, p[i_next_left].y,
    162                                               LV_DRAW_MASK_LINE_SIDE_RIGHT);
    163                 lv_draw_mask_add(mp_next, mp);
    164                 mp_next++;
    165             }
    166             mask_cnt++;
    167             i_prev_left = i_next_left;
    168         }
    169 
    170         if(mask_cnt == point_cnt) break;
    171 
    172         if(p[i_next_right].y >= p[i_prev_right].y) {
    173             if(p[i_next_right].y != p[i_prev_right].y &&
    174                p[i_next_right].x != p[i_prev_right].x) {
    175 
    176                 lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y,
    177                                               p[i_next_right].x, p[i_next_right].y,
    178                                               LV_DRAW_MASK_LINE_SIDE_LEFT);
    179                 lv_draw_mask_add(mp_next, mp);
    180                 mp_next++;
    181             }
    182             mask_cnt++;
    183             i_prev_right = i_next_right;
    184         }
    185 
    186     } while(mask_cnt < point_cnt);
    187 
    188     lv_draw_rect(draw_ctx, draw_dsc, &poly_coords);
    189 
    190     lv_draw_mask_remove_custom(mp);
    191 
    192     lv_mem_buf_release(mp);
    193     lv_mem_buf_release(p);
    194 
    195     draw_ctx->clip_area = clip_area_ori;
    196 #else
    197     LV_UNUSED(points);
    198     LV_UNUSED(point_cnt);
    199     LV_UNUSED(draw_ctx);
    200     LV_UNUSED(draw_dsc);
    201     LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0");
    202 #endif /*LV_DRAW_COMPLEX*/
    203 }
    204 
    205 /**********************
    206  *   STATIC FUNCTIONS
    207  **********************/