1 /**
2  * @file lv_draw_triangle.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_triangle.h"
10 #include "../lv_misc/lv_math.h"
11 
12 /*********************
13  *      DEFINES
14  *********************/
15 
16 /**********************
17  *      TYPEDEFS
18  **********************/
19 
20 /**********************
21  *  STATIC PROTOTYPES
22  **********************/
23 void tri_draw_flat(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa);
24 void tri_draw_tall(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa);
25 static void point_swap(lv_point_t * p1, lv_point_t * p2);
26 
27 /**********************
28  *  STATIC VARIABLES
29  **********************/
30 
31 /**********************
32  *      MACROS
33  **********************/
34 
35 /**********************
36  *   GLOBAL FUNCTIONS
37  **********************/
38 
39 /**
40  *
41  * @param points pointer to an array with 3 points
42  * @param mask the triangle will be drawn only in this mask
43  * @param style style for of the triangle
44  * @param opa_scale scale down all opacities by the factor (0..255)
45  */
lv_draw_triangle(const lv_point_t * points,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)46 void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
47 {
48 
49     /*Return is the triangle is degenerated*/
50     if(points[0].x == points[1].x && points[0].y == points[1].y) return;
51     if(points[1].x == points[2].x && points[1].y == points[2].y) return;
52     if(points[0].x == points[2].x && points[0].y == points[2].y) return;
53 
54     if(points[0].x == points[1].x && points[1].x == points[2].x) return;
55     if(points[0].y == points[1].y && points[1].y == points[2].y) return;
56 
57     lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t)style->body.opa * opa_scale) >> 8;
58 
59     /*Is the triangle flat or tall?*/
60     lv_coord_t x_min = LV_MATH_MIN(LV_MATH_MIN(points[0].x, points[1].x), points[2].x);
61     lv_coord_t x_max = LV_MATH_MAX(LV_MATH_MAX(points[0].x, points[1].x), points[2].x);
62     lv_coord_t y_min = LV_MATH_MIN(LV_MATH_MIN(points[0].y, points[1].y), points[2].y);
63     lv_coord_t y_max = LV_MATH_MAX(LV_MATH_MAX(points[0].y, points[1].y), points[2].y);
64 
65     /* Draw the tall rectangles from vertical lines
66      * and from the flat triangles from horizontal lines
67      * to minimize the number of lines.
68      * Some pixels are overdrawn on the common edges of the triangles
69      * so use it only if the triangle has no opacity*/
70 
71     /* Draw from horizontal lines*/
72     if(x_max - x_min < y_max - y_min) {
73         tri_draw_tall(points, mask, style, opa);
74     }
75     /*Else flat so draw from vertical lines*/
76     else {
77         tri_draw_flat(points, mask, style, opa);
78     }
79 }
80 
81 /**
82  * Draw a polygon from triangles. Only convex polygons are supported
83  * @param points an array of points
84  * @param point_cnt number of points
85  * @param mask polygon will be drawn only in this mask
86  * @param style style of the polygon
87  * @param opa_scale scale down all opacities by the factor (0..255)
88  */
lv_draw_polygon(const lv_point_t * points,uint32_t point_cnt,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)89 void lv_draw_polygon(const lv_point_t * points, uint32_t point_cnt, const lv_area_t * mask, const lv_style_t * style,
90                      lv_opa_t opa_scale)
91 {
92     if(point_cnt < 3) return;
93     if(points == NULL) return;
94 
95     uint32_t i;
96     lv_point_t tri[3];
97     tri[0].x = points[0].x;
98     tri[0].y = points[0].y;
99     for(i = 0; i < point_cnt - 1; i++) {
100         tri[1].x = points[i].x;
101         tri[1].y = points[i].y;
102         tri[2].x = points[i + 1].x;
103         tri[2].y = points[i + 1].y;
104         lv_draw_triangle(tri, mask, style, opa_scale);
105     }
106 }
107 
108 /**********************
109  *   STATIC FUNCTIONS
110  **********************/
111 
tri_draw_flat(const lv_point_t * points,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa)112 void tri_draw_flat(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa)
113 {
114     /*Return if the points are out of the mask*/
115     if(points[0].x < mask->x1 && points[1].x < mask->x1 && points[2].x < mask->x1) {
116         return;
117     }
118 
119     if(points[0].x > mask->x2 && points[1].x > mask->x2 && points[2].x > mask->x2) {
120         return;
121     }
122 
123     if(points[0].y < mask->y1 && points[1].y < mask->y1 && points[2].y < mask->y1) {
124         return;
125     }
126 
127     if(points[0].y > mask->y2 && points[1].y > mask->y2 && points[2].y > mask->y2) {
128         return;
129     }
130 
131     lv_point_t tri[3];
132 
133     memcpy(tri, points, sizeof(tri));
134 
135     /*Sort the vertices according to their y coordinate (0: y max, 1: y mid, 2:y min)*/
136     if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
137     if(tri[2].y < tri[1].y) point_swap(&tri[2], &tri[1]);
138     if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
139 
140     /*Draw the triangle*/
141     lv_point_t edge1;
142     lv_coord_t dx1  = LV_MATH_ABS(tri[0].x - tri[1].x);
143     lv_coord_t sx1  = tri[0].x < tri[1].x ? 1 : -1;
144     lv_coord_t dy1  = LV_MATH_ABS(tri[0].y - tri[1].y);
145     lv_coord_t sy1  = tri[0].y < tri[1].y ? 1 : -1;
146     lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
147     lv_coord_t err_tmp1;
148 
149     lv_point_t edge2;
150     lv_coord_t dx2  = LV_MATH_ABS(tri[0].x - tri[2].x);
151     lv_coord_t sx2  = tri[0].x < tri[2].x ? 1 : -1;
152     lv_coord_t dy2  = LV_MATH_ABS(tri[0].y - tri[2].y);
153     lv_coord_t sy2  = tri[0].y < tri[2].y ? 1 : -1;
154     lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
155     lv_coord_t err_tmp2;
156 
157     lv_coord_t y1_tmp;
158     lv_coord_t y2_tmp;
159 
160     edge1.x = tri[0].x;
161     edge1.y = tri[0].y;
162     edge2.x = tri[0].x;
163     edge2.y = tri[0].y;
164     lv_area_t act_area;
165     lv_area_t draw_area;
166 
167     while(1) {
168         act_area.x1 = edge1.x;
169         act_area.x2 = edge2.x;
170         act_area.y1 = edge1.y;
171         act_area.y2 = edge2.y;
172 
173         /* Get the area of a line.
174          * Adjust it a little bit to perfectly match (no redrawn pixels) with the adjacent triangles*/
175         draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2) + 1;
176         draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
177         draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2) - 1;
178         draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2) - 1;
179 
180         lv_draw_fill(&draw_area, mask, style->body.main_color, opa);
181 
182         /*Calc. the next point of edge1*/
183         y1_tmp = edge1.y;
184         do {
185             if(edge1.x == tri[1].x && edge1.y == tri[1].y) {
186 
187                 dx1  = LV_MATH_ABS(tri[1].x - tri[2].x);
188                 sx1  = tri[1].x < tri[2].x ? 1 : -1;
189                 dy1  = LV_MATH_ABS(tri[1].y - tri[2].y);
190                 sy1  = tri[1].y < tri[2].y ? 1 : -1;
191                 err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
192             } else if(edge1.x == tri[2].x && edge1.y == tri[2].y) {
193                 return;
194             }
195             err_tmp1 = err1;
196             if(err_tmp1 > -dx1) {
197                 err1 -= dy1;
198                 edge1.x += sx1;
199             }
200             if(err_tmp1 < dy1) {
201                 err1 += dx1;
202                 edge1.y += sy1;
203             }
204         } while(edge1.y == y1_tmp);
205 
206         /*Calc. the next point of edge2*/
207         y2_tmp = edge2.y;
208         do {
209             if(edge2.x == tri[2].x && edge2.y == tri[2].y) return;
210             err_tmp2 = err2;
211             if(err_tmp2 > -dx2) {
212                 err2 -= dy2;
213                 edge2.x += sx2;
214             }
215             if(err_tmp2 < dy2) {
216                 err2 += dx2;
217                 edge2.y += sy2;
218             }
219         } while(edge2.y == y2_tmp);
220     }
221 }
222 
tri_draw_tall(const lv_point_t * points,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa)223 void tri_draw_tall(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa)
224 {
225     /*
226      * Better to draw from vertical lines
227      * |\
228      * | |
229      * | |
230      * |  \
231      * |   |
232      * |___|
233      */
234 
235     lv_point_t tri[3];
236 
237     memcpy(tri, points, sizeof(tri));
238 
239     /*Sort the vertices according to their x coordinate (0: x max, 1: x mid, 2:x min)*/
240     if(tri[1].x < tri[0].x) point_swap(&tri[1], &tri[0]);
241     if(tri[2].x < tri[1].x) point_swap(&tri[2], &tri[1]);
242     if(tri[1].x < tri[0].x) point_swap(&tri[1], &tri[0]);
243 
244     /*Draw the triangle*/
245     lv_point_t edge1;
246     lv_coord_t dx1  = LV_MATH_ABS(tri[0].x - tri[1].x);
247     lv_coord_t sx1  = tri[0].x < tri[1].x ? 1 : -1;
248     lv_coord_t dy1  = LV_MATH_ABS(tri[0].y - tri[1].y);
249     lv_coord_t sy1  = tri[0].y < tri[1].y ? 1 : -1;
250     lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
251     lv_coord_t err_tmp1;
252 
253     lv_point_t edge2;
254     lv_coord_t dx2  = LV_MATH_ABS(tri[0].x - tri[2].x);
255     lv_coord_t sx2  = tri[0].x < tri[2].x ? 1 : -1;
256     lv_coord_t dy2  = LV_MATH_ABS(tri[0].y - tri[2].y);
257     lv_coord_t sy2  = tri[0].y < tri[2].y ? 1 : -1;
258     lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
259     lv_coord_t err_tmp2;
260 
261     lv_coord_t x1_tmp;
262     lv_coord_t x2_tmp;
263 
264     edge1.x = tri[0].x;
265     edge1.y = tri[0].y;
266     edge2.x = tri[0].x;
267     edge2.y = tri[0].y;
268     lv_area_t act_area;
269     lv_area_t draw_area;
270 
271     while(1) {
272         act_area.x1 = edge1.x;
273         act_area.x2 = edge2.x;
274         act_area.y1 = edge1.y;
275         act_area.y2 = edge2.y;
276 
277         draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
278         draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
279         draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
280         draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2) - 1;
281 
282         lv_draw_fill(&draw_area, mask, style->body.main_color, opa);
283 
284         /*Calc. the next point of edge1*/
285         x1_tmp = edge1.x;
286         do {
287             if(edge1.y == tri[1].y && edge1.x == tri[1].x) {
288 
289                 dx1  = LV_MATH_ABS(tri[1].x - tri[2].x);
290                 sx1  = tri[1].x < tri[2].x ? 1 : -1;
291                 dy1  = LV_MATH_ABS(tri[1].y - tri[2].y);
292                 sy1  = tri[1].y < tri[2].y ? 1 : -1;
293                 err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
294             } else if(edge1.y == tri[2].y && edge1.x == tri[2].x) {
295                 return;
296             }
297             err_tmp1 = err1;
298             if(err_tmp1 > -dx1) {
299                 err1 -= dy1;
300                 edge1.x += sx1;
301             }
302             if(err_tmp1 < dy1) {
303                 err1 += dx1;
304                 edge1.y += sy1;
305             }
306         } while(edge1.x == x1_tmp);
307 
308         /*Calc. the next point of edge2*/
309         x2_tmp = edge2.x;
310         do {
311             if(edge2.y == tri[2].y && edge2.x == tri[2].x) {
312                 return;
313             }
314 
315             err_tmp2 = err2;
316             if(err_tmp2 > -dx2) {
317                 err2 -= dy2;
318                 edge2.x += sx2;
319             }
320             if(err_tmp2 < dy2) {
321                 err2 += dx2;
322                 edge2.y += sy2;
323             }
324         } while(edge2.x == x2_tmp);
325     }
326 }
327 
328 /**
329  * Swap two points
330  * p1 pointer to the first point
331  * p2 pointer to the second point
332  */
point_swap(lv_point_t * p1,lv_point_t * p2)333 static void point_swap(lv_point_t * p1, lv_point_t * p2)
334 {
335     lv_point_t tmp;
336     tmp.x = p1->x;
337     tmp.y = p1->y;
338 
339     p1->x = p2->x;
340     p1->y = p2->y;
341 
342     p2->x = tmp.x;
343     p2->y = tmp.y;
344 }
345