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