lvgl
lvgl copied to clipboard
draw line got strange effect
LVGL version
v9.1.0
What happened?
I want to draw a Bezier curve. but got strange effect
if change this line
linePoints[i].y = newPoint.y;
to
linePoints[i].y= int(newPoint.y);
it seems better,I think the pixels are in the wrong place,for example the calculated Y value is 100.3 but draw on 101?
How to reproduce?
lv_point_precise_t quadraticBezier(lv_point_precise_t P0, lv_point_precise_t P1, lv_point_precise_t P2, double t) {
double u = 1 - t;
double x = u * u * P0.x + 2 * u * t * P1.x + t * t * P2.x;
double y = u * u * P0.y + 2 * u * t * P1.y + t * t * P2.y;
lv_point_precise_t result;
result.x = float(x);
result.y = float(y);
return result;
}
#define POINT_LEN 200
void drawBezierLine() {
lv_obj_t *cont = lv_obj_create(lv_screen_active());
lv_obj_set_size(cont,LV_HOR_RES,LV_VER_RES);
lv_obj_set_style_bg_color(cont,lv_color_black(),0);
lv_obj_set_style_bg_opa(cont,LV_OPA_20,0);
lv_point_precise_t P0 = {.x=0, .y=0}; // startPoint
lv_point_precise_t P1 = {.x=240, .y=200}; // endPoint
lv_point_precise_t P2 = {.x=480, .y=200}; // ctrlPoint
static lv_point_precise_t linePoints[POINT_LEN];
for (int i = 0; i < POINT_LEN; i++) {
double t = i / (double) POINT_LEN;
lv_point_precise_t newPoint = quadraticBezier(P0, P1, P2, t);
linePoints[i].x = newPoint.x;
linePoints[i].y = newPoint.y;
// linePoints[i].y= int(newPoint.y);
printf("point[%d]:(%.2f,%.2f)\n", i, newPoint.x, newPoint.y);
}
static lv_style_t styleTrackLine;
lv_style_init(&styleTrackLine);
lv_style_set_line_color(&styleTrackLine, lv_color_hex(0xff0000));
lv_style_set_line_width(&styleTrackLine, 2);
lv_style_set_line_opa(&styleTrackLine, LV_OPA_COVER);
lv_style_set_line_rounded(&styleTrackLine, true);
lv_obj_t *line = lv_line_create(cont);
lv_obj_remove_style_all(line);
lv_obj_add_style(line, &styleTrackLine, 0);
lv_line_set_points(line, &linePoints[0], POINT_LEN);
lv_obj_align(line, LV_ALIGN_TOP_LEFT, 0, 0);
}
Yep, it seems like a rounding error.
Can you use the built-in ThorVG library instead? Here and here are examples for drawing on a canvas.
I tried it and it worked fine on the simulator, but can't work on my embedded device because the embedded device doesn't have enough RAM to use the canvas api.
@FASTSHIFT I'm experimenting with using the vector API in an a draw event like this:
static void event_cb(lv_event_t *e)
{
lv_layer_t * layer = lv_event_get_layer(e);
lv_vector_dsc_t * dsc = lv_vector_dsc_create(layer);
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
lv_fpoint_t pts[] = {{10, 10}, {130, 130}, {10, 130}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_line_to(path, &pts[2]);
lv_vector_path_close(path);
lv_vector_dsc_set_fill_color(dsc, lv_color_make(0x00, 0x80, 0xff));
lv_vector_dsc_add_path(dsc, path);
lv_draw_vector(dsc);
lv_vector_path_delete(path);
lv_vector_dsc_delete(dsc);
}
...
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_center(obj);
lv_obj_add_event_cb(obj, event_cb, LV_EVENT_DRAW_MAIN, NULL);
LV_COLOR_DEPTH is 32.
However I got this:
Should it work with the code above?
@liyang5945 Drawing vectors directly without a canvas required ARGB8888 colors format for the display. Is it possible for you?
lv_obj_t * obj = lv_obj_create(lv_screen_active()); lv_obj_center(obj); lv_obj_add_event_cb(obj, event_cb, LV_EVENT_DRAW_MAIN, NULL);
Because the vector rendering API uses absolute coordinates relative to the screen, rather than coordinates relative to the component itself, you can use lv_vector_dsc_translate to move the drawing matrix as a whole so that it can follow the relative coordinates of the widget.
static void event_cb(lv_event_t *e)
{
lv_layer_t * layer = lv_event_get_layer(e);
lv_obj_t * obj = lv_event_get_current_target_obj(e);
lv_vector_dsc_t * dsc = lv_vector_dsc_create(layer);
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
lv_fpoint_t pts[] = {{10, 10}, {130, 130}, {10, 130}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_line_to(path, &pts[2]);
lv_vector_path_close(path);
lv_vector_dsc_translate(dsc, obj->coords.x1, obj->coords.y1);
lv_vector_dsc_set_fill_color(dsc, lv_color_make(0x00, 0x80, 0xff));
lv_vector_dsc_add_path(dsc, path);
lv_draw_vector(dsc);
lv_vector_path_delete(path);
lv_vector_dsc_delete(dsc);
}
By adding lv_vector_dsc_translate(dsc, obj->coords.x1, obj->coords.y1) it looks like this:
lv_example_canvas_8(); works well.
Is there any special setting that I need to add?
By adding
lv_vector_dsc_translate(dsc, obj->coords.x1, obj->coords.y1)it looks like this:
lv_example_canvas_8();works well.Is there any special setting that I need to add?
Is it written exactly according to my code? Can you send me lv_conf and the original code?
Yes, the code is copy pasted from your comment.
I've created a new lv_conf.h and the result is different. Now it's like yours but:
I thought that it's black because SDL uses XRGB8888 color format by default and ThorVG assumes that a memzero will result in transparent screen. Actually it can happen that:
- ThorVG clears the whole screen -> black
- Redraws the invalid areas -> blue is shown only under the cursor
Questions:
- Can we disable clearing the area?
- How did it work for you here?
Can we disable clearing the area?
Yes, we can consider directly deleting the buffer clearing operation of thorvg:
Because the latest thorvg also removes this code:
However, it should be noted that rasterClear is still retained in the release version of thorvg:
The specific reason can be seen in this discussion: https://github.com/thorvg/thorvg/issues/1901
How did it work for you https://github.com/lvgl/lvgl/issues/6378#issuecomment-2181908297?
I have enabled LV_USE_DRAW_VG_LITE and LV_USE_VG_LITE_THORVG here, so the buffer will not be cleared.
With
bool SwRenderer::preRender()
{
return true;
}
it works well indeed. Thank you!
Can we remove the #if LV_USE_DRAW_VG_LITE && LV_USE_VG_LITE_THORVG conditions and just return true?
Can we remove the
#if LV_USE_DRAW_VG_LITE && LV_USE_VG_LITE_THORVGconditions and justreturn true?
I think it's OK.
I've opened #6406
The vector demo is working too:
It's a pity that RGB565 is not supported.
@FASTSHIFT I'm experimenting with using the vector API in an a draw event like this:
static void event_cb(lv_event_t *e) { lv_layer_t * layer = lv_event_get_layer(e); lv_vector_dsc_t * dsc = lv_vector_dsc_create(layer); lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_fpoint_t pts[] = {{10, 10}, {130, 130}, {10, 130}}; lv_vector_path_move_to(path, &pts[0]); lv_vector_path_line_to(path, &pts[1]); lv_vector_path_line_to(path, &pts[2]); lv_vector_path_close(path); lv_vector_dsc_set_fill_color(dsc, lv_color_make(0x00, 0x80, 0xff)); lv_vector_dsc_add_path(dsc, path); lv_draw_vector(dsc); lv_vector_path_delete(path); lv_vector_dsc_delete(dsc); } ... lv_obj_t * obj = lv_obj_create(lv_screen_active()); lv_obj_center(obj); lv_obj_add_event_cb(obj, event_cb, LV_EVENT_DRAW_MAIN, NULL);
LV_COLOR_DEPTHis32.However I got this:
Should it work with the code above?
@liyang5945 Drawing vectors directly without a canvas required ARGB8888 colors format for the display. Is it possible for you?
I change color fomat to ARGB888 and use lastet master branch code, if use LV_DISPLAY_RENDER_MODE_PARTIAL mode, the output still got problem, just a single buffer size screen area render was right, (on simulator buffer size is half scren of 480*320 )same behaviour on embedded device, here is my code:
lv_point_precise_t quadraticBezier(lv_point_precise_t P0, lv_point_precise_t P1, lv_point_precise_t P2, double t) {
double u = 1 - t;
double x = u * u * P0.x + 2 * u * t * P1.x + t * t * P2.x;
double y = u * u * P0.y + 2 * u * t * P1.y + t * t * P2.y;
lv_point_precise_t result;
result.x = float(x);
result.y = float(y);
return result;
}
#define POINT_LEN 480
static lv_point_precise_t linePoints[POINT_LEN];
static void event_cb(lv_event_t *e)
{
lv_layer_t * layer = lv_event_get_layer(e);
lv_vector_dsc_t * dsc = lv_vector_dsc_create(layer);
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
lv_area_t rect = {0, 0, 480,320};
lv_vector_path_append_rect(path, &rect, 0, 0);
lv_vector_dsc_set_fill_color(dsc, lv_color_hex(0xFFFFFF));
lv_vector_dsc_set_fill_opa(dsc, LV_OPA_60);
lv_vector_clear_area(dsc, &rect); // clear screen
lv_vector_dsc_add_path(dsc, path); // draw a path
lv_vector_path_clear(path);
lv_vector_dsc_identity(dsc);
printf("LV_EVENT_DRAW_MAIN event_cb\n");
lv_vector_path_move_to(path, (lv_fpoint_t *)&linePoints[0]);
for (int i = 1; i < POINT_LEN-1; i++) {
lv_vector_path_line_to(path, (lv_fpoint_t *)&linePoints[i]);
}
lv_vector_dsc_set_stroke_color(dsc, lv_color_hex(0xff0000));
lv_vector_dsc_set_stroke_width(dsc, 3);
lv_vector_dsc_set_stroke_opa(dsc, LV_OPA_COVER);
lv_vector_dsc_set_fill_opa(dsc, LV_OPA_TRANSP);
lv_vector_dsc_add_path(dsc, path);
lv_draw_vector(dsc);
lv_vector_path_delete(path);
lv_vector_dsc_delete(dsc);
}
void drawBezierLine() {
lv_obj_t *cont = lv_obj_create(lv_screen_active());
lv_obj_set_size(cont,LV_HOR_RES,LV_VER_RES);
lv_obj_set_style_bg_color(cont,lv_color_white(),0);
lv_obj_set_style_bg_opa(cont,LV_OPA_COVER,0);
lv_point_precise_t P0 = {.x=0, .y=0}; // startPoint
lv_point_precise_t P1 = {.x=240, .y=320}; // endPoint
lv_point_precise_t P2 = {.x=480, .y=320}; // ctrlPoint
for (int i = 0; i < POINT_LEN; i++) {
double t = i / (double) POINT_LEN;
lv_point_precise_t newPoint = quadraticBezier(P0, P1, P2, t);
linePoints[i].x = newPoint.x;
linePoints[i].y = newPoint.y;
// linePoints[i].y= int(newPoint.y);
// printf("point[%d]:(%.2f,%.2f)\n", i, newPoint.x, newPoint.y);
}
lv_obj_add_event_cb(cont, event_cb, LV_EVENT_DRAW_MAIN, NULL);
}
output:
It should be that the draw vector is only adapted to the full-screen buffer mode. For PARTIAL mode, the global matrix in lv_vector_dsc_t needs to be moved as a whole according to layer->buf_area and the coordinates need to be converted, such as lv_vector_dsc_translate(ctx, -layer->buf_area.x1, -layer->buf_area.y1);.
What do you think? @onecoolx
@liyang5945
In addition, you can directly use lv_vector_path_quad_to and lv_vector_path_cubic_to to describe the Bezier curve without manually calculating the coordinates yourself.
We need some feedback on this issue.
Now we mark this as "stale" because there was no activity here for 14 days.
Remove the "stale" label or comment else this will be closed in 7 days.
It should be that the draw vector is only adapted to the full-screen buffer mode. For PARTIAL mode, the global matrix in lv_vector_dsc_t needs to be moved as a whole according to layer->buf_area and the coordinates need to be converted, such as lv_vector_dsc_translate(ctx, -layer->buf_area.x1, -layer->buf_area.y1);.
Is there a way to handle it automatically or in a more generic way? To avoid these kind of issues for other drawing functions (e.g. lv_draw_label) we pass all coordinates as absolute and the draw functions translate them as needed. Can we do this for vectors too?
See lv_line as a similar example.
We need some feedback on this issue.
Now we mark this as "stale" because there was no activity here for 14 days.
Remove the "stale" label or comment else this will be closed in 7 days.
As there was no activity here for a while we close this issue. But don't worry, the conversation is still here and you can get back to it at any time.
So feel free to comment if you have remarks or ideas on this topic.

