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  */
lv_draw_sw_polygon(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * draw_dsc,const lv_point_t * points,uint16_t point_cnt)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  **********************/
208