208 lines
6.0 KiB
C
208 lines
6.0 KiB
C
/**
|
|
* @file lv_draw_sw_polygon.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "lv_draw_sw.h"
|
|
#include "../../misc/lv_math.h"
|
|
#include "../../misc/lv_mem.h"
|
|
#include "../../misc/lv_area.h"
|
|
#include "../../misc/lv_color.h"
|
|
#include "../lv_draw_rect.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Draw a polygon. Only convex polygons are supported
|
|
* @param points an array of points
|
|
* @param point_cnt number of points
|
|
* @param clip_area polygon will be drawn only in this area
|
|
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
|
|
*/
|
|
void lv_draw_sw_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
|
|
uint16_t point_cnt)
|
|
{
|
|
#if LV_DRAW_COMPLEX
|
|
if(point_cnt < 3) return;
|
|
if(points == NULL) return;
|
|
|
|
/*Join adjacent points if they are on the same coordinate*/
|
|
lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t));
|
|
if(p == NULL) return;
|
|
uint16_t i;
|
|
uint16_t pcnt = 0;
|
|
p[0] = points[0];
|
|
for(i = 0; i < point_cnt - 1; i++) {
|
|
if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
|
|
p[pcnt] = points[i];
|
|
pcnt++;
|
|
}
|
|
}
|
|
/*The first and the last points are also adjacent*/
|
|
if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
|
|
p[pcnt] = points[point_cnt - 1];
|
|
pcnt++;
|
|
}
|
|
|
|
point_cnt = pcnt;
|
|
if(point_cnt < 3) {
|
|
lv_mem_buf_release(p);
|
|
return;
|
|
}
|
|
|
|
lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
|
|
|
|
for(i = 0; i < point_cnt; i++) {
|
|
poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x);
|
|
poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y);
|
|
poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x);
|
|
poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y);
|
|
}
|
|
|
|
bool is_common;
|
|
lv_area_t clip_area;
|
|
is_common = _lv_area_intersect(&clip_area, &poly_coords, draw_ctx->clip_area);
|
|
if(!is_common) {
|
|
lv_mem_buf_release(p);
|
|
return;
|
|
}
|
|
|
|
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
|
|
draw_ctx->clip_area = &clip_area;
|
|
|
|
/*Find the lowest point*/
|
|
lv_coord_t y_min = p[0].y;
|
|
int16_t y_min_i = 0;
|
|
|
|
for(i = 1; i < point_cnt; i++) {
|
|
if(p[i].y < y_min) {
|
|
y_min = p[i].y;
|
|
y_min_i = i;
|
|
}
|
|
}
|
|
|
|
lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt);
|
|
lv_draw_mask_line_param_t * mp_next = mp;
|
|
|
|
int32_t i_prev_left = y_min_i;
|
|
int32_t i_prev_right = y_min_i;
|
|
int32_t i_next_left;
|
|
int32_t i_next_right;
|
|
uint32_t mask_cnt = 0;
|
|
|
|
/*Get the index of the left and right points*/
|
|
i_next_left = y_min_i - 1;
|
|
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
|
|
|
|
i_next_right = y_min_i + 1;
|
|
if(i_next_right > point_cnt - 1) i_next_right = 0;
|
|
|
|
/**
|
|
* Check if the order of points is inverted or not.
|
|
* The normal case is when the left point is on `y_min_i - 1`
|
|
* Explanation:
|
|
* if angle(p_left) < angle(p_right) -> inverted
|
|
* dy_left/dx_left < dy_right/dx_right
|
|
* dy_left * dx_right < dy_right * dx_left
|
|
*/
|
|
lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x;
|
|
lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x;
|
|
lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y;
|
|
lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y;
|
|
|
|
bool inv = false;
|
|
if(dyl * dxr < dyr * dxl) inv = true;
|
|
|
|
do {
|
|
if(!inv) {
|
|
i_next_left = i_prev_left - 1;
|
|
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
|
|
|
|
i_next_right = i_prev_right + 1;
|
|
if(i_next_right > point_cnt - 1) i_next_right = 0;
|
|
}
|
|
else {
|
|
i_next_left = i_prev_left + 1;
|
|
if(i_next_left > point_cnt - 1) i_next_left = 0;
|
|
|
|
i_next_right = i_prev_right - 1;
|
|
if(i_next_right < 0) i_next_right = point_cnt + i_next_right;
|
|
}
|
|
|
|
if(p[i_next_left].y >= p[i_prev_left].y) {
|
|
if(p[i_next_left].y != p[i_prev_left].y &&
|
|
p[i_next_left].x != p[i_prev_left].x) {
|
|
lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y,
|
|
p[i_next_left].x, p[i_next_left].y,
|
|
LV_DRAW_MASK_LINE_SIDE_RIGHT);
|
|
lv_draw_mask_add(mp_next, mp);
|
|
mp_next++;
|
|
}
|
|
mask_cnt++;
|
|
i_prev_left = i_next_left;
|
|
}
|
|
|
|
if(mask_cnt == point_cnt) break;
|
|
|
|
if(p[i_next_right].y >= p[i_prev_right].y) {
|
|
if(p[i_next_right].y != p[i_prev_right].y &&
|
|
p[i_next_right].x != p[i_prev_right].x) {
|
|
|
|
lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y,
|
|
p[i_next_right].x, p[i_next_right].y,
|
|
LV_DRAW_MASK_LINE_SIDE_LEFT);
|
|
lv_draw_mask_add(mp_next, mp);
|
|
mp_next++;
|
|
}
|
|
mask_cnt++;
|
|
i_prev_right = i_next_right;
|
|
}
|
|
|
|
} while(mask_cnt < point_cnt);
|
|
|
|
lv_draw_rect(draw_ctx, draw_dsc, &poly_coords);
|
|
|
|
lv_draw_mask_remove_custom(mp);
|
|
|
|
lv_mem_buf_release(mp);
|
|
lv_mem_buf_release(p);
|
|
|
|
draw_ctx->clip_area = clip_area_ori;
|
|
#else
|
|
LV_UNUSED(points);
|
|
LV_UNUSED(point_cnt);
|
|
LV_UNUSED(draw_ctx);
|
|
LV_UNUSED(draw_dsc);
|
|
LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0");
|
|
#endif /*LV_DRAW_COMPLEX*/
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|