mirror of
https://github.com/CTCaer/hekate.git
synced 2024-11-01 08:21:45 +00:00
608 lines
22 KiB
C
608 lines
22 KiB
C
|
/**
|
||
|
* @file lv_draw_line.c
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*********************
|
||
|
* INCLUDES
|
||
|
*********************/
|
||
|
#include <stdio.h>
|
||
|
#include "lv_draw.h"
|
||
|
#include "../lv_misc/lv_math.h"
|
||
|
|
||
|
/*********************
|
||
|
* DEFINES
|
||
|
*********************/
|
||
|
#if LV_COMPILER_VLA_SUPPORTED == 0
|
||
|
#define LINE_MAX_WIDTH 64
|
||
|
#endif
|
||
|
|
||
|
/**********************
|
||
|
* 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, lv_opa_t opa_scale);
|
||
|
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);
|
||
|
static void 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);
|
||
|
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 bool line_next_x(line_draw_t * line);
|
||
|
|
||
|
/**********************
|
||
|
* STATIC VARIABLES
|
||
|
**********************/
|
||
|
|
||
|
/**********************
|
||
|
* MACROS
|
||
|
**********************/
|
||
|
|
||
|
/**********************
|
||
|
* GLOBAL FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
/**
|
||
|
* Draw a line
|
||
|
* @param point1 first point of the line
|
||
|
* @param point2 second point of the line
|
||
|
* @param mask the line will be drawn only on this area
|
||
|
* @param style pointer to a line's style
|
||
|
* @param opa_scale scale down all opacities by the factor
|
||
|
*/
|
||
|
void 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)
|
||
|
{
|
||
|
|
||
|
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;
|
||
|
|
||
|
/*If the line if rather vertical then be sure y1 < y2 else x1 < x2*/
|
||
|
|
||
|
if(LV_MATH_ABS(point1->x - point2->x) > LV_MATH_ABS(point1->y - point2->y)) {
|
||
|
|
||
|
/*Steps less in y then x -> rather horizontal*/
|
||
|
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;
|
||
|
}
|
||
|
} else {
|
||
|
/*Steps less in x then y -> rather vertical*/
|
||
|
if(point1->y < point2->y) {
|
||
|
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, opa_scale);
|
||
|
}
|
||
|
/*Special case draw a vertical line*/
|
||
|
else if(main_line.p1.x == main_line.p2.x) {
|
||
|
line_draw_ver(&main_line, mask, style, opa_scale);
|
||
|
}
|
||
|
/*Arbitrary skew line*/
|
||
|
else {
|
||
|
bool dir_ori = false;
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_point_t p_tmp;
|
||
|
|
||
|
if(main_line.hor) {
|
||
|
if(main_line.p1.y < main_line.p2.y) {
|
||
|
dir_ori = true;
|
||
|
p_tmp.x = main_line.p2.x;
|
||
|
p_tmp.y = main_line.p2.y - 1;
|
||
|
line_init(&main_line, &p1, &p_tmp);
|
||
|
main_line.sy = LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
|
||
|
}
|
||
|
else if(main_line.p1.y > main_line.p2.y) {
|
||
|
dir_ori = false;
|
||
|
p_tmp.x = main_line.p2.x;
|
||
|
p_tmp.y = main_line.p2.y + 1;
|
||
|
line_init(&main_line, &p1, &p_tmp);
|
||
|
main_line.sy = -LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if(main_line.p1.x < main_line.p2.x) {
|
||
|
dir_ori = true;
|
||
|
p_tmp.x = main_line.p2.x - 1;
|
||
|
p_tmp.y = main_line.p2.y;
|
||
|
line_init(&main_line, &p1, &p_tmp);
|
||
|
main_line.sx = LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
|
||
|
}
|
||
|
else if(main_line.p1.x > main_line.p2.x) {
|
||
|
dir_ori = false;
|
||
|
p_tmp.x = main_line.p2.x + 1;
|
||
|
p_tmp.y = main_line.p2.y;
|
||
|
line_init(&main_line, &p1, &p_tmp);
|
||
|
main_line.sx = -LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
line_draw_skew(&main_line, dir_ori, mask, style, opa_scale);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**********************
|
||
|
* STATIC FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
lv_coord_t width = style->line.width - 1;
|
||
|
lv_coord_t width_half = width >> 1;
|
||
|
lv_coord_t width_1 = width & 0x1;
|
||
|
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
|
||
|
|
||
|
lv_area_t act_area;
|
||
|
act_area.x1 = main_line->p1.x;
|
||
|
act_area.x2 = main_line->p2.x;
|
||
|
act_area.y1 = main_line->p1.y - width_half - width_1;
|
||
|
act_area.y2 = main_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, opa);
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
lv_coord_t width = style->line.width - 1;
|
||
|
lv_coord_t width_half = width >> 1;
|
||
|
lv_coord_t width_1 = width & 0x1;
|
||
|
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
|
||
|
|
||
|
|
||
|
lv_area_t act_area;
|
||
|
act_area.x1 = main_line->p1.x - width_half;
|
||
|
act_area.x2 = main_line->p2.x + width_half + width_1;
|
||
|
act_area.y1 = main_line->p1.y;
|
||
|
act_area.y2 = main_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, opa);
|
||
|
}
|
||
|
|
||
|
static void 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)
|
||
|
{
|
||
|
|
||
|
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
|
||
|
|
||
|
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->hor) {
|
||
|
if(main_line->p1.y < main_line->p2.y + dir_ori) {
|
||
|
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;
|
||
|
}
|
||
|
} else {
|
||
|
if(main_line->p1.x < main_line->p2.x + dir_ori) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* In case of a short but tick line the perpendicular ending is longer then the real line.
|
||
|
* it would break the calculations so make the normal vector larger*/
|
||
|
vect_norm.x = vect_norm.x << 4;
|
||
|
vect_norm.y = vect_norm.y << 4;
|
||
|
|
||
|
lv_coord_t width;
|
||
|
width = style->line.width;
|
||
|
|
||
|
/* The pattern stores the points of the line ending. It has the good direction and length.
|
||
|
* The worth case is the 45° line where pattern can have 1.41 x `width` points*/
|
||
|
#if LV_COMPILER_VLA_SUPPORTED
|
||
|
lv_point_t pattern[width * 2];
|
||
|
#else
|
||
|
lv_point_t pattern[LINE_MAX_WIDTH];
|
||
|
#endif
|
||
|
lv_coord_t i = 0;
|
||
|
|
||
|
/*Create a perpendicular pattern (a small line)*/
|
||
|
if(width != 0) {
|
||
|
line_draw_t pattern_line;
|
||
|
lv_point_t p0 = {0, 0};
|
||
|
line_init(&pattern_line, &p0, &vect_norm);
|
||
|
|
||
|
uint32_t width_sqr = width * width;
|
||
|
/* Run for a lot of times. Meanwhile the real width will be determined as well */
|
||
|
for(i = 0; i < (lv_coord_t)sizeof(pattern); i ++) {
|
||
|
pattern[i].x = pattern_line.p_act.x;
|
||
|
pattern[i].y = pattern_line.p_act.y;
|
||
|
|
||
|
/*Finish the pattern line if it's length equal to the desired width (Use Pythagoras theorem)*/
|
||
|
uint32_t sqr = pattern_line.p_act.x * pattern_line.p_act.x + pattern_line.p_act.y * pattern_line.p_act.y;
|
||
|
if(sqr >= width_sqr) {
|
||
|
width = i;
|
||
|
#if LV_ANTIALIAS
|
||
|
width--;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
line_next(&pattern_line);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_coord_t width_safe = width;
|
||
|
if(width == 0) width_safe = 1;
|
||
|
|
||
|
lv_coord_t aa_last_corner;
|
||
|
aa_last_corner = 0;
|
||
|
#endif
|
||
|
|
||
|
lv_coord_t x_center_ofs = 0;
|
||
|
lv_coord_t y_center_ofs = 0;
|
||
|
|
||
|
if(width != 0) {
|
||
|
x_center_ofs = pattern[width - 1].x / 2;
|
||
|
y_center_ofs = pattern[width - 1].y / 2;
|
||
|
}
|
||
|
else {
|
||
|
if(main_line->hor && main_line->p1.y >= main_line->p2.y + dir_ori) pattern[0].y --;
|
||
|
if(!main_line->hor && main_line->p1.x >= main_line->p2.x + dir_ori) pattern[0].x --;
|
||
|
}
|
||
|
|
||
|
/* Make the coordinates relative to the center */
|
||
|
for(i = 0; i < width; i++) {
|
||
|
pattern[i].x -= x_center_ofs;
|
||
|
pattern[i].y -= y_center_ofs;
|
||
|
#if LV_ANTIALIAS
|
||
|
if(i != 0) {
|
||
|
if(main_line->hor) {
|
||
|
if(pattern[i - 1].x != pattern[i].x) {
|
||
|
lv_coord_t seg_w = pattern[i].y - pattern[aa_last_corner].y;
|
||
|
if(main_line->sy < 0) {
|
||
|
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w + 1,
|
||
|
seg_w, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w + 1,
|
||
|
-seg_w, mask, style->line.color, opa);
|
||
|
} else {
|
||
|
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
|
||
|
seg_w, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
|
||
|
-seg_w, mask, style->line.color, opa);
|
||
|
}
|
||
|
aa_last_corner = i;
|
||
|
}
|
||
|
} else {
|
||
|
if(pattern[i - 1].y != pattern[i].y) {
|
||
|
lv_coord_t seg_w = pattern[i].x - pattern[aa_last_corner].x;
|
||
|
if(main_line->sx < 0) {
|
||
|
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p1.y + pattern[aa_last_corner].y - 1,
|
||
|
seg_w, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p2.y + pattern[aa_last_corner].y + 1,
|
||
|
-seg_w, mask, style->line.color, opa);
|
||
|
} else {
|
||
|
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
|
||
|
seg_w, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
|
||
|
-seg_w, mask, style->line.color, opa);
|
||
|
}
|
||
|
aa_last_corner = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
/*Add the last part of anti-aliasing for the perpendicular ending*/
|
||
|
if(width != 0) { /*Due to rounding error with very thin lines it looks ugly*/
|
||
|
if(main_line->hor) {
|
||
|
lv_coord_t seg_w = pattern[width_safe - 1].y - pattern[aa_last_corner].y;
|
||
|
if(main_line->sy < 0) {
|
||
|
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w,
|
||
|
seg_w + main_line->sy, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w,
|
||
|
-(seg_w + main_line->sy), mask, style->line.color, opa);
|
||
|
|
||
|
} else {
|
||
|
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
|
||
|
seg_w + main_line->sy, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
|
||
|
-(seg_w + main_line->sy), mask, style->line.color, opa);
|
||
|
}
|
||
|
} else {
|
||
|
lv_coord_t seg_w = pattern[width_safe - 1].x - pattern[aa_last_corner].x;
|
||
|
if(main_line->sx < 0) {
|
||
|
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w, main_line->p1.y + pattern[aa_last_corner].y - 1,
|
||
|
seg_w + main_line->sx, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w, main_line->p2.y + pattern[aa_last_corner].y + 1,
|
||
|
-(seg_w + main_line->sx), mask, style->line.color, opa);
|
||
|
|
||
|
} else {
|
||
|
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
|
||
|
seg_w + main_line->sx, mask, style->line.color, opa);
|
||
|
|
||
|
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
|
||
|
-(seg_w + main_line->sx), mask, style->line.color, opa);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
|
||
|
/*Shift the anti aliasing on the edges (-1, 1 or 0 (zero only in case width == 0))*/
|
||
|
lv_coord_t aa_shift1;
|
||
|
lv_coord_t aa_shift2;
|
||
|
|
||
|
if(main_line->hor == false) {
|
||
|
if(main_line->sx < 0) {
|
||
|
aa_shift1 = -1;
|
||
|
aa_shift2 = width == 0 ? 0 : aa_shift1;
|
||
|
} else {
|
||
|
aa_shift2 = 1;
|
||
|
aa_shift1 = width == 0 ? 0 : aa_shift2;
|
||
|
}
|
||
|
} else {
|
||
|
if(main_line->sy < 0) {
|
||
|
aa_shift1 = -1;
|
||
|
aa_shift2 = width == 0 ? 0 : aa_shift1;
|
||
|
} else {
|
||
|
aa_shift2 = 1;
|
||
|
aa_shift1 = width == 0 ? 0 : aa_shift2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
volatile lv_point_t prev_p;
|
||
|
prev_p.x = main_line->p1.x;
|
||
|
prev_p.y = main_line->p1.y;
|
||
|
lv_area_t draw_area;
|
||
|
bool first_run = true;
|
||
|
|
||
|
if(main_line->hor) {
|
||
|
while(line_next_y(main_line)) {
|
||
|
for(i = 0; i < width; i++) {
|
||
|
draw_area.x1 = prev_p.x + pattern[i].x;
|
||
|
draw_area.y1 = prev_p.y + pattern[i].y;
|
||
|
draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x - 1;
|
||
|
draw_area.y2 = draw_area.y1;
|
||
|
fill_fp(&draw_area, mask, style->line.color, opa);
|
||
|
|
||
|
/* Fill the gaps
|
||
|
* When stepping in y one pixel remains empty on every corner (don't do this on the first segment ) */
|
||
|
if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
|
||
|
px_fp(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
|
||
|
-(main_line->p_act.x - prev_p.x), mask, style->line.color, opa);
|
||
|
lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
|
||
|
main_line->p_act.x - prev_p.x, mask, style->line.color, opa);
|
||
|
#endif
|
||
|
|
||
|
first_run = false;
|
||
|
|
||
|
prev_p.x = main_line->p_act.x;
|
||
|
prev_p.y = main_line->p_act.y;
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < width; i++) {
|
||
|
draw_area.x1 = prev_p.x + pattern[i].x;
|
||
|
draw_area.y1 = prev_p.y + pattern[i].y;
|
||
|
draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x;
|
||
|
draw_area.y2 = draw_area.y1;
|
||
|
fill_fp(&draw_area, mask, style->line.color, opa);
|
||
|
|
||
|
/* Fill the gaps
|
||
|
* When stepping in y one pixel remains empty on every corner */
|
||
|
if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
|
||
|
px_fp(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
|
||
|
-(main_line->p_act.x - prev_p.x + 1), mask, style->line.color, opa);
|
||
|
lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
|
||
|
main_line->p_act.x - prev_p.x + 1, mask, style->line.color, opa);
|
||
|
#endif
|
||
|
}
|
||
|
/*Rather a vertical line*/
|
||
|
else {
|
||
|
|
||
|
while(line_next_x(main_line)) {
|
||
|
for(i = 0; i < width; i++) {
|
||
|
draw_area.x1 = prev_p.x + pattern[i].x;
|
||
|
draw_area.y1 = prev_p.y + pattern[i].y;
|
||
|
draw_area.x2 = draw_area.x1;
|
||
|
draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y - 1;
|
||
|
|
||
|
fill_fp(&draw_area, mask, style->line.color, opa);
|
||
|
|
||
|
/* Fill the gaps
|
||
|
* When stepping in x one pixel remains empty on every corner (don't do this on the first segment ) */
|
||
|
if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
|
||
|
px_fp(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
|
||
|
-(main_line->p_act.y - prev_p.y), mask, style->line.color, opa);
|
||
|
lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
|
||
|
main_line->p_act.y - prev_p.y, mask, style->line.color, opa);
|
||
|
#endif
|
||
|
|
||
|
first_run = false;
|
||
|
|
||
|
prev_p.x = main_line->p_act.x;
|
||
|
prev_p.y = main_line->p_act.y;
|
||
|
}
|
||
|
|
||
|
/*Draw the last part*/
|
||
|
for(i = 0; i < width; i++) {
|
||
|
draw_area.x1 = prev_p.x + pattern[i].x;
|
||
|
draw_area.y1 = prev_p.y + pattern[i].y;
|
||
|
draw_area.x2 = draw_area.x1;
|
||
|
draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y;
|
||
|
|
||
|
fill_fp(&draw_area, mask, style->line.color, opa);
|
||
|
|
||
|
/* Fill the gaps
|
||
|
* When stepping in x one pixel remains empty on every corner */
|
||
|
if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
|
||
|
px_fp(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if LV_ANTIALIAS
|
||
|
lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
|
||
|
-(main_line->p_act.y - prev_p.y + 1), mask, style->line.color, opa);
|
||
|
lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
|
||
|
main_line->p_act.y - prev_p.y + 1, mask, style->line.color, opa);
|
||
|
#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;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Iterate until step one in x direction.
|
||
|
* @param line
|
||
|
* @return
|
||
|
*/
|
||
|
static bool line_next_x(line_draw_t * line)
|
||
|
{
|
||
|
lv_coord_t last_x = line->p_act.x;
|
||
|
|
||
|
do {
|
||
|
if(!line_next(line)) return false;
|
||
|
} while(last_x == line->p_act.x);
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|