feat(line): allow defining an array of point in the draw task (#9269)

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Gabor Kiss-Vamosi
2026-01-21 11:21:59 +01:00
committed by GitHub
parent af5c3f12f6
commit d99d2a553e
18 changed files with 366 additions and 203 deletions
+41 -35
View File
@@ -584,45 +584,51 @@ static void chart_event_cb(lv_event_t * e)
lv_obj_get_coords(obj, &obj_coords);
const lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
if(base_dsc->id1 == 1) ser = lv_chart_get_series_next(obj, ser);
lv_point_precise_t p1;
lv_point_precise_t p2;
int32_t i;
for(i = 0; i < draw_line_dsc->point_cnt - 1; i++) {
p1 = draw_line_dsc->points[i];
p2 = draw_line_dsc->points[i + 1];
lv_draw_triangle_dsc_t tri_dsc;
lv_draw_triangle_dsc_init(&tri_dsc);
tri_dsc.p[0].x = (int32_t)p1.x;
tri_dsc.p[0].y = (int32_t)p1.y;
tri_dsc.p[1].x = (int32_t)p2.x;
tri_dsc.p[1].y = (int32_t)p2.y;
tri_dsc.p[2].x = (int32_t)(p1.y < p2.y ? p1.x : p2.x);
tri_dsc.p[2].y = (int32_t)LV_MAX(p1.y, p2.y);
tri_dsc.grad.dir = LV_GRAD_DIR_VER;
lv_draw_triangle_dsc_t tri_dsc;
lv_draw_triangle_dsc_init(&tri_dsc);
tri_dsc.p[0].x = (int32_t)draw_line_dsc->p1.x;
tri_dsc.p[0].y = (int32_t)draw_line_dsc->p1.y;
tri_dsc.p[1].x = (int32_t)draw_line_dsc->p2.x;
tri_dsc.p[1].y = (int32_t)draw_line_dsc->p2.y;
tri_dsc.p[2].x = (int32_t)(draw_line_dsc->p1.y < draw_line_dsc->p2.y ? draw_line_dsc->p1.x : draw_line_dsc->p2.x);
tri_dsc.p[2].y = (int32_t)LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y);
tri_dsc.grad.dir = LV_GRAD_DIR_VER;
int32_t full_h = lv_obj_get_height(obj);
int32_t fract_upper = (int32_t)(LV_MIN(p1.y, p2.y) - obj_coords.y1) * 255 / full_h;
int32_t fract_lower = (int32_t)(LV_MAX(p1.y, p2.y) - obj_coords.y1) * 255 / full_h;
tri_dsc.grad.stops[0].color = lv_chart_get_series_color(obj, ser);
tri_dsc.grad.stops[0].opa = 255 - fract_upper;
tri_dsc.grad.stops[0].frac = 0;
tri_dsc.grad.stops[1].color = lv_chart_get_series_color(obj, ser);
tri_dsc.grad.stops[1].opa = 255 - fract_lower;
tri_dsc.grad.stops[1].frac = 255;
int32_t full_h = lv_obj_get_height(obj);
int32_t fract_uppter = (int32_t)(LV_MIN(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - obj_coords.y1) * 255 / full_h;
int32_t fract_lower = (int32_t)(LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - obj_coords.y1) * 255 / full_h;
tri_dsc.grad.stops[0].color = lv_chart_get_series_color(obj, ser);
tri_dsc.grad.stops[0].opa = 255 - fract_uppter;
tri_dsc.grad.stops[0].frac = 0;
tri_dsc.grad.stops[1].color = lv_chart_get_series_color(obj, ser);
tri_dsc.grad.stops[1].opa = 255 - fract_lower;
tri_dsc.grad.stops[1].frac = 255;
lv_draw_triangle(base_dsc->layer, &tri_dsc);
lv_draw_triangle(base_dsc->layer, &tri_dsc);
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_grad.dir = LV_GRAD_DIR_VER;
rect_dsc.bg_grad.stops[0].color = lv_chart_get_series_color(obj, ser);
rect_dsc.bg_grad.stops[0].frac = 0;
rect_dsc.bg_grad.stops[0].opa = 255 - fract_lower;
rect_dsc.bg_grad.stops[1].color = lv_chart_get_series_color(obj, ser);
rect_dsc.bg_grad.stops[1].frac = 255;
rect_dsc.bg_grad.stops[1].opa = 0;
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_grad.dir = LV_GRAD_DIR_VER;
rect_dsc.bg_grad.stops[0].color = lv_chart_get_series_color(obj, ser);
rect_dsc.bg_grad.stops[0].frac = 0;
rect_dsc.bg_grad.stops[0].opa = 255 - fract_lower;
rect_dsc.bg_grad.stops[1].color = lv_chart_get_series_color(obj, ser);
rect_dsc.bg_grad.stops[1].frac = 255;
rect_dsc.bg_grad.stops[1].opa = 0;
lv_area_t rect_area;
rect_area.x1 = (int32_t)draw_line_dsc->p1.x;
rect_area.x2 = (int32_t)draw_line_dsc->p2.x;
rect_area.y1 = (int32_t)LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y);
rect_area.y2 = (int32_t)obj_coords.y2;
lv_draw_rect(base_dsc->layer, &rect_dsc, &rect_area);
lv_area_t rect_area;
rect_area.x1 = (int32_t)p1.x;
rect_area.x2 = (int32_t)p2.x;
rect_area.y1 = (int32_t)LV_MAX(p1.y, p2.y);
rect_area.y2 = (int32_t)obj_coords.y2;
lv_draw_rect(base_dsc->layer, &rect_dsc, &rect_area);
}
}
bool add_value = false;
@@ -569,8 +569,10 @@ Line Draw Descriptor
The :cpp:type:`lv_draw_line_dsc_t` line descriptor defines line rendering with
these fields:
:p1: First point of line (supports floating-point coordinates).
:p2: Second point of line (supports floating-point coordinates).
:p1: First point of line (supports floating-point coordinates). Ignored if ``points`` are set.
:p2: Second point of line (supports floating-point coordinates). Ignored if ``points`` are set.
:points: Array of points to draw.
:point_cnt: Number of points in ``points``
:color: Line color.
:width: Line thickness.
:opa: Line opacity (0--255).
@@ -580,11 +582,17 @@ these fields:
:round_end: Rounds the line end.
:raw_end: Set to 1 to skip end calculations if they are unnecessary.
+If a large amount of points needs to be rendered it's recommended to use ``points``
instead of ``p1`` and ``p2`` as it avoids creating many draw tasks.
Functions for line drawing:
- :cpp:expr:`lv_draw_line_dsc_init(&dsc)` initializes a line descriptor.
- :cpp:expr:`lv_draw_line(layer, &dsc)` creates a task to draw a line.
- :cpp:expr:`lv_draw_task_get_line_dsc(draw_task)` retrieves line descriptor.
- :cpp:expr:`lv_draw_line_iterate(draw_task, dsc, callback)` is a helper function
to call a callback which draws a line between two points. This way it doesn't matter if
``p1, p2`` or ``points`` were used as it calls the ``callback`` as needed.
.. lv_example:: widgets/canvas/lv_example_canvas_7
:language: c
+51 -35
View File
@@ -61,47 +61,63 @@ static void add_faded_area(lv_event_t * e)
lv_color_t ser_color = lv_chart_get_series_color(obj, ser);
/*Draw a triangle below the line witch some opacity gradient*/
lv_draw_line_dsc_t * draw_line_dsc = (lv_draw_line_dsc_t *)lv_draw_task_get_draw_dsc(draw_task);
lv_draw_line_dsc_t * draw_line_dsc = lv_draw_task_get_line_dsc(draw_task);
lv_draw_triangle_dsc_t tri_dsc;
lv_draw_triangle_dsc_init(&tri_dsc);
tri_dsc.p[0].x = draw_line_dsc->p1.x;
tri_dsc.p[0].y = draw_line_dsc->p1.y;
tri_dsc.p[1].x = draw_line_dsc->p2.x;
tri_dsc.p[1].y = draw_line_dsc->p2.y;
tri_dsc.p[2].x = draw_line_dsc->p1.y < draw_line_dsc->p2.y ? draw_line_dsc->p1.x : draw_line_dsc->p2.x;
tri_dsc.p[2].y = LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y);
tri_dsc.grad.dir = LV_GRAD_DIR_VER;
int32_t full_h = lv_obj_get_height(obj);
int32_t fract_uppter = (int32_t)(LV_MIN(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - coords.y1) * 255 / full_h;
int32_t fract_lower = (int32_t)(LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - coords.y1) * 255 / full_h;
tri_dsc.grad.stops[0].color = ser_color;
tri_dsc.grad.stops[0].opa = (lv_opa_t)(255 - fract_uppter);
tri_dsc.grad.stops[0].frac = 0;
tri_dsc.grad.stops[1].color = ser_color;
tri_dsc.grad.stops[1].opa = (lv_opa_t)(255 - fract_lower);
tri_dsc.grad.stops[1].frac = 255;
lv_point_precise_t p1;
lv_point_precise_t p2;
int32_t i;
for(i = 0; i < draw_line_dsc->point_cnt - 1; i++) {
p1 = draw_line_dsc->points[i];
p2 = draw_line_dsc->points[i + 1];
if(p1.x == LV_DRAW_LINE_POINT_NONE ||
p1.y == LV_DRAW_LINE_POINT_NONE) {
continue;
}
if(p2.x == LV_DRAW_LINE_POINT_NONE ||
p2.y == LV_DRAW_LINE_POINT_NONE) {
continue;
}
lv_draw_triangle(base_dsc->layer, &tri_dsc);
tri_dsc.p[0].x = p1.x;
tri_dsc.p[0].y = p1.y;
tri_dsc.p[1].x = p2.x;
tri_dsc.p[1].y = p2.y;
tri_dsc.p[2].x = p1.y < p2.y ? p1.x : p2.x;
tri_dsc.p[2].y = LV_MAX(p1.y, p2.y);
tri_dsc.grad.dir = LV_GRAD_DIR_VER;
/*Draw rectangle below the triangle*/
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_grad.dir = LV_GRAD_DIR_VER;
rect_dsc.bg_grad.stops[0].color = ser_color;
rect_dsc.bg_grad.stops[0].frac = 0;
rect_dsc.bg_grad.stops[0].opa = (lv_opa_t)(255 - fract_lower);
rect_dsc.bg_grad.stops[1].color = ser_color;
rect_dsc.bg_grad.stops[1].frac = 255;
rect_dsc.bg_grad.stops[1].opa = 0;
int32_t full_h = lv_obj_get_height(obj);
int32_t fract_upper = (int32_t)(LV_MIN(p1.y, p2.y) - coords.y1) * 255 / full_h;
int32_t fract_lower = (int32_t)(LV_MAX(p1.y, p2.y) - coords.y1) * 255 / full_h;
tri_dsc.grad.stops[0].color = ser_color;
tri_dsc.grad.stops[0].opa = (lv_opa_t)(255 - fract_upper);
tri_dsc.grad.stops[0].frac = 0;
tri_dsc.grad.stops[1].color = ser_color;
tri_dsc.grad.stops[1].opa = (lv_opa_t)(255 - fract_lower);
tri_dsc.grad.stops[1].frac = 255;
lv_area_t rect_area;
rect_area.x1 = (int32_t)draw_line_dsc->p1.x;
rect_area.x2 = (int32_t)draw_line_dsc->p2.x - 1;
rect_area.y1 = (int32_t)LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y);
rect_area.y2 = (int32_t)coords.y2;
lv_draw_rect(base_dsc->layer, &rect_dsc, &rect_area);
lv_draw_triangle(base_dsc->layer, &tri_dsc);
/*Draw rectangle below the triangle*/
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_grad.dir = LV_GRAD_DIR_VER;
rect_dsc.bg_grad.stops[0].color = ser_color;
rect_dsc.bg_grad.stops[0].frac = 0;
rect_dsc.bg_grad.stops[0].opa = (lv_opa_t)(255 - fract_lower);
rect_dsc.bg_grad.stops[1].color = ser_color;
rect_dsc.bg_grad.stops[1].frac = 255;
rect_dsc.bg_grad.stops[1].opa = 0;
lv_area_t rect_area;
rect_area.x1 = (int32_t)p1.x;
rect_area.x2 = (int32_t)p2.x - 1;
rect_area.y1 = (int32_t)LV_MAX(p1.y, p2.y);
rect_area.y2 = (int32_t)coords.y2;
lv_draw_rect(base_dsc->layer, &rect_dsc, &rect_area);
}
}
static void hook_division_lines(lv_event_t * e)
@@ -25,9 +25,6 @@ void lv_example_chart_6(void)
lv_obj_set_size(chart, 200, 150);
lv_obj_align(chart, LV_ALIGN_CENTER, 0, -10);
// lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 40);
// lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 10, 1, true, 30);
lv_obj_add_event_cb(chart, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_refresh_ext_draw_size(chart);
@@ -39,8 +36,6 @@ void lv_example_chart_6(void)
lv_chart_set_next_value(chart, ser, (int32_t)lv_rand(10, 90));
}
// lv_chart_set_scale_x(chart, 500);
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "Click on a point");
lv_obj_align_to(label, chart, LV_ALIGN_OUT_TOP_MID, 0, -5);
+20 -19
View File
@@ -5,28 +5,29 @@ static void draw_event_cb(lv_event_t * e)
{
lv_draw_task_t * draw_task = lv_event_get_draw_task(e);
lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t *)lv_draw_task_get_draw_dsc(draw_task);
if(base_dsc->part == LV_PART_INDICATOR) {
lv_obj_t * obj = lv_event_get_target_obj(e);
lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
lv_draw_rect_dsc_t * rect_draw_dsc = (lv_draw_rect_dsc_t *)lv_draw_task_get_draw_dsc(draw_task);
uint32_t cnt = lv_chart_get_point_count(obj);
if(base_dsc->part != LV_PART_INDICATOR) return;
lv_obj_t * obj = lv_event_get_target_obj(e);
lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
if(fill_draw_dsc == NULL) return;
/*Make older value more transparent*/
rect_draw_dsc->bg_opa = (lv_opa_t)((LV_OPA_COVER * base_dsc->id2) / (cnt - 1));
uint32_t cnt = lv_chart_get_point_count(obj);
/*Make smaller values blue, higher values red*/
int32_t * x_array = lv_chart_get_series_x_array(obj, ser);
int32_t * y_array = lv_chart_get_series_y_array(obj, ser);
/*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/
uint32_t start_point = lv_chart_get_x_start_point(obj, ser);
uint32_t p_act = (start_point + base_dsc->id2) % cnt; /*Consider start point to get the index of the array*/
lv_opa_t x_opa = (lv_opa_t)((x_array[p_act] * LV_OPA_50) / 200);
lv_opa_t y_opa = (lv_opa_t)((y_array[p_act] * LV_OPA_50) / 1000);
/*Make older value more transparent*/
fill_draw_dsc->opa = (lv_opa_t)((LV_OPA_COVER * base_dsc->id2) / (cnt - 1));
rect_draw_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED),
lv_palette_main(LV_PALETTE_BLUE),
x_opa + y_opa);
}
/*Make smaller values blue, higher values red*/
int32_t * x_array = lv_chart_get_series_x_array(obj, ser);
int32_t * y_array = lv_chart_get_series_y_array(obj, ser);
/*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/
uint32_t start_point = lv_chart_get_x_start_point(obj, ser);
uint32_t p_act = (start_point + base_dsc->id2) % cnt; /*Consider start point to get the index of the array*/
lv_opa_t x_opa = (lv_opa_t)((x_array[p_act] * LV_OPA_50) / 200);
lv_opa_t y_opa = (lv_opa_t)((y_array[p_act] * LV_OPA_50) / 1000);
fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_RED),
lv_palette_main(LV_PALETTE_BLUE),
x_opa + y_opa);
}
static void add_data(lv_timer_t * timer)
+1 -1
View File
@@ -129,7 +129,7 @@ static void eve_execute_drawing(lv_draw_eve_unit_t * u)
switch(t->type) {
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_eve_line(t, t->draw_dsc);
lv_draw_line_iterate(t, t->draw_dsc, lv_draw_eve_line);
break;
case LV_DRAW_TASK_TYPE_BORDER:
lv_draw_eve_border(t, t->draw_dsc, &t->area);
+8 -1
View File
@@ -673,8 +673,15 @@ static inline size_t get_draw_dsc_size(lv_draw_task_type_t type)
static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp)
{
LV_PROFILER_DRAW_BEGIN;
if(t->type == LV_DRAW_TASK_TYPE_LINE) {
lv_draw_line_dsc_t * draw_line_dsc = t->draw_dsc;
if(draw_line_dsc->points) {
lv_free(draw_line_dsc->points);
draw_line_dsc->points = NULL;
}
}
/*If it was layer drawing free the layer too*/
if(t->type == LV_DRAW_TASK_TYPE_LAYER) {
else if(t->type == LV_DRAW_TASK_TYPE_LAYER) {
lv_draw_image_dsc_t * draw_image_dsc = t->draw_dsc;
lv_layer_t * layer_drawn = (lv_layer_t *)draw_image_dsc->src;
+80 -5
View File
@@ -58,10 +58,43 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_d
LV_PROFILER_DRAW_BEGIN;
lv_area_t a;
a.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width;
a.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width;
a.y1 = (int32_t)LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width;
a.y2 = (int32_t)LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width;
if(dsc->points == NULL) {
a.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width;
a.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width;
a.y1 = (int32_t)LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width;
a.y2 = (int32_t)LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width;
}
else {
if(dsc->point_cnt <= 1) {
LV_LOG_INFO("Skip line drawing as point_cnt was 1");
LV_PROFILER_DRAW_END;
return;
}
a.x1 = LV_COORD_MAX;
a.y1 = LV_COORD_MAX;
a.x2 = LV_COORD_MIN;
a.y2 = LV_COORD_MIN;
int32_t i;
for(i = 0; i < dsc->point_cnt; i++) {
if(dsc->points[i].x == LV_DRAW_LINE_POINT_NONE ||
dsc->points[i].y == LV_DRAW_LINE_POINT_NONE) {
continue;
}
a.x1 = (int32_t)LV_MIN(a.x1, dsc->points[i].x);
a.x2 = (int32_t)LV_MAX(a.x2, dsc->points[i].x);
a.y1 = (int32_t)LV_MIN(a.y1, dsc->points[i].y);
a.y2 = (int32_t)LV_MAX(a.y2, dsc->points[i].y);
}
if(a.x1 == LV_COORD_MAX) {
LV_LOG_INFO("No valid point was found. Not adding the draw task.");
return;
}
lv_area_increase(&a, dsc->width, dsc->width);
}
if(dsc->base.drop_shadow_opa) {
lv_layer_t * ds_layer = lv_draw_layer_create_drop_shadow(layer, &dsc->base, &a);
@@ -73,13 +106,55 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_d
}
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LINE);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
if(dsc->points) {
lv_draw_line_dsc_t * new_draw_dsc = t->draw_dsc;
size_t array_size = dsc->point_cnt * sizeof(lv_point_precise_t);
lv_point_precise_t * new_points = lv_malloc(array_size);
lv_memcpy(new_points, dsc->points, array_size);
new_draw_dsc->points = new_points;
}
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_line_iterate(lv_draw_task_t * t, lv_draw_line_dsc_t * dsc,
void (*draw_line_cb)(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc))
{
uint32_t i;
lv_point_precise_t * points = dsc->points;
if(points == NULL) {
draw_line_cb(t, dsc);
}
else {
/*Create a temporary dsc where the point array is replaced by 2 points*/
lv_draw_line_dsc_t dsc_tmp = *dsc;
size_t point_cnt = dsc_tmp.point_cnt;
if(point_cnt <= 1) return;
dsc_tmp.points = NULL;
dsc_tmp.point_cnt = 0;
for(i = 0; i < point_cnt - 1; i++) {
if(points[i].x == LV_DRAW_LINE_POINT_NONE ||
points[i].y == LV_DRAW_LINE_POINT_NONE) {
continue;
}
if(points[i + 1].x == LV_DRAW_LINE_POINT_NONE ||
points[i + 1].y == LV_DRAW_LINE_POINT_NONE) {
continue;
}
dsc_tmp.p1 = points[i];
dsc_tmp.p2 = points[i + 1];
draw_line_cb(t, &dsc_tmp);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
+30 -2
View File
@@ -21,19 +21,36 @@ extern "C" {
/*********************
* DEFINES
*********************/
#if LV_USE_FLOAT
#include <float.h>
#define LV_DRAW_LINE_POINT_NONE FLT_MAX
#else
#define LV_DRAW_LINE_POINT_NONE INT32_MAX
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
/**The first point of the line. If `LV_USE_FLOAT` is enabled float number can be also used*/
/**The first point of the line. If `LV_USE_FLOAT` is enabled float number can be also used.
*Ignored if `points` are set*/
lv_point_precise_t p1;
/**The second point of the line. If `LV_USE_FLOAT` is enabled float number can be also used*/
/**The second point of the line. If `LV_USE_FLOAT` is enabled float number can be also used
* Ignored if `points` are set*/
lv_point_precise_t p2;
/**Array of points to draw. If `LV_USE_FLOAT` is enabled, float numbers can also be used.*/
lv_point_precise_t * points;
/**
* Number of points in the `points`
*/
int32_t point_cnt;
/**The color of the line*/
lv_color_t color;
@@ -84,6 +101,17 @@ lv_draw_line_dsc_t * lv_draw_task_get_line_dsc(lv_draw_task_t * task);
*/
void lv_draw_line(lv_layer_t * layer, const lv_draw_line_dsc_t * dsc);
/**
* A helper function to call a callback which draws a line between two points.
* This way it doesn't matter if ``p1, p2`` or ``points`` were used as it calls the
* ``callback`` as needed.
* @param t draw task
* @param dsc pointer to a draw descriptor
* @param draw_line_cb a callback that draws a line between ``dsc->p1`` and ``dsc->p2``
*/
void lv_draw_line_iterate(lv_draw_task_t * t, lv_draw_line_dsc_t * dsc,
void (*draw_line_cb)(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc));
/**********************
* MACROS
**********************/
+1 -1
View File
@@ -324,7 +324,7 @@ static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u)
lv_draw_nema_gfx_layer(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_nema_gfx_line(t, t->draw_dsc);
lv_draw_line_iterate(t, t->draw_dsc, lv_draw_nema_gfx_line);
break;
#if LV_USE_NEMA_VG
case LV_DRAW_TASK_TYPE_ARC:
+7
View File
@@ -538,6 +538,13 @@ static void draw_from_cached_texture(lv_draw_task_t * t)
lv_cache_drop(u->texture_cache, &data_to_find, u);
}
}
/*Do not cache lines rendered from points at dsc->points will be freed*/
else if(t->type == LV_DRAW_TASK_TYPE_LINE) {
lv_draw_line_dsc_t * line_dsc = t->draw_dsc;
if(line_dsc->points) {
lv_cache_drop(u->texture_cache, &data_to_find, u);
}
}
LV_PROFILER_DRAW_END;
}
+3 -3
View File
@@ -15,7 +15,7 @@
* DEFINES
*********************/
#define DRAW_UNIT_ID_DAVE2D 4
/* The amount of tasks exercising pressure to the currrent to get finished
/* The amount of tasks exercising pressure to the current to get finished
* This one is used as the main signal to start to render a block of tasks.
*/
#define DAVE2D_MAX_DRAW_PRESSURE 256
@@ -385,7 +385,7 @@ static int32_t lv_draw_dave2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t *
t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_DAVE2D);
if(t == NULL) {
/* No valid task, but there are tasks waiting to be rendered,
* start to draw then immediatelly.
* start to draw then immediately.
*/
if(false == lv_ll_is_empty(&draw_tasks_on_dlist)) {
draw_pressure = 0;
@@ -519,7 +519,7 @@ static void execute_drawing(lv_draw_dave2d_unit_t * u)
lv_draw_dave2d_image(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_dave2d_line(t, t->draw_dsc);
lv_draw_line_iterate(t, t->draw_dsc, lv_draw_dave2d_line);
break;
case LV_DRAW_TASK_TYPE_ARC:
lv_draw_dave2d_arc(t, t->draw_dsc, &t->area);
+7
View File
@@ -473,6 +473,13 @@ static void draw_from_cached_texture(lv_draw_sdl_unit_t * u)
lv_cache_drop(u->texture_cache, &data_to_find, NULL);
}
}
/*Do not cache lines rendered from points at dsc->points will be freed*/
else if(t->type == LV_DRAW_TASK_TYPE_LINE) {
lv_draw_line_dsc_t * line_dsc = t->draw_dsc;
if(line_dsc->points) {
lv_cache_drop(u->texture_cache, &data_to_find, u);
}
}
}
static void execute_drawing(lv_draw_sdl_unit_t * u)
+1 -1
View File
@@ -408,7 +408,7 @@ static void execute_drawing(lv_draw_task_t * t)
lv_draw_sw_arc(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_sw_line(t, t->draw_dsc);
lv_draw_line_iterate(t, t->draw_dsc, lv_draw_sw_line);
break;
case LV_DRAW_TASK_TYPE_BLUR:
lv_draw_sw_blur(t, t->draw_dsc, &t->area);
+1 -1
View File
@@ -167,7 +167,7 @@ static void draw_execute(lv_draw_vg_lite_unit_t * u)
lv_draw_vg_lite_arc(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_vg_lite_line(t, t->draw_dsc);
lv_draw_line_iterate(t, t->draw_dsc, lv_draw_vg_lite_line);
break;
case LV_DRAW_TASK_TYPE_LAYER:
lv_draw_vg_lite_layer(t, t->draw_dsc, &t->area);
+105 -92
View File
@@ -1018,7 +1018,9 @@ static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer)
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->point_cnt < 2) return;
uint32_t i;
uint32_t ser_cnt = lv_ll_get_len(&chart->series_ll);
if(ser_cnt == 0) return;
int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
@@ -1032,136 +1034,147 @@ static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer)
lv_draw_line_dsc_init(&line_dsc);
line_dsc.base.layer = layer;
lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &line_dsc);
lv_draw_rect_dsc_t point_dsc_default;
lv_draw_rect_dsc_init(&point_dsc_default);
point_dsc_default.base.layer = layer;
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &point_dsc_default);
int32_t point_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
int32_t point_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
line_dsc.base.id1 = ser_cnt - 1;
/*If there are at least as many points as pixels then draw only vertical lines*/
bool crowded_mode = (int32_t)chart->point_cnt >= w;
uint32_t ser_cnt = lv_ll_get_len(&chart->series_ll);
if(ser_cnt == 0) {
int32_t bullet_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
int32_t bullet_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
int32_t extra_space_x = w / (chart->point_cnt - 1) + bullet_w + line_dsc.width;
lv_draw_rect_dsc_t point_draw_dsc;
if(crowded_mode == false) {
lv_draw_rect_dsc_init(&point_draw_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &point_draw_dsc);
point_draw_dsc.base.id1 = line_dsc.base.id1;
}
lv_point_precise_t * points = NULL;
if(crowded_mode) {
points = lv_malloc((w + 2 * extra_space_x) * 3 * sizeof(lv_point_precise_t));
}
else {
points = lv_malloc(chart->point_cnt * sizeof(lv_point_precise_t));
}
if(points == NULL) {
LV_LOG_WARN("Couldn't allocate the points array");
return;
}
line_dsc.base.id1 = ser_cnt - 1;
point_dsc_default.base.id1 = line_dsc.base.id1;
line_dsc.points = points;
/*Go through all data lines*/
LV_LL_READ_BACK(&chart->series_ll, ser) {
if(ser->hidden) {
if(line_dsc.base.id1 > 0) {
line_dsc.base.id1--;
point_dsc_default.base.id1--;
}
continue;
}
line_dsc.color = ser->color;
line_dsc.base.drop_shadow_color = ser->color;
point_dsc_default.bg_color = ser->color;
point_draw_dsc.bg_color = ser->color;
line_dsc.base.id2 = 0;
point_dsc_default.base.id2 = 0;
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
line_dsc.p1.x = x_ofs;
line_dsc.p2.x = x_ofs;
int32_t p_act = start_point;
int32_t p_prev = start_point;
int32_t y_tmp = (int32_t)((int32_t)ser->y_points[p_prev] - chart->ymin[ser->y_axis_sec]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
line_dsc.p2.y = h - y_tmp + y_ofs;
lv_value_precise_t y_min = line_dsc.p2.y;
lv_value_precise_t y_max = line_dsc.p2.y;
lv_value_precise_t y_min = obj->coords.y2;
lv_value_precise_t y_max = obj->coords.y1;
lv_value_precise_t x_prev = -10000;
line_dsc.p1.x = x_ofs;
line_dsc.p2.x = x_ofs;
line_dsc.point_cnt = 0;
uint32_t i;
for(i = 0; i < chart->point_cnt; i++) {
line_dsc.p1.x = line_dsc.p2.x;
line_dsc.p1.y = line_dsc.p2.y;
if(line_dsc.p1.x > layer->_clip_area.x2 + point_w + 1) break;
line_dsc.p2.x = (lv_value_precise_t)((w * i) / (chart->point_cnt - 1)) + x_ofs;
p_act = (start_point + i) % chart->point_cnt;
y_tmp = (int32_t)((int32_t)ser->y_points[p_act] - chart->ymin[ser->y_axis_sec]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
line_dsc.p2.y = h - y_tmp + y_ofs;
if(line_dsc.p2.x < layer->_clip_area.x1 - point_w - 1) {
lv_value_precise_t p_x = (int32_t)((w * i) / (chart->point_cnt - 1)) + x_ofs;
if(p_x > layer->_clip_area.x2 + extra_space_x + 1) break;
if(p_x < layer->_clip_area.x1 - extra_space_x - 1) {
p_prev = p_act;
continue;
}
p_act = (start_point + i) % chart->point_cnt;
/*Don't draw the first point. A second point is also required to draw the line*/
if(i != 0) {
if(crowded_mode) {
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
/*Draw only one vertical line between the min and max y-values on the same x-value*/
y_max = LV_MAX(y_max, line_dsc.p2.y);
y_min = LV_MIN(y_min, line_dsc.p2.y);
if(line_dsc.p1.x != line_dsc.p2.x) {
lv_value_precise_t y_cur = line_dsc.p2.y;
line_dsc.p2.x--; /*It's already on the next x value*/
line_dsc.p1.x = line_dsc.p2.x;
line_dsc.p1.y = y_min;
line_dsc.p2.y = y_max;
if(line_dsc.p1.y == line_dsc.p2.y) line_dsc.p2.y++; /*If they are the same no line will be drawn*/
lv_draw_line(layer, &line_dsc);
line_dsc.p2.x++; /*Compensate the previous x--*/
y_min = y_cur; /*Start the line of the next x from the current last y*/
y_max = y_cur;
}
}
}
else {
lv_area_t point_area;
point_area.x1 = (int32_t)line_dsc.p1.x - point_w;
point_area.x2 = (int32_t)line_dsc.p1.x + point_w;
point_area.y1 = (int32_t)line_dsc.p1.y - point_h;
point_area.y2 = (int32_t)line_dsc.p1.y + point_h;
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
line_dsc.base.id2 = i;
lv_draw_line(layer, &line_dsc);
}
if(point_w && point_h && ser->y_points[p_prev] != LV_CHART_POINT_NONE) {
point_dsc_default.base.id2 = i - 1;
lv_draw_rect(layer, &point_dsc_default, &point_area);
}
}
lv_value_precise_t p_y;
if(ser->y_points[p_act] == LV_CHART_POINT_NONE) {
p_y = LV_DRAW_LINE_POINT_NONE;
}
else {
int32_t v = ser->y_points[p_act];
int32_t min_v = chart->ymin[ser->y_axis_sec];
int32_t max_v = chart->ymax[ser->y_axis_sec];
p_y = (int32_t)lv_map(v, min_v, max_v, y_ofs + h, y_ofs);
}
/*In normal mode just collect the points here*/
if(crowded_mode == false) {
points[line_dsc.point_cnt].x = p_x;
points[line_dsc.point_cnt].y = p_y;
line_dsc.point_cnt++;
}
/*In crowded mode draw vertical lines from the min/max on the same X coordinate*/
else {
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
/*Draw only one vertical line between the min and max y-values on the same x-value*/
y_max = LV_MAX(y_max, p_y);
y_min = LV_MIN(y_min, p_y);
if(x_prev != p_x) {
line_dsc.points[line_dsc.point_cnt].y = y_min;
line_dsc.points[line_dsc.point_cnt].x = p_x;
line_dsc.points[line_dsc.point_cnt + 1].y = y_max;
line_dsc.points[line_dsc.point_cnt + 1].x = p_x;
line_dsc.points[line_dsc.point_cnt + 2].y = LV_DRAW_LINE_POINT_NONE;
line_dsc.points[line_dsc.point_cnt + 2].x = p_x;
/*If they are the same no line would be drawn*/
if(line_dsc.points[line_dsc.point_cnt].y == line_dsc.points[line_dsc.point_cnt + 1].y) {
line_dsc.points[line_dsc.point_cnt + 1].y++;
}
y_min = p_y; /*Start the line of the next x from the current last y*/
y_max = p_y;
x_prev = p_x;
line_dsc.point_cnt += 3;
}
}
}
p_prev = p_act;
}
/*Draw the last point*/
if(!crowded_mode && i == chart->point_cnt) {
/*Draw the line from the accumulated points*/
lv_draw_line(layer, &line_dsc);
if(!crowded_mode) {
point_draw_dsc.bg_color = ser->color;
point_draw_dsc.base.id1 = line_dsc.base.id1;
/*Add the bullets too*/
if(bullet_w > 0 && bullet_h > 0) {
point_draw_dsc.base.id2 = i - 1; /*Start from the last rendered point*/
int32_t j;
for(j = line_dsc.point_cnt - 1; j >= 0; j--) {
if(points[j].y == LV_DRAW_LINE_POINT_NONE) continue;
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_area_t point_area;
point_area.x1 = (int32_t)line_dsc.p2.x - point_w;
point_area.x2 = (int32_t)line_dsc.p2.x + point_w;
point_area.y1 = (int32_t)line_dsc.p2.y - point_h;
point_area.y2 = (int32_t)line_dsc.p2.y + point_h;
point_dsc_default.base.id2 = i - 1;
lv_draw_rect(layer, &point_dsc_default, &point_area);
lv_area_t point_area;
point_area.x1 = (int32_t)points[j].x - bullet_w;
point_area.x2 = (int32_t)points[j].x + bullet_w;
point_area.y1 = (int32_t)points[j].y - bullet_h;
point_area.y2 = (int32_t)points[j].y + bullet_h;
lv_draw_rect(layer, &point_draw_dsc, &point_area);
point_draw_dsc.base.id2--;
}
}
}
if(line_dsc.base.id1 > 0) {
point_dsc_default.base.id1--;
line_dsc.base.id1--;
}
line_dsc.base.id1--;
}
if(points) lv_free(points);
}
static void draw_series_scatter(lv_obj_t * obj, lv_layer_t * layer)
{
lv_chart_t * chart = (lv_chart_t *)obj;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 76 KiB