lvgl icon indicating copy to clipboard operation
lvgl copied to clipboard

draw line got strange effect

Open liyang5945 opened this issue 1 year ago • 14 comments

LVGL version

v9.1.0

What happened?

I want to draw a Bezier curve. but got strange effect

20240616_210017

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?

20240616_210038

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);
}

liyang5945 avatar Jun 16 '24 13:06 liyang5945

Yep, it seems like a rounding error.

C47D avatar Jun 16 '24 22:06 C47D

Can you use the built-in ThorVG library instead? Here and here are examples for drawing on a canvas.

kisvegabor avatar Jun 19 '24 14:06 kisvegabor

您可以使用内置的 ThorVG 库吗?这里这里是在画布上绘图的示例。

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.

liyang5945 avatar Jun 19 '24 15:06 liyang5945

@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: image

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?

kisvegabor avatar Jun 20 '24 22:06 kisvegabor

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);
}

image

FASTSHIFT avatar Jun 21 '24 03:06 FASTSHIFT

By adding lv_vector_dsc_translate(dsc, obj->coords.x1, obj->coords.y1) it looks like this:

image

lv_example_canvas_8(); works well.

Is there any special setting that I need to add?

kisvegabor avatar Jun 21 '24 06:06 kisvegabor

By adding lv_vector_dsc_translate(dsc, obj->coords.x1, obj->coords.y1) it looks like this:

image

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? image

FASTSHIFT avatar Jun 21 '24 07:06 FASTSHIFT

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: vector

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:

  1. ThorVG clears the whole screen -> black
  2. Redraws the invalid areas -> blue is shown only under the cursor

Questions:

  1. Can we disable clearing the area?
  2. How did it work for you here?

lv_conf.h.zip

kisvegabor avatar Jun 21 '24 07:06 kisvegabor

Can we disable clearing the area?

Yes, we can consider directly deleting the buffer clearing operation of thorvg: image

Because the latest thorvg also removes this code: image

However, it should be noted that rasterClear is still retained in the release version of thorvg: image

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.

FASTSHIFT avatar Jun 21 '24 08:06 FASTSHIFT

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?

kisvegabor avatar Jun 21 '24 08:06 kisvegabor

Can we remove the #if LV_USE_DRAW_VG_LITE && LV_USE_VG_LITE_THORVG conditions and just return true?

I think it's OK.

FASTSHIFT avatar Jun 21 '24 08:06 FASTSHIFT

I've opened #6406

The vector demo is working too: image

It's a pity that RGB565 is not supported.

kisvegabor avatar Jun 21 '24 11:06 kisvegabor

@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: image

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:

20240622_211304

liyang5945 avatar Jun 22 '24 13:06 liyang5945

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.

FASTSHIFT avatar Jun 25 '24 08:06 FASTSHIFT

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.

lvgl-bot avatar Jul 11 '24 01:07 lvgl-bot

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.

kisvegabor avatar Jul 11 '24 06:07 kisvegabor

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.

lvgl-bot avatar Aug 15 '24 01:08 lvgl-bot

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.

lvgl-bot avatar Aug 24 '24 01:08 lvgl-bot