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