2018-06-07 15:32:19 +02:00
|
|
|
/**
|
|
|
|
* @file lv_draw_triangle.c
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* INCLUDES
|
|
|
|
*********************/
|
|
|
|
#include "lv_draw_triangle.h"
|
|
|
|
#include "../lv_misc/lv_math.h"
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* DEFINES
|
|
|
|
*********************/
|
2019-05-31 07:51:18 +02:00
|
|
|
|
2018-06-07 15:32:19 +02:00
|
|
|
/**********************
|
|
|
|
* TYPEDEFS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC PROTOTYPES
|
|
|
|
**********************/
|
2019-05-31 07:51:18 +02:00
|
|
|
void tri_draw_flat(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa);
|
|
|
|
void tri_draw_tall(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa);
|
|
|
|
static void point_swap(lv_point_t * p1, lv_point_t * p2);
|
2018-06-07 15:32:19 +02:00
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC VARIABLES
|
|
|
|
**********************/
|
2019-05-31 07:51:18 +02:00
|
|
|
static uint8_t corr_value;
|
2018-06-07 15:32:19 +02:00
|
|
|
|
|
|
|
/**********************
|
|
|
|
* MACROS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* GLOBAL FUNCTIONS
|
|
|
|
**********************/
|
2019-03-18 16:16:52 +01:00
|
|
|
|
2018-06-07 15:32:19 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param points pointer to an array with 3 points
|
|
|
|
* @param mask the triangle will be drawn only in this mask
|
2019-03-18 16:16:52 +01:00
|
|
|
* @param style style for of the triangle
|
2019-03-17 05:22:25 +01:00
|
|
|
* @param opa_scale scale down all opacities by the factor
|
2018-06-07 15:32:19 +02:00
|
|
|
*/
|
2019-04-04 07:15:40 +02:00
|
|
|
void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style,
|
2019-04-17 15:14:09 +02:00
|
|
|
lv_opa_t opa_scale)
|
|
|
|
{
|
|
|
|
/*Return is the triangle is degenerated*/
|
|
|
|
if(points[0].x == points[1].x && points[0].y == points[1].y) return;
|
|
|
|
if(points[1].x == points[2].x && points[1].y == points[2].y) return;
|
|
|
|
if(points[0].x == points[2].x && points[0].y == points[2].y) return;
|
|
|
|
|
|
|
|
if(points[0].x == points[1].x && points[1].x == points[2].x) return;
|
|
|
|
if(points[0].y == points[1].y && points[1].y == points[2].y) return;
|
|
|
|
|
|
|
|
lv_opa_t opa = opa_scale == LV_OPA_COVER
|
|
|
|
? style->body.opa
|
|
|
|
: (uint16_t)((uint16_t)style->body.opa * opa_scale) >> 8;
|
|
|
|
|
|
|
|
/*Is the triangle flat or tall?*/
|
|
|
|
lv_coord_t x_min = LV_MATH_MIN(LV_MATH_MIN(points[0].x, points[1].x), points[2].x);
|
|
|
|
lv_coord_t x_max = LV_MATH_MAX(LV_MATH_MAX(points[0].x, points[1].x), points[2].x);
|
|
|
|
lv_coord_t y_min = LV_MATH_MIN(LV_MATH_MIN(points[0].y, points[1].y), points[2].y);
|
2019-04-18 05:58:09 +02:00
|
|
|
lv_coord_t y_max = LV_MATH_MAX(LV_MATH_MAX(points[0].y, points[1].y), points[2].y);
|
2019-04-17 15:14:09 +02:00
|
|
|
|
2019-05-31 07:51:18 +02:00
|
|
|
if(opa < LV_OPA_MAX) {
|
|
|
|
/*Simply draw the triangles with opacity */
|
|
|
|
corr_value = 1;
|
2019-05-31 07:40:00 +02:00
|
|
|
tri_draw_tall(points, mask, style, opa);
|
2019-05-31 07:51:18 +02:00
|
|
|
} else {
|
|
|
|
/* Draw the tall rectangles from vertical lines
|
|
|
|
* and from the flat triangles from horizontal lines
|
|
|
|
* to minimize the number of lines.
|
|
|
|
* Some pixels are overdrawn on the common edges of the triangles
|
|
|
|
* so use it only if the triangle has no opacity*/
|
|
|
|
|
|
|
|
corr_value = 0;
|
|
|
|
|
|
|
|
/* Draw from horizontal lines*/
|
|
|
|
if(x_max - x_min < y_max - y_min) {
|
|
|
|
tri_draw_tall(points, mask, style, opa);
|
|
|
|
}
|
|
|
|
/*Else flat so draw from vertical lines*/
|
|
|
|
else {
|
|
|
|
tri_draw_flat(points, mask, style, opa);
|
|
|
|
}
|
2019-04-17 15:14:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC FUNCTIONS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
void tri_draw_flat(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa)
|
2018-06-07 15:32:19 +02:00
|
|
|
{
|
2019-05-09 06:03:25 +02:00
|
|
|
/*Return if the points are out of the mask*/
|
|
|
|
if(points[0].x < mask->x1 &&
|
|
|
|
points[1].x < mask->x1 &&
|
|
|
|
points[2].x < mask->x1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(points[0].x > mask->x2 &&
|
|
|
|
points[1].x > mask->x2 &&
|
|
|
|
points[2].x > mask->x2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(points[0].y < mask->y1 &&
|
|
|
|
points[1].y < mask->y1 &&
|
|
|
|
points[2].y < mask->y1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(points[0].y > mask->y2 &&
|
|
|
|
points[1].y > mask->y2 &&
|
|
|
|
points[2].y > mask->y2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-07 15:32:19 +02:00
|
|
|
lv_point_t tri[3];
|
|
|
|
|
|
|
|
memcpy(tri, points, sizeof(tri));
|
|
|
|
|
|
|
|
/*Sort the vertices according to their y coordinate (0: y max, 1: y mid, 2:y min)*/
|
|
|
|
if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
|
|
|
|
if(tri[2].y < tri[1].y) point_swap(&tri[2], &tri[1]);
|
|
|
|
if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
|
|
|
|
|
|
|
|
/*Draw the triangle*/
|
|
|
|
lv_point_t edge1;
|
2019-04-04 07:15:40 +02:00
|
|
|
lv_coord_t dx1 = LV_MATH_ABS(tri[0].x - tri[1].x);
|
|
|
|
lv_coord_t sx1 = tri[0].x < tri[1].x ? 1 : -1;
|
|
|
|
lv_coord_t dy1 = LV_MATH_ABS(tri[0].y - tri[1].y);
|
|
|
|
lv_coord_t sy1 = tri[0].y < tri[1].y ? 1 : -1;
|
2018-06-07 15:32:19 +02:00
|
|
|
lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
|
|
|
|
lv_coord_t err_tmp1;
|
|
|
|
|
|
|
|
lv_point_t edge2;
|
2019-04-04 07:15:40 +02:00
|
|
|
lv_coord_t dx2 = LV_MATH_ABS(tri[0].x - tri[2].x);
|
|
|
|
lv_coord_t sx2 = tri[0].x < tri[2].x ? 1 : -1;
|
|
|
|
lv_coord_t dy2 = LV_MATH_ABS(tri[0].y - tri[2].y);
|
|
|
|
lv_coord_t sy2 = tri[0].y < tri[2].y ? 1 : -1;
|
2018-06-07 15:32:19 +02:00
|
|
|
lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
|
|
|
|
lv_coord_t err_tmp2;
|
|
|
|
|
|
|
|
lv_coord_t y1_tmp;
|
|
|
|
lv_coord_t y2_tmp;
|
|
|
|
|
|
|
|
edge1.x = tri[0].x;
|
|
|
|
edge1.y = tri[0].y;
|
|
|
|
edge2.x = tri[0].x;
|
|
|
|
edge2.y = tri[0].y;
|
|
|
|
lv_area_t act_area;
|
|
|
|
lv_area_t draw_area;
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
act_area.x1 = edge1.x;
|
2019-04-04 07:15:40 +02:00
|
|
|
act_area.x2 = edge2.x;
|
2018-06-07 15:32:19 +02:00
|
|
|
act_area.y1 = edge1.y;
|
2019-04-04 07:15:40 +02:00
|
|
|
act_area.y2 = edge2.y;
|
2018-06-07 15:32:19 +02:00
|
|
|
|
2019-04-18 05:58:09 +02:00
|
|
|
/* Get the area of a line.
|
|
|
|
* Adjust it a little bit to perfectly match (no redrawn pixels) with the adjacent triangles*/
|
2018-06-07 15:32:19 +02:00
|
|
|
draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
|
2019-05-31 07:51:18 +02:00
|
|
|
draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2) - corr_value;
|
2019-05-31 07:40:00 +02:00
|
|
|
draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
|
|
|
|
draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
|
2019-04-18 05:58:09 +02:00
|
|
|
|
2019-03-18 16:16:52 +01:00
|
|
|
lv_draw_fill(&draw_area, mask, style->body.main_color, opa);
|
2018-06-07 15:32:19 +02:00
|
|
|
|
|
|
|
/*Calc. the next point of edge1*/
|
|
|
|
y1_tmp = edge1.y;
|
|
|
|
do {
|
2018-06-19 09:49:58 +02:00
|
|
|
if(edge1.x == tri[1].x && edge1.y == tri[1].y) {
|
2018-06-07 15:32:19 +02:00
|
|
|
|
2019-04-04 07:15:40 +02:00
|
|
|
dx1 = LV_MATH_ABS(tri[1].x - tri[2].x);
|
|
|
|
sx1 = tri[1].x < tri[2].x ? 1 : -1;
|
|
|
|
dy1 = LV_MATH_ABS(tri[1].y - tri[2].y);
|
|
|
|
sy1 = tri[1].y < tri[2].y ? 1 : -1;
|
2018-06-07 15:32:19 +02:00
|
|
|
err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
|
2019-05-31 07:40:00 +02:00
|
|
|
} else if(edge1.x == tri[2].x && edge1.y == tri[2].y) {
|
2019-04-04 07:15:40 +02:00
|
|
|
return;
|
2019-05-31 07:40:00 +02:00
|
|
|
}
|
2018-06-07 15:32:19 +02:00
|
|
|
err_tmp1 = err1;
|
2018-06-19 09:49:58 +02:00
|
|
|
if(err_tmp1 > -dx1) {
|
2018-06-07 15:32:19 +02:00
|
|
|
err1 -= dy1;
|
|
|
|
edge1.x += sx1;
|
|
|
|
}
|
2018-06-19 09:49:58 +02:00
|
|
|
if(err_tmp1 < dy1) {
|
2018-06-07 15:32:19 +02:00
|
|
|
err1 += dx1;
|
|
|
|
edge1.y += sy1;
|
|
|
|
}
|
|
|
|
} while(edge1.y == y1_tmp);
|
|
|
|
|
|
|
|
/*Calc. the next point of edge2*/
|
|
|
|
y2_tmp = edge2.y;
|
|
|
|
do {
|
2019-05-31 07:40:00 +02:00
|
|
|
if(edge2.x == tri[2].x && edge2.y == tri[2].y) return;
|
2018-06-07 15:32:19 +02:00
|
|
|
err_tmp2 = err2;
|
2018-06-19 09:49:58 +02:00
|
|
|
if(err_tmp2 > -dx2) {
|
2018-06-07 15:32:19 +02:00
|
|
|
err2 -= dy2;
|
|
|
|
edge2.x += sx2;
|
|
|
|
}
|
2018-06-19 09:49:58 +02:00
|
|
|
if(err_tmp2 < dy2) {
|
2018-06-07 15:32:19 +02:00
|
|
|
err2 += dx2;
|
|
|
|
edge2.y += sy2;
|
|
|
|
}
|
|
|
|
} while(edge2.y == y2_tmp);
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 15:14:09 +02:00
|
|
|
|
|
|
|
void tri_draw_tall(const lv_point_t * points, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Better to draw from vertical lines
|
|
|
|
* |\
|
|
|
|
* | |
|
|
|
|
* | |
|
|
|
|
* | \
|
|
|
|
* | |
|
|
|
|
* |___|
|
|
|
|
*/
|
|
|
|
|
|
|
|
lv_point_t tri[3];
|
|
|
|
|
|
|
|
memcpy(tri, points, sizeof(tri));
|
|
|
|
|
|
|
|
/*Sort the vertices according to their x coordinate (0: x max, 1: x mid, 2:x min)*/
|
|
|
|
if(tri[1].x < tri[0].x) point_swap(&tri[1], &tri[0]);
|
|
|
|
if(tri[2].x < tri[1].x) point_swap(&tri[2], &tri[1]);
|
|
|
|
if(tri[1].x < tri[0].x) point_swap(&tri[1], &tri[0]);
|
|
|
|
|
|
|
|
/*Draw the triangle*/
|
|
|
|
lv_point_t edge1;
|
|
|
|
lv_coord_t dx1 = LV_MATH_ABS(tri[0].x - tri[1].x);
|
|
|
|
lv_coord_t sx1 = tri[0].x < tri[1].x ? 1 : -1;
|
|
|
|
lv_coord_t dy1 = LV_MATH_ABS(tri[0].y - tri[1].y);
|
|
|
|
lv_coord_t sy1 = tri[0].y < tri[1].y ? 1 : -1;
|
|
|
|
lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
|
|
|
|
lv_coord_t err_tmp1;
|
|
|
|
|
|
|
|
lv_point_t edge2;
|
|
|
|
lv_coord_t dx2 = LV_MATH_ABS(tri[0].x - tri[2].x);
|
|
|
|
lv_coord_t sx2 = tri[0].x < tri[2].x ? 1 : -1;
|
|
|
|
lv_coord_t dy2 = LV_MATH_ABS(tri[0].y - tri[2].y);
|
|
|
|
lv_coord_t sy2 = tri[0].y < tri[2].y ? 1 : -1;
|
|
|
|
lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
|
|
|
|
lv_coord_t err_tmp2;
|
|
|
|
|
|
|
|
lv_coord_t x1_tmp;
|
|
|
|
lv_coord_t x2_tmp;
|
|
|
|
|
|
|
|
edge1.x = tri[0].x;
|
|
|
|
edge1.y = tri[0].y;
|
|
|
|
edge2.x = tri[0].x;
|
|
|
|
edge2.y = tri[0].y;
|
|
|
|
lv_area_t act_area;
|
|
|
|
lv_area_t draw_area;
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
act_area.x1 = edge1.x;
|
|
|
|
act_area.x2 = edge2.x;
|
|
|
|
act_area.y1 = edge1.y;
|
|
|
|
act_area.y2 = edge2.y;
|
|
|
|
|
|
|
|
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);
|
2019-05-31 07:51:18 +02:00
|
|
|
draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2) - corr_value;
|
2019-04-18 05:58:09 +02:00
|
|
|
|
2019-04-17 15:14:09 +02:00
|
|
|
lv_draw_fill(&draw_area, mask, style->body.main_color, opa);
|
|
|
|
|
|
|
|
/*Calc. the next point of edge1*/
|
|
|
|
x1_tmp = edge1.x;
|
|
|
|
do {
|
|
|
|
if(edge1.y == tri[1].y && edge1.x == tri[1].x) {
|
|
|
|
|
|
|
|
dx1 = LV_MATH_ABS(tri[1].x - tri[2].x);
|
|
|
|
sx1 = tri[1].x < tri[2].x ? 1 : -1;
|
|
|
|
dy1 = LV_MATH_ABS(tri[1].y - tri[2].y);
|
|
|
|
sy1 = tri[1].y < tri[2].y ? 1 : -1;
|
|
|
|
err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
|
2019-05-31 07:40:00 +02:00
|
|
|
} else if(edge1.y == tri[2].y && edge1.x == tri[2].x) {
|
2019-04-17 15:14:09 +02:00
|
|
|
return;
|
2019-05-31 07:40:00 +02:00
|
|
|
}
|
2019-04-17 15:14:09 +02:00
|
|
|
err_tmp1 = err1;
|
|
|
|
if(err_tmp1 > -dx1) {
|
|
|
|
err1 -= dy1;
|
|
|
|
edge1.x += sx1;
|
|
|
|
}
|
|
|
|
if(err_tmp1 < dy1) {
|
|
|
|
err1 += dx1;
|
|
|
|
edge1.y += sy1;
|
|
|
|
}
|
|
|
|
} while(edge1.x == x1_tmp);
|
|
|
|
|
|
|
|
/*Calc. the next point of edge2*/
|
|
|
|
x2_tmp = edge2.x;
|
|
|
|
do {
|
2019-05-31 07:40:00 +02:00
|
|
|
if(edge2.y == tri[2].y && edge2.x == tri[2].x) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-17 15:14:09 +02:00
|
|
|
err_tmp2 = err2;
|
|
|
|
if(err_tmp2 > -dx2) {
|
|
|
|
err2 -= dy2;
|
|
|
|
edge2.x += sx2;
|
|
|
|
}
|
|
|
|
if(err_tmp2 < dy2) {
|
|
|
|
err2 += dx2;
|
|
|
|
edge2.y += sy2;
|
|
|
|
}
|
|
|
|
} while(edge2.x == x2_tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-07 15:32:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Swap two points
|
|
|
|
* p1 pointer to the first point
|
|
|
|
* p2 pointer to the second point
|
|
|
|
*/
|
|
|
|
static void point_swap(lv_point_t * p1, lv_point_t * p2)
|
|
|
|
{
|
|
|
|
lv_point_t tmp;
|
|
|
|
tmp.x = p1->x;
|
|
|
|
tmp.y = p1->y;
|
|
|
|
|
|
|
|
p1->x = p2->x;
|
|
|
|
p1->y = p2->y;
|
|
|
|
|
|
|
|
p2->x = tmp.x;
|
|
|
|
p2->y = tmp.y;
|
|
|
|
}
|