diff --git a/lv_draw/lv_draw.c b/lv_draw/lv_draw.c index aadba0303..b26ed370b 100644 --- a/lv_draw/lv_draw.c +++ b/lv_draw/lv_draw.c @@ -21,10 +21,6 @@ /********************* * DEFINES *********************/ - -#define LINE_WIDTH_CORR_BASE 64 -#define LINE_WIDTH_CORR_SHIFT 6 - #define LABEL_RECOLOR_PAR_LENGTH 6 /********************** @@ -37,43 +33,12 @@ typedef enum CMD_STATE_IN, }cmd_state_t; -typedef struct -{ - lv_point_t p1; - lv_point_t p2; - lv_point_t p_act; - lv_coord_t dx; - lv_coord_t sx; - lv_coord_t dy; - lv_coord_t sy; - lv_coord_t err; - lv_coord_t e2; - bool hor; /*Rather horizontal or vertical*/ -}line_draw_t; - -typedef struct { - lv_coord_t width; - lv_coord_t width_1; - lv_coord_t width_half; -}line_width_t; /********************** * STATIC PROTOTYPES **********************/ -static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); -static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); -static void line_draw_skew(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); -static void line_draw_perpendicular_ending(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, - lv_coord_t width, lv_coord_t width_half, lv_coord_t width_1); -static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2); -static bool line_next(line_draw_t * line); - -#if LV_ANTIALIAS != 0 -lv_opa_t antialias_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t line_opa); - -#endif static uint8_t hex_char_to_num(char hex); @@ -480,466 +445,11 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, #endif -/** - * Draw a line - * @param p1 first point of the line - * @param p2 second point of the line - * @param maskthe line will be drawn only on this area - * @param lines_p pointer to a line style - */ -void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask, - const lv_style_t * style) -{ - - if(style->line.width == 0) return; - if(point1->x == point2->x && point1->y == point2->y) return; - - line_draw_t main_line; - lv_point_t p1; - lv_point_t p2; - - /*Be sure always x1 < x2*/ - if(point1->x < point2->x) { - p1.x = point1->x; - p1.y = point1->y; - p2.x = point2->x; - p2.y = point2->y; - } else { - p1.x = point2->x; - p1.y = point2->y; - p2.x = point1->x; - p2.y = point1->y; - } - line_init(&main_line, &p1, &p2); - - - /*Special case draw a horizontal line*/ - if(main_line.p1.y == main_line.p2.y ) { - line_draw_hor(&main_line, mask, style); - } - /*Special case draw a vertical line*/ - else if(main_line.p1.x == main_line.p2.x ) { - line_draw_ver(&main_line, mask, style); - } - /*Arbitrary skew line*/ - else { - line_draw_skew(&main_line, mask, style); - } - -} - /********************** * STATIC FUNCTIONS **********************/ -static void line_draw_hor(line_draw_t * line, const lv_area_t * mask, const lv_style_t * style) -{ - lv_coord_t width = style->line.width - 1; - lv_coord_t width_half = width >> 1; - lv_coord_t width_1 = width & 0x1; - - lv_area_t act_area; - act_area.x1 = line->p1.x; - act_area.x2 = line->p2.x; - act_area.y1 = line->p1.y - width_half - width_1; - act_area.y2 = line->p2.y + width_half ; - - lv_area_t draw_area; - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - fill_fp(&draw_area, mask, style->line.color, style->line.opa); -} - -static void line_draw_ver(line_draw_t * line, const lv_area_t * mask, const lv_style_t * style) -{ - lv_coord_t width = style->line.width - 1; - lv_coord_t width_half = width >> 1; - lv_coord_t width_1 = width & 0x1; - lv_area_t act_area; - - act_area.x1 = line->p1.x - width_half; - act_area.x2 = line->p2.x + width_half + width_1; - act_area.y1 = line->p1.y; - act_area.y2 = line->p2.y; - - lv_area_t draw_area; - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - fill_fp(&draw_area, mask, style->line.color, style->line.opa); -} - -static void line_draw_skew(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style) -{ - - lv_coord_t width; - width = style->line.width - 1; - -#if LV_ANTIALIAS != 0 - bool aa_invert = false; - aa_invert = main_line->p1.y < main_line->p2.y ? false : true; /*Direction of opacity increase on the edges*/ - width--; /*Because of anti aliasing*/ -#endif - - static const uint8_t width_corr_array[] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, - 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, 72, 72, 73, 73, 74, - 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, - 86, 86, 87, 88, 88, 89, 90, 91, - }; - - lv_coord_t width_half; - lv_coord_t width_1; - uint16_t wcor; - if(main_line->hor == false) { - wcor = (main_line->dx * LINE_WIDTH_CORR_BASE) / main_line->dy; - } else { - wcor = (main_line->dy * LINE_WIDTH_CORR_BASE) / main_line->dx; - } - - /*Make the correction on line width*/ - if(width > 0) { - width = (width * width_corr_array[wcor]); - width = width >> LINE_WIDTH_CORR_SHIFT; - width_half = width >> 1; - width_1 = width & 0x1; - } else { - width_1 = 0; - width_half = 0; - } - - line_draw_perpendicular_ending(main_line, mask, style, width, width_half, width_1); - - - lv_coord_t perp_height = main_line->p_act.y - main_line->p1.y; /*Height of the perpendicular "triangle"*/ - - lv_coord_t last_x = main_line->p_act.x; - lv_coord_t last_y = main_line->p_act.y; - lv_area_t draw_area; - do { - if(main_line->hor == true && last_y != main_line->p_act.y) { - lv_area_t act_area; - act_area.x1 = last_x; - act_area.x2 = main_line->p_act.x - main_line->sx; - act_area.y1 = last_y - width_half; - act_area.y2 = main_line->p_act.y - main_line->sy + width_half + width_1; - last_y = main_line->p_act.y; - last_x = main_line->p_act.x; - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - if(width >= 0) { - fill_fp(&draw_area, mask, style->line.color, style->line.opa); - } - -#if LV_ANTIALIAS != 0 - lv_coord_t seg_w = lv_area_get_width(&draw_area); /*Segment width*/ - lv_point_t aa_p1; - lv_point_t aa_p2; - - aa_p1.x = draw_area.x1; - aa_p1.y = draw_area.y1 - 1; - - aa_p2.x = draw_area.x1; - aa_p2.y = draw_area.y1 + width + 1; - - lv_coord_t i; - for(i = 0; i < seg_w; i++) { - lv_opa_t aa_opa = antialias_get_opa(seg_w, i, style->line.opa); - - px_fp(aa_p1.x + i, aa_p1.y, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa); - px_fp(aa_p2.x + i, aa_p2.y, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa); - } -#endif - } - if (main_line->hor == false && last_x != main_line->p_act.x) { - lv_area_t act_area; - lv_area_t draw_area; - act_area.x1 = last_x - width_half; - act_area.x2 = main_line->p_act.x - main_line->sx + width_half + width_1; - act_area.y1 = last_y; - act_area.y2 = main_line->p_act.y - main_line->sy; - last_y = main_line->p_act.y; - last_x = main_line->p_act.x; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - - if(width >= 0) { - fill_fp(&draw_area, mask, style->line.color, style->line.opa); - } - -#if LV_ANTIALIAS - lv_coord_t seg_h = lv_area_get_height(&draw_area); /*Segment height*/ - lv_point_t aa_p1; - lv_point_t aa_p2; - - aa_p1.x = draw_area.x1 - 1; - aa_p1.y = draw_area.y1; - - aa_p2.x = draw_area.x1 + width + 1; - aa_p2.y = draw_area.y1; - - lv_coord_t i; - for(i = 0; i < seg_h; i++) { - lv_opa_t aa_opa = antialias_get_opa(seg_h, i, style->line.opa); - px_fp(aa_p1.x, aa_p1.y + i, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa); - px_fp(aa_p2.x, aa_p2.y + i, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa); - } -#endif - } - - /*Calc. the next point of the line*/ - if(!line_next(main_line)) break; /*In case error. It should stop because of the condition in "while(...)"*/ - - } while(main_line->p_act.y + perp_height <= main_line->p2.y); - - /*Draw the last part of the line*/ - if(main_line->hor == true) { - lv_area_t act_area; - lv_area_t draw_area; - act_area.x1 = last_x; - act_area.x2 = main_line->p_act.x; - act_area.y1 = last_y - width_half ; - act_area.y2 = main_line->p_act.y + width_half + width_1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - if(width >= 0) { - fill_fp(&draw_area, mask, style->line.color, style->line.opa); - } - -#if LV_ANTIALIAS != 0 - lv_coord_t seg_w = lv_area_get_width(&draw_area); /*Segment width*/ - lv_point_t aa_p1; - lv_point_t aa_p2; - - aa_p1.x = draw_area.x1; - aa_p1.y = draw_area.y1 - 1; - - aa_p2.x = draw_area.x1; - aa_p2.y = draw_area.y1 + width + 1; - - lv_coord_t i; - for(i = 0; i < seg_w; i++) { - lv_opa_t aa_opa = antialias_get_opa(seg_w, i, style->line.opa); - - px_fp(aa_p1.x + i, aa_p1.y, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa); - px_fp(aa_p2.x + i, aa_p2.y, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa); - } -#endif - - } - if (main_line->hor == false) { - lv_area_t act_area; - lv_area_t draw_area; - act_area.x1 = last_x - width_half; - act_area.x2 = main_line->p_act.x + width_half + width_1; - act_area.y1 = last_y; - act_area.y2 = main_line->p_act.y - 1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - if(width >= 0) { - fill_fp(&draw_area, mask, LV_COLOR_BLUE, style->line.opa); - } - -#if LV_ANTIALIAS != 0 - lv_coord_t seg_h = lv_area_get_height(&draw_area); /*Segment height*/ - lv_point_t aa_p1; - lv_point_t aa_p2; - - aa_p1.x = draw_area.x1 - 1; - aa_p1.y = draw_area.y1; - - aa_p2.x = draw_area.x1 + width + 1; - aa_p2.y = draw_area.y1; - - lv_coord_t i; - for(i = 0; i < seg_h; i++) { - lv_opa_t aa_opa = antialias_get_opa(seg_h, i, style->line.opa); - - px_fp(aa_p1.x, aa_p1.y + i, mask, LV_COLOR_GREEN, aa_invert ? aa_opa : style->line.opa - aa_opa); - px_fp(aa_p2.x, aa_p2.y + i, mask, LV_COLOR_GREEN, aa_invert ? style->line.opa - aa_opa : aa_opa); - } -#endif - } -} - -static void line_draw_perpendicular_ending(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, - lv_coord_t width, lv_coord_t width_half, lv_coord_t width_1) -{ - lv_coord_t main_vx = main_line->p2.x - main_line->p1.x; - lv_coord_t main_vy = main_line->p2.y - main_line->p1.y; - - lv_point_t pp_1 = {main_line->p1.x - main_line->sx + width_half + width_1 + 1, main_line->p1.y}; - lv_point_t pp_2 = {pp_1.x - main_vy, pp_1.y + main_vx}; /*Was - ... + */ - line_draw_t end_line; - line_init(&end_line, &pp_1, &pp_2); - -#if LV_ANTIALIAS - lv_coord_t aa_main_y_seg_cnt = 1; - px_fp(end_line.p_act.x, end_line.p_act.y - 1, mask, LV_COLOR_YELLOW, LV_OPA_40); /*One px above the perpendicular corner*/ -#endif - - lv_area_t draw_area; - volatile lv_area_t act_area; - do { - lv_coord_t last_y; - lv_coord_t last_x_main; - lv_coord_t last_x_end; - - last_x_main = main_line->p_act.x; - last_y = main_line->p_act.y; - while(main_line->p_act.y == last_y){ - line_next(main_line); -#if LV_ANTIALIAS - if(last_x_main != main_line->p_act.x) { - printf("seg_w: %d\n", aa_main_y_seg_cnt); - lv_coord_t aa_px_id; - for(aa_px_id = 0; aa_px_id < aa_main_y_seg_cnt; aa_px_id++) { - lv_opa_t px_opa = LV_OPA_100 - antialias_get_opa(aa_main_y_seg_cnt, aa_px_id, LV_OPA_100); - px_fp(last_x_main - main_line->sx + width_half + width_1 + 2, main_line->p_act.y - aa_px_id - 1, mask, LV_COLOR_GREEN, px_opa); - } - aa_main_y_seg_cnt = 0; - } -#endif - } - aa_main_y_seg_cnt++; - - last_x_end = end_line.p_act.x; - last_y = end_line.p_act.y; - while(end_line.p_act.y == last_y){ - if(!line_next(&end_line)) break; - } - - /*Rather vertical, direction: "\" */ - if(!main_line->hor && main_line->p1.y < main_line->p2.y) - { - act_area.x1 = last_x_end; - act_area.y1 = main_line->p_act.y - 1; - act_area.x2 = last_x_main - main_line->sx + width_half + width_1 + 1; - act_area.y2 = act_area.y1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - printf("TOP x1: %d, y1: %d, x2: %d, y2: %d\n", draw_area.x1, draw_area.y1, draw_area.x2, draw_area.y2); - fill_fp(&draw_area, mask, LV_COLOR_RED, LV_OPA_40); - - -#if LV_ANTIALIAS - lv_coord_t aa_seg_w = last_x_end - end_line.p_act.x; - lv_coord_t act_w = main_line->p_act.x - main_line->sx + width_half + width_1 + 1 - end_line.p_act.x; - if(act_w >= width) { /*In the last row limit the segment length to the main line*/ - aa_seg_w = 1; - } - - lv_coord_t aa_px_id; - for(aa_px_id = 0; aa_px_id < aa_seg_w; aa_px_id++) { - lv_opa_t px_opa = LV_OPA_100 - antialias_get_opa(aa_seg_w, aa_px_id, LV_OPA_100); - px_fp(draw_area.x1 - aa_px_id - 1, draw_area.y1, mask, LV_COLOR_BLUE, px_opa); - } -#endif - - - act_area.x1 = main_line->p2.x - width_half - (act_area.x2 - end_line.p1.x); - act_area.y1 = main_line->p2.y - (draw_area.y1 - main_line->p1.y); - act_area.x2 = main_line->p2.x - width_half - (last_x_end - end_line.p1.x); - act_area.y2 = act_area.y1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - - - printf("BOT x1: %d, y1: %d, x2: %d, y2: %d\n", draw_area.x1, draw_area.y1, draw_area.x2, draw_area.y2); - fill_fp(&draw_area, mask, LV_COLOR_RED, LV_OPA_100); - - - } - /*Rather vertical, direction: "/" */ - if(!main_line->hor && main_line->p2.y < main_line->p1.y) - { - act_area.x1 = last_x_end; - act_area.y1 = main_line->p_act.y + 1; - act_area.x2 = last_x_main - main_line->sx + width_half + width_1 + 1; - act_area.y2 = act_area.y1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - printf("TOP x1: %d, y1: %d, x2: %d, y2: %d\n", draw_area.x1, draw_area.y1, draw_area.x2, draw_area.y2); - fill_fp(&draw_area, mask, LV_COLOR_RED, LV_OPA_40); - - act_area.x1 = main_line->p2.x - width_half - (last_x_end - end_line.p1.x); - act_area.y1 = main_line->p2.y - (draw_area.y1 - main_line->p1.y); - act_area.x2 = main_line->p2.x - width_half - (act_area.x2 - end_line.p1.x); - act_area.y2 = act_area.y1; - - draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); - draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); - draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); - draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); - - printf("BOT x1: %d, y1: %d, x2: %d, y2: %d\n", draw_area.x1, draw_area.y1, draw_area.x2, draw_area.y2); - fill_fp(&draw_area, mask, LV_COLOR_RED, LV_OPA_40); - - } - } while(lv_area_get_width(&draw_area) < width); - - - printf("\n"); -} - - -static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2) -{ - line->p1.x = p1->x; - line->p1.y = p1->y; - line->p2.x = p2->x; - line->p2.y = p2->y; - - line->dx = LV_MATH_ABS(line->p2.x - line->p1.x); - line->sx = line->p1.x < line->p2.x ? 1 : -1; - line->dy = LV_MATH_ABS(line->p2.y - line->p1.y); - line->sy = line->p1.y < line->p2.y ? 1 : -1; - line->err = (line->dx > line->dy ? line->dx : -line->dy) / 2; - line->e2 = 0; - line->hor = line->dx > line->dy ? true : false; /*Rather horizontal or vertical*/ - - line->p_act.x = line->p1.x; - line->p_act.y = line->p1.y; -} - -static bool line_next(line_draw_t * line) -{ - if (line->p_act.x == line->p2.x && line->p_act.y == line->p2.y) return false; - line->e2 = line->err; - if (line->e2 >-line->dx) { - line->err -= line->dy; - line->p_act.x += line->sx; - } - if (line->e2 < line->dy) { - line->err += line->dx; - line->p_act.y += line->sy; - } - return true; -} #if LV_ANTIALIAS != 0 diff --git a/lv_draw/lv_draw.h b/lv_draw/lv_draw.h index c9c51d94e..2c1b0959e 100644 --- a/lv_draw/lv_draw.h +++ b/lv_draw/lv_draw.h @@ -128,6 +128,9 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, void lv_draw_line(const lv_point_t * p1, const lv_point_t * p2, const lv_area_t * mask_p, const lv_style_t * style_p); +#if LV_ANTIALIAS != 0 +lv_opa_t antialias_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t line_opa); +#endif /********************** * GLOBAL VARIABLES diff --git a/lv_draw/lv_draw_line.c b/lv_draw/lv_draw_line.c index e69de29bb..2b598e5c0 100644 --- a/lv_draw/lv_draw_line.c +++ b/lv_draw/lv_draw_line.c @@ -0,0 +1,477 @@ +/** + * @file lv_draw_line.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include +#include "lv_draw.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LINE_WIDTH_CORR_BASE 64 +#define LINE_WIDTH_CORR_SHIFT 6 + +/********************** + * TYPEDEFS + **********************/ + +typedef struct +{ + lv_point_t p1; + lv_point_t p2; + lv_point_t p_act; + lv_coord_t dx; + lv_coord_t sx; /*-1: x1 < x2; 1: x2 >= x1*/ + lv_coord_t dy; + lv_coord_t sy; /*-1: y1 < y2; 1: y2 >= y1*/ + lv_coord_t err; + lv_coord_t e2; + bool hor; /*Rather horizontal or vertical*/ +}line_draw_t; + +typedef struct { + lv_coord_t width; + lv_coord_t width_1; + lv_coord_t width_half; +}line_width_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); +static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); +static void line_draw_skew(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style); +static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2); +static bool line_next(line_draw_t * line); +static bool line_next_y(line_draw_t * line); +static void line_ver_aa(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa); +static void line_hor_aa(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Draw a line + * @param p1 first point of the line + * @param p2 second point of the line + * @param maskthe line will be drawn only on this area + * @param lines_p pointer to a line style + */ +void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask, + const lv_style_t * style) +{ + + if(style->line.width == 0) return; + if(point1->x == point2->x && point1->y == point2->y) return; + + line_draw_t main_line; + lv_point_t p1; + lv_point_t p2; + + /*Be sure always x1 < x2*/ + if(point1->x < point2->x) { + p1.x = point1->x; + p1.y = point1->y; + p2.x = point2->x; + p2.y = point2->y; + } else { + p1.x = point2->x; + p1.y = point2->y; + p2.x = point1->x; + p2.y = point1->y; + } + line_init(&main_line, &p1, &p2); + + + /*Special case draw a horizontal line*/ + if(main_line.p1.y == main_line.p2.y ) { + line_draw_hor(&main_line, mask, style); + } + /*Special case draw a vertical line*/ + else if(main_line.p1.x == main_line.p2.x ) { + line_draw_ver(&main_line, mask, style); + } + /*Arbitrary skew line*/ + else { + line_draw_skew(&main_line, mask, style); + } +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void line_draw_hor(line_draw_t * line, const lv_area_t * mask, const lv_style_t * style) +{ + lv_coord_t width = style->line.width - 1; + lv_coord_t width_half = width >> 1; + lv_coord_t width_1 = width & 0x1; + + lv_area_t act_area; + act_area.x1 = line->p1.x; + act_area.x2 = line->p2.x; + act_area.y1 = line->p1.y - width_half - width_1; + act_area.y2 = line->p2.y + width_half ; + + lv_area_t draw_area; + draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); + draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); + draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); + draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); + fill_fp(&draw_area, mask, style->line.color, style->line.opa); +} + +static void line_draw_ver(line_draw_t * line, const lv_area_t * mask, const lv_style_t * style) +{ + lv_coord_t width = style->line.width - 1; + lv_coord_t width_half = width >> 1; + lv_coord_t width_1 = width & 0x1; + lv_area_t act_area; + + act_area.x1 = line->p1.x - width_half; + act_area.x2 = line->p2.x + width_half + width_1; + act_area.y1 = line->p1.y; + act_area.y2 = line->p2.y; + + lv_area_t draw_area; + draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2); + draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2); + draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2); + draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2); + fill_fp(&draw_area, mask, style->line.color, style->line.opa); +} + +static void line_draw_skew(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style) +{ + + lv_coord_t width; + width = style->line.width - 1; + + +#if LV_ANTIALIAS != 0 + width--; /*Because of anti aliasing*/ +#endif + + static const uint8_t width_corr_array[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, + 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, 72, 72, 73, 73, 74, + 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, + 86, 86, 87, 88, 88, 89, 90, 91, + }; + + lv_coord_t width_half; + lv_coord_t width_1; + uint16_t wcor; + if(main_line->hor == false) { + wcor = (main_line->dx * LINE_WIDTH_CORR_BASE) / main_line->dy; + } else { + wcor = (main_line->dy * LINE_WIDTH_CORR_BASE) / main_line->dx; + } + + /*Make the correction on line width*/ + if(width > 0) { + width = (width * width_corr_array[wcor]); + width = width >> LINE_WIDTH_CORR_SHIFT; + width_half = width >> 1; + width_1 = width & 0x1; + } else { + width_1 = 0; + width_half = 0; + } + + lv_point_t vect_main, vect_norm; + vect_main.x = main_line->p2.x - main_line->p1.x; + vect_main.y = main_line->p2.y - main_line->p1.y; + if(main_line->p1.y < main_line->p2.y) { + vect_norm.x = - vect_main.y; + vect_norm.y = vect_main.x; + } else { + vect_norm.x = vect_main.y; + vect_norm.y = -vect_main.x; + + } + + line_draw_t line_neg; + line_draw_t line_pos; + line_draw_t line_end; + lv_point_t p1_neg, p2_neg, p1_pos, p2_pos, p1_end, p2_end; + p1_neg.x = main_line->p1.x - width_half; + p1_neg.y = main_line->p1.y; + p2_neg.x = main_line->p2.x - width_half; + p2_neg.y = main_line->p2.y; + p1_pos.x = main_line->p1.x + width_half + width_1; + p1_pos.y = main_line->p1.y; + p2_pos.x = main_line->p2.x + width_half + width_1; + p2_pos.y = main_line->p2.y; + p1_end.x = p1_pos.x; + p1_end.y = p1_pos.y; + p2_end.x = p1_end.x + vect_norm.x; + p2_end.y = p1_end.y + vect_norm.y; + + line_init(&line_pos, &p1_pos, &p2_pos); + line_init(&line_end, &p1_end, &p2_end); + + lv_area_t draw_area; + + /*Top perpendicular end*/ +#if LV_ANTIALIAS + volatile lv_point_t aa_last_step_pos; /*The point where the last step in x occurred*/ + volatile lv_point_t aa_last_step_end; + aa_last_step_pos.x = line_pos.p_act.x; + aa_last_step_pos.y = line_pos.p_act.y; + aa_last_step_end.x = line_end.p_act.x; + aa_last_step_end.y = line_end.p_act.y; +// +// /*On pixel at the top and bottom*/ +// px_fp(aa_last_step_pos.x, aa_last_step_pos.y - 1, mask, LV_COLOR_AQUA, LV_OPA_80 / 2); +#endif + do { + draw_area.x1 = LV_MATH_MIN(line_end.p_act.x, line_pos.p_act.x); + draw_area.y1 = LV_MATH_MIN(line_end.p_act.y, line_pos.p_act.y); + draw_area.x2 = LV_MATH_MAX(line_end.p_act.x, line_pos.p_act.x); + draw_area.y2 = LV_MATH_MAX(line_pos.p_act.y, line_pos.p_act.y); + +#if LV_ANTIALIAS + if(aa_last_step_pos.x != line_pos.p_act.x) { + line_ver_aa(aa_last_step_pos.x + 1, aa_last_step_pos.y, line_pos.p_act.y - aa_last_step_pos.y, mask, LV_COLOR_BLUE, LV_OPA_50); + aa_last_step_pos.x = line_pos.p_act.x; + aa_last_step_pos.y = line_pos.p_act.y; + } + + if(aa_last_step_end.y != line_end.p_act.y) { + lv_coord_t seg_w = aa_last_step_end.x - line_end.p_act.x; /*Segment width*/ + if(line_pos.p_act.x - (aa_last_step_end.x - seg_w) >= width) { + seg_w = width - (line_pos.p_act.x - aa_last_step_end.x); + } + line_hor_aa(aa_last_step_end.x - seg_w, aa_last_step_end.y, seg_w , mask, LV_COLOR_RED, LV_OPA_50); + aa_last_step_end.x = line_end.p_act.x; + aa_last_step_end.y = line_end.p_act.y; + } +#endif + /*Be sure the area is not wide ("x" can be more then where the intersection should be)*/ + if(lv_area_get_width(&draw_area) > width) { + draw_area.x1 = draw_area.x2 - width; + } + fill_fp(&draw_area, mask, LV_COLOR_MAGENTA, LV_OPA_50); + + line_next_y(&line_end); + line_next_y(&line_pos); + + }while(lv_area_get_width(&draw_area) < width); + +#if LV_ANTIALIAS + /*Anti-aliasing of the last perpendicular segment (only one pixel)*/ + px_fp(draw_area.x1 - 1, draw_area.y1, mask, LV_COLOR_AQUA, LV_OPA_80 / 2); +#endif + + /*Middle part*/ + if(line_end.p_act.x == line_end.p2.x) line_end.p_act.y ++; /*If the the perpendicular line is finished (very steep line) then the last y step is missing */ + lv_coord_t perp_height = line_end.p_act.y - line_pos.p1.y; /*Height of the perpendicular area*/ + p1_neg.x = draw_area.x1 = draw_area.x2 - width; + p1_neg.y = line_end.p_act.y; + line_init(&line_neg, &p1_neg, &p2_neg); + +#if LV_ANTIALIAS + lv_point_t aa_last_step_neg; + aa_last_step_neg.x = line_neg.p_act.x; + aa_last_step_neg.y = line_neg.p_act.y; +#endif + + do { + draw_area.x1 = LV_MATH_MIN(line_neg.p_act.x, line_pos.p_act.x); + draw_area.y1 = LV_MATH_MIN(line_neg.p_act.y, line_pos.p_act.y); + draw_area.x2 = LV_MATH_MAX(line_neg.p_act.x, line_pos.p_act.x); + draw_area.y2 = LV_MATH_MAX(line_neg.p_act.y, line_pos.p_act.y); + fill_fp(&draw_area, mask, LV_COLOR_GREEN, LV_OPA_50); + +#if LV_ANTIALIAS + if(aa_last_step_neg.x != line_neg.p_act.x) { + line_ver_aa(aa_last_step_neg.x - 1, aa_last_step_neg.y, aa_last_step_neg.y - line_neg.p_act.y, mask, LV_COLOR_BLUE, LV_OPA_50); + aa_last_step_neg.x = line_neg.p_act.x; + aa_last_step_neg.y = line_neg.p_act.y; + } + + if(aa_last_step_pos.x != line_pos.p_act.x) { + line_ver_aa(aa_last_step_pos.x + 1, aa_last_step_pos.y, line_pos.p_act.y - aa_last_step_pos.y, mask, LV_COLOR_ORANGE, LV_OPA_50); + aa_last_step_pos.x = line_pos.p_act.x; + aa_last_step_pos.y = line_pos.p_act.y; + } +#endif + if(!line_next_y(&line_neg)) break; + if(!line_next_y(&line_pos)) break; + }while(LV_MATH_ABS(line_pos.p_act.y - line_pos.p2.y) >= LV_MATH_ABS(perp_height)); + +#if LV_ANTIALIAS + /*Anti-aliasing of the last segment*/ + line_ver_aa(aa_last_step_pos.x + 1, aa_last_step_pos.y, line_pos.p2.y - aa_last_step_pos.y - perp_height + 1, mask, LV_COLOR_RED, LV_OPA_50); + +#endif + /*Bottom perpendicular end*/ + p1_end.x = line_pos.p_act.x; + p1_end.y = line_pos.p_act.y; + line_init(&line_end, &p1_end, &line_neg.p2); +#if LV_ANTIALIAS + aa_last_step_end.x = line_end.p_act.x; + aa_last_step_end.y = line_end.p_act.y; +#endif + + do { + draw_area.x1 = LV_MATH_MIN(line_neg.p_act.x, line_end.p_act.x); + draw_area.y1 = LV_MATH_MIN(line_neg.p_act.y, line_end.p_act.y); + draw_area.x2 = LV_MATH_MAX(line_neg.p_act.x, line_end.p_act.x); + draw_area.y2 = LV_MATH_MAX(line_neg.p_act.y, line_end.p_act.y); + fill_fp(&draw_area, mask, LV_COLOR_MAGENTA, LV_OPA_50); + +#if LV_ANTIALIAS + if(aa_last_step_neg.x != line_neg.p_act.x) { + line_ver_aa(aa_last_step_neg.x - 1, aa_last_step_neg.y, aa_last_step_neg.y - line_neg.p_act.y, mask, LV_COLOR_BLUE, LV_OPA_50); + aa_last_step_neg.x = line_neg.p_act.x; + aa_last_step_neg.y = line_neg.p_act.y; + } + + if(aa_last_step_end.y != line_end.p_act.y) { + lv_coord_t seg_w = aa_last_step_end.x - line_end.p_act.x; /*Segment width*/ + if(line_neg.p_act.x - (aa_last_step_end.x - seg_w) >= width) { + seg_w = width - (line_neg.p_act.x - aa_last_step_end.x); + } + line_hor_aa(aa_last_step_end.x - seg_w + 1, aa_last_step_end.y + 1, -seg_w , mask, LV_COLOR_RED, LV_OPA_50); + aa_last_step_end.x = line_end.p_act.x; + aa_last_step_end.y = line_end.p_act.y; + } + +#endif + + if(!line_next_y(&line_neg)) break; + if(!line_next_y(&line_end)) break; + + }while(1); + + +#if LV_ANTIALIAS + line_ver_aa(aa_last_step_neg.x - 1, aa_last_step_neg.y, aa_last_step_neg.y - line_neg.p2.y - 1, mask, LV_COLOR_BLUE, LV_OPA_50); + lv_coord_t seg_w = aa_last_step_end.x - line_neg.p2.x; /*Segment width*/ + if(line_neg.p2.x - (aa_last_step_end.x - seg_w) >= width) { + seg_w = width - (line_neg.p2.x - aa_last_step_end.x); + } + line_hor_aa(line_neg.p2.x, line_neg.p2.y + 1, -seg_w , mask, LV_COLOR_LIME, LV_OPA_50); +#endif +} + + +static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2) +{ + line->p1.x = p1->x; + line->p1.y = p1->y; + line->p2.x = p2->x; + line->p2.y = p2->y; + + line->dx = LV_MATH_ABS(line->p2.x - line->p1.x); + line->sx = line->p1.x < line->p2.x ? 1 : -1; + line->dy = LV_MATH_ABS(line->p2.y - line->p1.y); + line->sy = line->p1.y < line->p2.y ? 1 : -1; + line->err = (line->dx > line->dy ? line->dx : -line->dy) / 2; + line->e2 = 0; + line->hor = line->dx > line->dy ? true : false; /*Rather horizontal or vertical*/ + + line->p_act.x = line->p1.x; + line->p_act.y = line->p1.y; +} + +static bool line_next(line_draw_t * line) +{ + if (line->p_act.x == line->p2.x && line->p_act.y == line->p2.y) return false; + line->e2 = line->err; + if (line->e2 >-line->dx) { + line->err -= line->dy; + line->p_act.x += line->sx; + } + if (line->e2 < line->dy) { + line->err += line->dx; + line->p_act.y += line->sy; + } + return true; +} + +/** + * Iterate until step one in y direction. + * @param line + * @return + */ +static bool line_next_y(line_draw_t * line) +{ + lv_coord_t last_y = line->p_act.y; + + do { + if(!line_next(line)) return false; + } while(last_y == line->p_act.y); + + return true; + +} + +/** + * Add a vertical anti-aliasing segment (pixels with decreasing opacity) + * @param x start point x coordinate + * @param y start point y coordinate + * @param length length of segment (negative value to start from 0 opacity) + * @param mask draw only in this area + * @param color color of pixels + * @param opa maximum opacity + */ +static void line_ver_aa(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) +{ + bool aa_inv = false; + if(length < 0) { + aa_inv = true; + length = -length; + } + + lv_coord_t i; + for(i = 0; i < length; i++) { + lv_opa_t px_opa = antialias_get_opa(length, i, opa); + if(aa_inv) px_opa = opa - px_opa; + px_fp(x, y + i, mask, color, px_opa); + } +} + +/** + * Add a horizontal anti-aliasing segment (pixels with decreasing opacity) + * @param x start point x coordinate + * @param y start point y coordinate + * @param length length of segment (negative value to start from 0 opacity) + * @param mask draw only in this area + * @param color color of pixels + * @param opa maximum opacity + */ +static void line_hor_aa(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) +{ + bool aa_inv = false; + if(length < 0) { + aa_inv = true; + length = -length; + } + + lv_coord_t i; + for(i = 0; i < length; i++) { + lv_opa_t px_opa = antialias_get_opa(length, i, opa); + if(aa_inv) px_opa = opa - px_opa; + px_fp(x + i, y, mask, color, px_opa); + } +} diff --git a/lv_draw/lv_draw_line.h b/lv_draw/lv_draw_line.h index e69de29bb..604d1140f 100644 --- a/lv_draw/lv_draw_line.h +++ b/lv_draw/lv_draw_line.h @@ -0,0 +1,38 @@ +/** + * @file lv_draw_line.h + * + */ + +#ifndef LV_DRAW_LINE_H +#define LV_DRAW_LINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_DRAW_LINE_H*/