1 /**
2  * @file lv_draw_line.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include <stdio.h>
10 #include <stdbool.h>
11 #include "lv_draw.h"
12 #include "../lv_core/lv_refr.h"
13 #include "../lv_misc/lv_math.h"
14 
15 /*********************
16  *      DEFINES
17  *********************/
18 
19 /**********************
20  *      TYPEDEFS
21  **********************/
22 
23 typedef struct
24 {
25     lv_point_t p1;
26     lv_point_t p2;
27     lv_point_t p_act;
28     lv_coord_t dx;
29     lv_coord_t sx; /*-1: x1 < x2; 1: x2 >= x1*/
30     lv_coord_t dy;
31     lv_coord_t sy; /*-1: y1 < y2; 1: y2 >= y1*/
32     lv_coord_t err;
33     lv_coord_t e2;
34     bool hor; /*Rather horizontal or vertical*/
35 } line_draw_t;
36 
37 typedef struct
38 {
39     lv_coord_t width;
40     lv_coord_t width_1;
41     lv_coord_t width_half;
42 } line_width_t;
43 
44 /**********************
45  *  STATIC PROTOTYPES
46  **********************/
47 static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style,
48                           lv_opa_t opa_scale);
49 static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style,
50                           lv_opa_t opa_scale);
51 static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style,
52                            lv_opa_t opa_scale);
53 static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2);
54 static bool line_next(line_draw_t * line);
55 static bool line_next_y(line_draw_t * line);
56 static bool line_next_x(line_draw_t * line);
57 
58 /**********************
59  *  STATIC VARIABLES
60  **********************/
61 
62 /**********************
63  *      MACROS
64  **********************/
65 
66 /**********************
67  *   GLOBAL FUNCTIONS
68  **********************/
69 
70 /**
71  * Draw a line
72  * @param point1 first point of the line
73  * @param point2 second point of the line
74  * @param mask the line will be drawn only on this area
75  * @param style pointer to a line's style
76  * @param opa_scale scale down all opacities by the factor
77  */
lv_draw_line(const lv_point_t * point1,const lv_point_t * point2,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)78 void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
79                   const lv_style_t * style, lv_opa_t opa_scale)
80 {
81 
82     if(style->line.width == 0) return;
83     if(point1->x == point2->x && point1->y == point2->y) return;
84 
85     /*Return if the points are out of the mask*/
86     if(point1->x < mask->x1 - style->line.width && point2->x < mask->x1 - style->line.width) return;
87     if(point1->x > mask->x2 + style->line.width && point2->x > mask->x2 + style->line.width) return;
88     if(point1->y < mask->y1 - style->line.width && point2->y < mask->y1 - style->line.width) return;
89     if(point1->y > mask->y2 + style->line.width && point2->y > mask->y2 + style->line.width) return;
90 
91     line_draw_t main_line;
92     lv_point_t p1;
93     lv_point_t p2;
94 
95     /*If the line if rather vertical then be sure y1 < y2 else x1 < x2*/
96 
97     if(LV_MATH_ABS(point1->x - point2->x) > LV_MATH_ABS(point1->y - point2->y)) {
98 
99         /*Steps less in y then x -> rather horizontal*/
100         if(point1->x < point2->x) {
101             p1.x = point1->x;
102             p1.y = point1->y;
103             p2.x = point2->x;
104             p2.y = point2->y;
105         } else {
106             p1.x = point2->x;
107             p1.y = point2->y;
108             p2.x = point1->x;
109             p2.y = point1->y;
110         }
111     } else {
112         /*Steps less in x then y -> rather vertical*/
113         if(point1->y < point2->y) {
114             p1.x = point1->x;
115             p1.y = point1->y;
116             p2.x = point2->x;
117             p2.y = point2->y;
118         } else {
119             p1.x = point2->x;
120             p1.y = point2->y;
121             p2.x = point1->x;
122             p2.y = point1->y;
123         }
124     }
125 
126     line_init(&main_line, &p1, &p2);
127 
128     /*Special case draw a horizontal line*/
129     if(main_line.p1.y == main_line.p2.y) {
130         line_draw_hor(&main_line, mask, style, opa_scale);
131     }
132     /*Special case draw a vertical line*/
133     else if(main_line.p1.x == main_line.p2.x) {
134         line_draw_ver(&main_line, mask, style, opa_scale);
135     }
136     /*Arbitrary skew line*/
137     else {
138         bool dir_ori = false;
139 #if LV_ANTIALIAS
140         bool aa = lv_disp_get_antialiasing(lv_refr_get_disp_refreshing());
141         if(aa) {
142             lv_point_t p_tmp;
143 
144             if(main_line.hor) {
145                 if(main_line.p1.y < main_line.p2.y) {
146                     dir_ori = true;
147                     p_tmp.x = main_line.p2.x;
148                     p_tmp.y = main_line.p2.y - 1;
149                     line_init(&main_line, &p1, &p_tmp);
150                     main_line.sy = LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
151                 } else if(main_line.p1.y > main_line.p2.y) {
152                     dir_ori = false;
153                     p_tmp.x = main_line.p2.x;
154                     p_tmp.y = main_line.p2.y + 1;
155                     line_init(&main_line, &p1, &p_tmp);
156                     main_line.sy = -LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
157                 }
158             } else {
159                 if(main_line.p1.x < main_line.p2.x) {
160                     dir_ori = true;
161                     p_tmp.x = main_line.p2.x - 1;
162                     p_tmp.y = main_line.p2.y;
163                     line_init(&main_line, &p1, &p_tmp);
164                     main_line.sx = LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
165                 } else if(main_line.p1.x > main_line.p2.x) {
166                     dir_ori = false;
167                     p_tmp.x = main_line.p2.x + 1;
168                     p_tmp.y = main_line.p2.y;
169                     line_init(&main_line, &p1, &p_tmp);
170                     main_line.sx = -LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
171                 }
172             }
173         }
174 #endif
175         line_draw_skew(&main_line, dir_ori, mask, style, opa_scale);
176     }
177 }
178 
179 /**********************
180  *   STATIC FUNCTIONS
181  **********************/
182 
line_draw_hor(line_draw_t * main_line,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)183 static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
184 {
185     lv_coord_t width      = style->line.width - 1;
186     lv_coord_t width_half = width >> 1;
187     lv_coord_t width_1    = width & 0x1;
188     lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t)style->line.opa * opa_scale) >> 8;
189 
190     lv_area_t act_area;
191     act_area.x1 = main_line->p1.x;
192     act_area.x2 = main_line->p2.x;
193     act_area.y1 = main_line->p1.y - width_half - width_1;
194     act_area.y2 = main_line->p2.y + width_half;
195 
196     lv_area_t draw_area;
197     draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
198     draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
199     draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
200     draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
201     lv_draw_fill(&draw_area, mask, style->line.color, opa);
202 }
203 
line_draw_ver(line_draw_t * main_line,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)204 static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
205 {
206     lv_coord_t width      = style->line.width - 1;
207     lv_coord_t width_half = width >> 1;
208     lv_coord_t width_1    = width & 0x1;
209     lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t)style->line.opa * opa_scale) >> 8;
210 
211     lv_area_t act_area;
212     act_area.x1 = main_line->p1.x - width_half;
213     act_area.x2 = main_line->p2.x + width_half + width_1;
214     act_area.y1 = main_line->p1.y;
215     act_area.y2 = main_line->p2.y;
216 
217     lv_area_t draw_area;
218     draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
219     draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
220     draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
221     draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
222     lv_draw_fill(&draw_area, mask, style->line.color, opa);
223 }
224 
line_draw_skew(line_draw_t * main_line,bool dir_ori,const lv_area_t * mask,const lv_style_t * style,lv_opa_t opa_scale)225 static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style,
226                            lv_opa_t opa_scale)
227 {
228 
229     lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t)style->line.opa * opa_scale) >> 8;
230 #if LV_ANTIALIAS
231     bool aa = lv_disp_get_antialiasing(lv_refr_get_disp_refreshing());
232 #endif
233     lv_point_t vect_main, vect_norm;
234     vect_main.x = main_line->p2.x - main_line->p1.x;
235     vect_main.y = main_line->p2.y - main_line->p1.y;
236 
237     if(main_line->hor) {
238         if(main_line->p1.y < main_line->p2.y + dir_ori) {
239             vect_norm.x = -vect_main.y;
240             vect_norm.y = vect_main.x;
241         } else {
242             vect_norm.x = vect_main.y;
243             vect_norm.y = -vect_main.x;
244         }
245     } else {
246         if(main_line->p1.x < main_line->p2.x + dir_ori) {
247             vect_norm.x = vect_main.y;
248             vect_norm.y = -vect_main.x;
249         } else {
250             vect_norm.x = -vect_main.y;
251             vect_norm.y = vect_main.x;
252         }
253     }
254 
255     /* In case of a short but tick line the perpendicular ending is longer then the real line.
256      * it would break the calculations so make the normal vector larger*/
257     vect_norm.x = vect_norm.x << 4;
258     vect_norm.y = vect_norm.y << 4;
259 
260     lv_coord_t width;
261     width = style->line.width;
262 
263     /* The pattern stores the points of the line ending. It has the good direction and length.
264      * The worth case is the 45° line where pattern can have 1.41 x `width` points*/
265 
266     lv_coord_t pattern_size = width * 2;
267     lv_point_t * pattern = lv_draw_get_buf(pattern_size * sizeof(lv_point_t));
268     lv_coord_t i = 0;
269 
270     /*Create a perpendicular pattern (a small line)*/
271     if(width != 0) {
272         line_draw_t pattern_line;
273         lv_point_t p0 = {0, 0};
274         line_init(&pattern_line, &p0, &vect_norm);
275 
276         uint32_t width_sqr = width * width;
277         /* Run for a lot of times. Meanwhile the real width will be determined as well */
278         for(i = 0; i < (lv_coord_t)pattern_size - 1; i++) {
279             pattern[i].x = pattern_line.p_act.x;
280             pattern[i].y = pattern_line.p_act.y;
281 
282             /*Finish the pattern line if it's length equal to the desired width (Use Pythagoras
283              * theorem)*/
284             uint32_t sqr = pattern_line.p_act.x * pattern_line.p_act.x + pattern_line.p_act.y * pattern_line.p_act.y;
285             if(sqr >= width_sqr) {
286                 width = i;
287 #if LV_ANTIALIAS
288                 if(aa) width--;
289 #endif
290                 break;
291             }
292 
293             line_next(&pattern_line);
294         }
295     }
296 
297 #if LV_ANTIALIAS
298     lv_coord_t aa_last_corner;
299     lv_coord_t width_safe = width;
300     if(aa) {
301         if(width == 0) width_safe = 1;
302 
303         aa_last_corner = 0;
304     }
305 #endif
306 
307     lv_coord_t x_center_ofs = 0;
308     lv_coord_t y_center_ofs = 0;
309 
310     if(width != 0) {
311         x_center_ofs = pattern[width - 1].x / 2;
312         y_center_ofs = pattern[width - 1].y / 2;
313     } else {
314         if(main_line->hor && main_line->p1.y >= main_line->p2.y + dir_ori) pattern[0].y--;
315         if(!main_line->hor && main_line->p1.x >= main_line->p2.x + dir_ori) pattern[0].x--;
316     }
317 
318     /* Make the coordinates relative to the center */
319     for(i = 0; i < width; i++) {
320         pattern[i].x -= x_center_ofs;
321         pattern[i].y -= y_center_ofs;
322 #if LV_ANTIALIAS
323         if(aa) {
324             if(i != 0) {
325                 if(main_line->hor) {
326                     if(pattern[i - 1].x != pattern[i].x) {
327                         lv_coord_t seg_w = pattern[i].y - pattern[aa_last_corner].y;
328                         if(main_line->sy < 0) {
329                             lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1,
330                                                main_line->p1.y + pattern[aa_last_corner].y + seg_w + 1, seg_w, mask,
331                                                style->line.color, opa);
332 
333                             lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1,
334                                                main_line->p2.y + pattern[aa_last_corner].y + seg_w + 1, -seg_w, mask,
335                                                style->line.color, opa);
336                         } else {
337                             lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1,
338                                                main_line->p1.y + pattern[aa_last_corner].y, seg_w, mask,
339                                                style->line.color, opa);
340 
341                             lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1,
342                                                main_line->p2.y + pattern[aa_last_corner].y, -seg_w, mask,
343                                                style->line.color, opa);
344                         }
345                         aa_last_corner = i;
346                     }
347                 } else {
348                     if(pattern[i - 1].y != pattern[i].y) {
349                         lv_coord_t seg_w = pattern[i].x - pattern[aa_last_corner].x;
350                         if(main_line->sx < 0) {
351                             lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w + 1,
352                                                main_line->p1.y + pattern[aa_last_corner].y - 1, seg_w, mask,
353                                                style->line.color, opa);
354 
355                             lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w + 1,
356                                                main_line->p2.y + pattern[aa_last_corner].y + 1, -seg_w, mask,
357                                                style->line.color, opa);
358                         } else {
359                             lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x,
360                                                main_line->p1.y + pattern[aa_last_corner].y - 1, seg_w, mask,
361                                                style->line.color, opa);
362 
363                             lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x,
364                                                main_line->p2.y + pattern[aa_last_corner].y + 1, -seg_w, mask,
365                                                style->line.color, opa);
366                         }
367                         aa_last_corner = i;
368                     }
369                 }
370             }
371         }
372 #endif
373     }
374 
375 #if LV_ANTIALIAS
376     /*Add the last part of anti-aliasing for the perpendicular ending*/
377     if(width != 0 && aa) { /*Due to rounding error with very thin lines it looks ugly*/
378         if(main_line->hor) {
379             lv_coord_t seg_w = pattern[width_safe - 1].y - pattern[aa_last_corner].y;
380             if(main_line->sy < 0) {
381                 lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1,
382                                    main_line->p1.y + pattern[aa_last_corner].y + seg_w, seg_w + main_line->sy, mask,
383                                    style->line.color, opa);
384 
385                 lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1,
386                                    main_line->p2.y + pattern[aa_last_corner].y + seg_w, -(seg_w + main_line->sy), mask,
387                                    style->line.color, opa);
388 
389             } else {
390                 lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1,
391                                    main_line->p1.y + pattern[aa_last_corner].y, seg_w + main_line->sy, mask,
392                                    style->line.color, opa);
393 
394                 lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1,
395                                    main_line->p2.y + pattern[aa_last_corner].y, -(seg_w + main_line->sy), mask,
396                                    style->line.color, opa);
397             }
398         } else {
399             lv_coord_t seg_w = pattern[width_safe - 1].x - pattern[aa_last_corner].x;
400             if(main_line->sx < 0) {
401                 lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w,
402                                    main_line->p1.y + pattern[aa_last_corner].y - 1, seg_w + main_line->sx, mask,
403                                    style->line.color, opa);
404 
405                 lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w,
406                                    main_line->p2.y + pattern[aa_last_corner].y + 1, -(seg_w + main_line->sx), mask,
407                                    style->line.color, opa);
408 
409             } else {
410                 lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x,
411                                    main_line->p1.y + pattern[aa_last_corner].y - 1, seg_w + main_line->sx, mask,
412                                    style->line.color, opa);
413 
414                 lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x,
415                                    main_line->p2.y + pattern[aa_last_corner].y + 1, -(seg_w + main_line->sx), mask,
416                                    style->line.color, opa);
417             }
418         }
419     }
420 #endif
421 
422 #if LV_ANTIALIAS
423 
424     /*Shift the anti aliasing on the edges (-1, 1 or 0 (zero only in case width == 0))*/
425     lv_coord_t aa_shift1 = 0;
426     lv_coord_t aa_shift2 = 0;
427     if(aa) {
428         if(main_line->hor == false) {
429             if(main_line->sx < 0) {
430                 aa_shift1 = -1;
431                 aa_shift2 = width == 0 ? 0 : aa_shift1;
432             } else {
433                 aa_shift2 = 1;
434                 aa_shift1 = width == 0 ? 0 : aa_shift2;
435             }
436         } else {
437             if(main_line->sy < 0) {
438                 aa_shift1 = -1;
439                 aa_shift2 = width == 0 ? 0 : aa_shift1;
440             } else {
441                 aa_shift2 = 1;
442                 aa_shift1 = width == 0 ? 0 : aa_shift2;
443             }
444         }
445     }
446 #endif
447 
448     volatile lv_point_t prev_p;
449     prev_p.x = main_line->p1.x;
450     prev_p.y = main_line->p1.y;
451     lv_area_t draw_area;
452     bool first_run = true;
453 
454     if(main_line->hor) {
455         while(line_next_y(main_line)) {
456             for(i = 0; i < width; i++) {
457                 draw_area.x1 = prev_p.x + pattern[i].x;
458                 draw_area.y1 = prev_p.y + pattern[i].y;
459                 draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x - 1;
460                 draw_area.y2 = draw_area.y1;
461                 lv_draw_fill(&draw_area, mask, style->line.color, opa);
462 
463                 /* Fill the gaps
464                  * When stepping in y one pixel remains empty on every corner (don't do this on the
465                  * first segment ) */
466                 if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
467                     lv_draw_px(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
468                 }
469             }
470 
471 #if LV_ANTIALIAS
472             if(aa) {
473                 lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
474                                    -(main_line->p_act.x - prev_p.x), mask, style->line.color, opa);
475                 lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x,
476                                    prev_p.y + pattern[width_safe - 1].y + aa_shift2, main_line->p_act.x - prev_p.x,
477                                    mask, style->line.color, opa);
478             }
479 #endif
480 
481             first_run = false;
482 
483             prev_p.x = main_line->p_act.x;
484             prev_p.y = main_line->p_act.y;
485         }
486 
487         for(i = 0; i < width; i++) {
488             draw_area.x1 = prev_p.x + pattern[i].x;
489             draw_area.y1 = prev_p.y + pattern[i].y;
490             draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x;
491             draw_area.y2 = draw_area.y1;
492             lv_draw_fill(&draw_area, mask, style->line.color, opa);
493 
494             /* Fill the gaps
495              * When stepping in y one pixel remains empty on every corner */
496             if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
497                 lv_draw_px(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
498             }
499         }
500 
501 #if LV_ANTIALIAS
502         if(aa) {
503             lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
504                                -(main_line->p_act.x - prev_p.x + 1), mask, style->line.color, opa);
505             lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
506                                main_line->p_act.x - prev_p.x + 1, mask, style->line.color, opa);
507         }
508 #endif
509     }
510     /*Rather a vertical line*/
511     else {
512 
513         while(line_next_x(main_line)) {
514             for(i = 0; i < width; i++) {
515                 draw_area.x1 = prev_p.x + pattern[i].x;
516                 draw_area.y1 = prev_p.y + pattern[i].y;
517                 draw_area.x2 = draw_area.x1;
518                 draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y - 1;
519 
520                 lv_draw_fill(&draw_area, mask, style->line.color, opa);
521 
522                 /* Fill the gaps
523                  * When stepping in x one pixel remains empty on every corner (don't do this on the
524                  * first segment ) */
525                 if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
526                     lv_draw_px(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
527                 }
528             }
529 
530 #if LV_ANTIALIAS
531             if(aa) {
532                 lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
533                                    -(main_line->p_act.y - prev_p.y), mask, style->line.color, opa);
534                 lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2,
535                                    prev_p.y + pattern[width_safe - 1].y, main_line->p_act.y - prev_p.y, mask,
536                                    style->line.color, opa);
537             }
538 #endif
539 
540             first_run = false;
541 
542             prev_p.x = main_line->p_act.x;
543             prev_p.y = main_line->p_act.y;
544         }
545 
546         /*Draw the last part*/
547         for(i = 0; i < width; i++) {
548             draw_area.x1 = prev_p.x + pattern[i].x;
549             draw_area.y1 = prev_p.y + pattern[i].y;
550             draw_area.x2 = draw_area.x1;
551             draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y;
552 
553             lv_draw_fill(&draw_area, mask, style->line.color, opa);
554 
555             /* Fill the gaps
556              * When stepping in x one pixel remains empty on every corner */
557             if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
558                 lv_draw_px(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
559             }
560         }
561 
562 #if LV_ANTIALIAS
563         if(aa) {
564             lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
565                                -(main_line->p_act.y - prev_p.y + 1), mask, style->line.color, opa);
566             lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
567                                main_line->p_act.y - prev_p.y + 1, mask, style->line.color, opa);
568         }
569 #endif
570     }
571 }
572 
line_init(line_draw_t * line,const lv_point_t * p1,const lv_point_t * p2)573 static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2)
574 {
575     line->p1.x = p1->x;
576     line->p1.y = p1->y;
577     line->p2.x = p2->x;
578     line->p2.y = p2->y;
579 
580     line->dx  = LV_MATH_ABS(line->p2.x - line->p1.x);
581     line->sx  = line->p1.x < line->p2.x ? 1 : -1;
582     line->dy  = LV_MATH_ABS(line->p2.y - line->p1.y);
583     line->sy  = line->p1.y < line->p2.y ? 1 : -1;
584     line->err = (line->dx > line->dy ? line->dx : -line->dy) / 2;
585     line->e2  = 0;
586     line->hor = line->dx > line->dy ? true : false; /*Rather horizontal or vertical*/
587 
588     line->p_act.x = line->p1.x;
589     line->p_act.y = line->p1.y;
590 }
591 
line_next(line_draw_t * line)592 static bool line_next(line_draw_t * line)
593 {
594     if(line->p_act.x == line->p2.x && line->p_act.y == line->p2.y) return false;
595     line->e2 = line->err;
596     if(line->e2 > -line->dx) {
597         line->err -= line->dy;
598         line->p_act.x += line->sx;
599     }
600     if(line->e2 < line->dy) {
601         line->err += line->dx;
602         line->p_act.y += line->sy;
603     }
604     return true;
605 }
606 
607 /**
608  * Iterate until step one in y direction.
609  * @param line
610  * @return
611  */
line_next_y(line_draw_t * line)612 static bool line_next_y(line_draw_t * line)
613 {
614     lv_coord_t last_y = line->p_act.y;
615 
616     do {
617         if(!line_next(line)) return false;
618     } while(last_y == line->p_act.y);
619 
620     return true;
621 }
622 
623 /**
624  * Iterate until step one in x direction.
625  * @param line
626  * @return
627  */
line_next_x(line_draw_t * line)628 static bool line_next_x(line_draw_t * line)
629 {
630     lv_coord_t last_x = line->p_act.x;
631 
632     do {
633         if(!line_next(line)) return false;
634     } while(last_x == line->p_act.x);
635 
636     return true;
637 }
638