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