1 /**
2 * @file lv_draw_sw_line.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../../misc/lv_area_private.h"
10 #include "lv_draw_sw_mask_private.h"
11 #include "blend/lv_draw_sw_blend_private.h"
12 #include "../lv_draw_private.h"
13 #include "lv_draw_sw.h"
14
15 #if LV_USE_DRAW_SW
16
17 #include "../../misc/lv_math.h"
18 #include "../../misc/lv_types.h"
19 #include "../../core/lv_refr_private.h"
20 #include "../../stdlib/lv_string.h"
21
22 /*********************
23 * DEFINES
24 *********************/
25
26 /**********************
27 * TYPEDEFS
28 **********************/
29
30 /**********************
31 * STATIC PROTOTYPES
32 **********************/
33
34 static void /* LV_ATTRIBUTE_FAST_MEM */ draw_line_skew(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc);
35 static void /* LV_ATTRIBUTE_FAST_MEM */ draw_line_hor(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc);
36 static void /* LV_ATTRIBUTE_FAST_MEM */ draw_line_ver(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc);
37
38 /**********************
39 * STATIC VARIABLES
40 **********************/
41
42 /**********************
43 * MACROS
44 **********************/
45
46 /**********************
47 * GLOBAL FUNCTIONS
48 **********************/
49
lv_draw_sw_line(lv_draw_unit_t * draw_unit,const lv_draw_line_dsc_t * dsc)50 void lv_draw_sw_line(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc)
51 {
52 if(dsc->width == 0) return;
53 if(dsc->opa <= LV_OPA_MIN) return;
54
55 if(dsc->p1.x == dsc->p2.x && dsc->p1.y == dsc->p2.y) return;
56
57 lv_area_t clip_line;
58 clip_line.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width / 2;
59 clip_line.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width / 2;
60 clip_line.y1 = (int32_t)LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width / 2;
61 clip_line.y2 = (int32_t)LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width / 2;
62
63 bool is_common;
64 is_common = lv_area_intersect(&clip_line, &clip_line, draw_unit->clip_area);
65 if(!is_common) return;
66
67 LV_PROFILER_DRAW_BEGIN;
68 if((int32_t)dsc->p1.y == (int32_t)dsc->p2.y) draw_line_hor(draw_unit, dsc);
69 else if((int32_t)dsc->p1.x == (int32_t)dsc->p2.x) draw_line_ver(draw_unit, dsc);
70 else draw_line_skew(draw_unit, dsc);
71
72 if(dsc->round_end || dsc->round_start) {
73 lv_draw_fill_dsc_t cir_dsc;
74 lv_draw_fill_dsc_init(&cir_dsc);
75 cir_dsc.color = dsc->color;
76 cir_dsc.radius = LV_RADIUS_CIRCLE;
77 cir_dsc.opa = dsc->opa;
78
79 int32_t r = (dsc->width >> 1);
80 int32_t r_corr = (dsc->width & 1) ? 0 : 1;
81 lv_area_t cir_area;
82
83 if(dsc->round_start) {
84 cir_area.x1 = (int32_t)dsc->p1.x - r;
85 cir_area.y1 = (int32_t)dsc->p1.y - r;
86 cir_area.x2 = (int32_t)dsc->p1.x + r - r_corr;
87 cir_area.y2 = (int32_t)dsc->p1.y + r - r_corr ;
88 lv_draw_sw_fill(draw_unit, &cir_dsc, &cir_area);
89 }
90
91 if(dsc->round_end) {
92 cir_area.x1 = (int32_t)dsc->p2.x - r;
93 cir_area.y1 = (int32_t)dsc->p2.y - r;
94 cir_area.x2 = (int32_t)dsc->p2.x + r - r_corr;
95 cir_area.y2 = (int32_t)dsc->p2.y + r - r_corr ;
96 lv_draw_sw_fill(draw_unit, &cir_dsc, &cir_area);
97 }
98 }
99 LV_PROFILER_DRAW_END;
100 }
101
102 /**********************
103 * STATIC FUNCTIONS
104 **********************/
draw_line_hor(lv_draw_unit_t * draw_unit,const lv_draw_line_dsc_t * dsc)105 static void LV_ATTRIBUTE_FAST_MEM draw_line_hor(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc)
106 {
107 int32_t w = dsc->width - 1;
108 int32_t w_half0 = w >> 1;
109 int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
110
111 lv_area_t blend_area;
112 blend_area.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x);
113 blend_area.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) - 1;
114 blend_area.y1 = (int32_t)dsc->p1.y - w_half1;
115 blend_area.y2 = (int32_t)dsc->p1.y + w_half0;
116
117 bool is_common;
118 is_common = lv_area_intersect(&blend_area, &blend_area, draw_unit->clip_area);
119 if(!is_common) return;
120
121 bool dashed = dsc->dash_gap && dsc->dash_width;
122
123 lv_draw_sw_blend_dsc_t blend_dsc;
124 lv_memzero(&blend_dsc, sizeof(blend_dsc));
125 blend_dsc.blend_area = &blend_area;
126 blend_dsc.color = dsc->color;
127 blend_dsc.opa = dsc->opa;
128
129 /*If there is no mask then simply draw a rectangle*/
130 if(!dashed) {
131 lv_draw_sw_blend(draw_unit, &blend_dsc);
132 }
133 #if LV_DRAW_SW_COMPLEX
134 /*If there other mask apply it*/
135 else {
136
137 int32_t blend_area_w = lv_area_get_width(&blend_area);
138
139 int32_t y2 = blend_area.y2;
140 blend_area.y2 = blend_area.y1;
141
142 int32_t dash_start = blend_area.x1 % (dsc->dash_gap + dsc->dash_width);
143
144 lv_opa_t * mask_buf = lv_malloc(blend_area_w);
145 blend_dsc.mask_buf = mask_buf;
146 blend_dsc.mask_area = &blend_area;
147 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
148 int32_t h;
149 for(h = blend_area.y1; h <= y2; h++) {
150 lv_memset(mask_buf, 0xff, blend_area_w);
151
152 int32_t dash_cnt = dash_start;
153 int32_t i;
154 for(i = 0; i < blend_area_w; i++, dash_cnt++) {
155 if(dash_cnt <= dsc->dash_width) {
156 int16_t diff = dsc->dash_width - dash_cnt;
157 i += diff;
158 dash_cnt += diff;
159 }
160 else if(dash_cnt > dsc->dash_gap + dsc->dash_width) {
161 dash_cnt = 0;
162 }
163 else {
164 mask_buf[i] = 0x00;
165 }
166
167 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
168 }
169
170 lv_draw_sw_blend(draw_unit, &blend_dsc);
171
172 blend_area.y1++;
173 blend_area.y2++;
174 }
175 lv_free(mask_buf);
176 }
177 #endif /*LV_DRAW_SW_COMPLEX*/
178 }
179
draw_line_ver(lv_draw_unit_t * draw_unit,const lv_draw_line_dsc_t * dsc)180 static void LV_ATTRIBUTE_FAST_MEM draw_line_ver(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc)
181 {
182 int32_t w = dsc->width - 1;
183 int32_t w_half0 = w >> 1;
184 int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
185
186 lv_area_t blend_area;
187 blend_area.x1 = (int32_t)dsc->p1.x - w_half1;
188 blend_area.x2 = (int32_t)dsc->p1.x + w_half0;
189 blend_area.y1 = (int32_t)LV_MIN(dsc->p1.y, dsc->p2.y);
190 blend_area.y2 = (int32_t)LV_MAX(dsc->p1.y, dsc->p2.y) - 1;
191
192 bool is_common;
193 is_common = lv_area_intersect(&blend_area, &blend_area, draw_unit->clip_area);
194 if(!is_common) return;
195
196 bool dashed = dsc->dash_gap && dsc->dash_width;
197
198 lv_draw_sw_blend_dsc_t blend_dsc;
199 lv_memzero(&blend_dsc, sizeof(blend_dsc));
200 blend_dsc.blend_area = &blend_area;
201 blend_dsc.color = dsc->color;
202 blend_dsc.opa = dsc->opa;
203
204 /*If there is no mask then simply draw a rectangle*/
205 if(!dashed) {
206 lv_draw_sw_blend(draw_unit, &blend_dsc);
207 }
208
209 #if LV_DRAW_SW_COMPLEX
210 /*If there other mask apply it*/
211 else {
212 int32_t draw_area_w = lv_area_get_width(&blend_area);
213
214 int32_t y2 = blend_area.y2;
215 blend_area.y2 = blend_area.y1;
216
217 lv_opa_t * mask_buf = lv_malloc(draw_area_w);
218 blend_dsc.mask_buf = mask_buf;
219 blend_dsc.mask_area = &blend_area;
220 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
221 int32_t dash_start = (blend_area.y1) % (dsc->dash_gap + dsc->dash_width);
222
223 int32_t dash_cnt = dash_start;
224
225 int32_t h;
226 for(h = blend_area.y1; h <= y2; h++) {
227 lv_memset(mask_buf, 0xff, draw_area_w);
228
229 if(dash_cnt > dsc->dash_width) {
230 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_TRANSP;
231 }
232 else {
233 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER;
234 }
235
236 if(dash_cnt >= dsc->dash_gap + dsc->dash_width) {
237 dash_cnt = 0;
238 }
239 dash_cnt ++;
240
241 lv_draw_sw_blend(draw_unit, &blend_dsc);
242
243 blend_area.y1++;
244 blend_area.y2++;
245 }
246 lv_free(mask_buf);
247 }
248 #endif /*LV_DRAW_SW_COMPLEX*/
249 }
250
draw_line_skew(lv_draw_unit_t * draw_unit,const lv_draw_line_dsc_t * dsc)251 static void LV_ATTRIBUTE_FAST_MEM draw_line_skew(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc)
252 {
253 #if LV_DRAW_SW_COMPLEX
254 /*Keep the great y in p1*/
255 lv_point_t p1;
256 lv_point_t p2;
257 if(dsc->p1.y < dsc->p2.y) {
258 p1 = lv_point_from_precise(&dsc->p1);
259 p2 = lv_point_from_precise(&dsc->p2);
260 }
261 else {
262 p1 = lv_point_from_precise(&dsc->p2);
263 p2 = lv_point_from_precise(&dsc->p1);
264 }
265
266 int32_t xdiff = p2.x - p1.x;
267 int32_t ydiff = p2.y - p1.y;
268 bool flat = LV_ABS(xdiff) > LV_ABS(ydiff);
269
270 static const uint8_t wcorr[] = {
271 128, 128, 128, 129, 129, 130, 130, 131,
272 132, 133, 134, 135, 137, 138, 140, 141,
273 143, 145, 147, 149, 151, 153, 155, 158,
274 160, 162, 165, 167, 170, 173, 175, 178,
275 181,
276 };
277
278 int32_t w = dsc->width;
279 int32_t wcorr_i = 0;
280 if(flat) wcorr_i = (LV_ABS(ydiff) << 5) / LV_ABS(xdiff);
281 else wcorr_i = (LV_ABS(xdiff) << 5) / LV_ABS(ydiff);
282
283 w = (w * wcorr[wcorr_i] + 63) >> 7; /*+ 63 for rounding*/
284 int32_t w_half0 = w >> 1;
285 int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
286
287 lv_area_t blend_area;
288 blend_area.x1 = LV_MIN(p1.x, p2.x) - w;
289 blend_area.x2 = LV_MAX(p1.x, p2.x) + w;
290 blend_area.y1 = LV_MIN(p1.y, p2.y) - w;
291 blend_area.y2 = LV_MAX(p1.y, p2.y) + w;
292
293 /*Get the union of `coords` and `clip`*/
294 /*`clip` is already truncated to the `draw_buf` size
295 *in 'lv_refr_area' function*/
296 bool is_common = lv_area_intersect(&blend_area, &blend_area, draw_unit->clip_area);
297 if(is_common == false) return;
298
299 lv_draw_sw_mask_line_param_t mask_left_param;
300 lv_draw_sw_mask_line_param_t mask_right_param;
301 lv_draw_sw_mask_line_param_t mask_top_param;
302 lv_draw_sw_mask_line_param_t mask_bottom_param;
303
304 void * masks[5] = {&mask_left_param, & mask_right_param, NULL, NULL, NULL};
305
306 if(flat) {
307 if(xdiff > 0) {
308 lv_draw_sw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
309 LV_DRAW_SW_MASK_LINE_SIDE_LEFT);
310 lv_draw_sw_mask_line_points_init(&mask_right_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
311 LV_DRAW_SW_MASK_LINE_SIDE_RIGHT);
312 }
313 else {
314 lv_draw_sw_mask_line_points_init(&mask_left_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
315 LV_DRAW_SW_MASK_LINE_SIDE_LEFT);
316 lv_draw_sw_mask_line_points_init(&mask_right_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
317 LV_DRAW_SW_MASK_LINE_SIDE_RIGHT);
318 }
319 }
320 else {
321 lv_draw_sw_mask_line_points_init(&mask_left_param, p1.x + w_half1, p1.y, p2.x + w_half1, p2.y,
322 LV_DRAW_SW_MASK_LINE_SIDE_LEFT);
323 lv_draw_sw_mask_line_points_init(&mask_right_param, p1.x - w_half0, p1.y, p2.x - w_half0, p2.y,
324 LV_DRAW_SW_MASK_LINE_SIDE_RIGHT);
325
326 }
327
328 /*Use the normal vector for the endings*/
329
330 if(!dsc->raw_end) {
331 lv_draw_sw_mask_line_points_init(&mask_top_param, p1.x, p1.y, p1.x - ydiff, p1.y + xdiff,
332 LV_DRAW_SW_MASK_LINE_SIDE_BOTTOM);
333 lv_draw_sw_mask_line_points_init(&mask_bottom_param, p2.x, p2.y, p2.x - ydiff, p2.y + xdiff,
334 LV_DRAW_SW_MASK_LINE_SIDE_TOP);
335 masks[2] = &mask_top_param;
336 masks[3] = &mask_bottom_param;
337 }
338
339 /*The real draw area is around the line.
340 *It's easy to calculate with steep lines, but the area can be very wide with very flat lines.
341 *So deal with it only with steep lines.*/
342 int32_t draw_area_w = lv_area_get_width(&blend_area);
343
344 /*Draw the background line by line*/
345 int32_t h;
346 uint32_t hor_res = (uint32_t)lv_display_get_horizontal_resolution(lv_refr_get_disp_refreshing());
347 size_t mask_buf_size = LV_MIN(lv_area_get_size(&blend_area), hor_res);
348 lv_opa_t * mask_buf = lv_malloc(mask_buf_size);
349
350 int32_t y2 = blend_area.y2;
351 blend_area.y2 = blend_area.y1;
352
353 uint32_t mask_p = 0;
354 lv_memset(mask_buf, 0xff, mask_buf_size);
355
356 lv_draw_sw_blend_dsc_t blend_dsc;
357 lv_memzero(&blend_dsc, sizeof(blend_dsc));
358 blend_dsc.blend_area = &blend_area;
359 blend_dsc.color = dsc->color;
360 blend_dsc.opa = dsc->opa;
361 blend_dsc.mask_buf = mask_buf;
362 blend_dsc.mask_area = &blend_area;
363
364 /*Fill the first row with 'color'*/
365 for(h = blend_area.y1; h <= y2; h++) {
366 blend_dsc.mask_res = lv_draw_sw_mask_apply(masks, &mask_buf[mask_p], blend_area.x1, h, draw_area_w);
367 if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_TRANSP) {
368 lv_memzero(&mask_buf[mask_p], draw_area_w);
369 }
370
371 mask_p += draw_area_w;
372 if((uint32_t) mask_p + draw_area_w < mask_buf_size) {
373 blend_area.y2 ++;
374 }
375 else {
376 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
377 lv_draw_sw_blend(draw_unit, &blend_dsc);
378
379 blend_area.y1 = blend_area.y2 + 1;
380 blend_area.y2 = blend_area.y1;
381 mask_p = 0;
382 lv_memset(mask_buf, 0xff, mask_buf_size);
383 }
384 }
385
386 /*Flush the last part*/
387 if(blend_area.y1 != blend_area.y2) {
388 blend_area.y2--;
389 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
390 lv_draw_sw_blend(draw_unit, &blend_dsc);
391 }
392
393 lv_free(mask_buf);
394
395 lv_draw_sw_mask_free_param(&mask_left_param);
396 lv_draw_sw_mask_free_param(&mask_right_param);
397 if(!dsc->raw_end) {
398 lv_draw_sw_mask_free_param(&mask_top_param);
399 lv_draw_sw_mask_free_param(&mask_bottom_param);
400 }
401 #else
402 LV_UNUSED(draw_unit);
403 LV_UNUSED(dsc);
404 LV_LOG_WARN("Can't draw skewed line with LV_DRAW_SW_COMPLEX == 0");
405 #endif /*LV_DRAW_SW_COMPLEX*/
406 }
407
408 #endif /*LV_USE_DRAW_SW*/
409