Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draw line got strange effect #6378

Closed
liyang5945 opened this issue Jun 16, 2024 · 18 comments
Closed

draw line got strange effect #6378

liyang5945 opened this issue Jun 16, 2024 · 18 comments
Labels

Comments

@liyang5945
Copy link

liyang5945 commented Jun 16, 2024

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);
}
@C47D
Copy link
Contributor

C47D commented Jun 16, 2024

Yep, it seems like a rounding error.

@kisvegabor
Copy link
Member

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

@liyang5945
Copy link
Author

您可以使用内置的 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.

@kisvegabor
Copy link
Member

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

@FASTSHIFT
Copy link
Collaborator

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

@kisvegabor
Copy link
Member

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?

@FASTSHIFT
Copy link
Collaborator

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

@kisvegabor
Copy link
Member

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

@FASTSHIFT
Copy link
Collaborator

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: thorvg/thorvg#1901

How did it work for you #6378 (comment)?

I have enabled LV_USE_DRAW_VG_LITE and LV_USE_VG_LITE_THORVG here, so the buffer will not be cleared.

@kisvegabor
Copy link
Member

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?

@FASTSHIFT
Copy link
Collaborator

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.

@kisvegabor
Copy link
Member

I've opened #6406

The vector demo is working too:
image

It's a pity that RGB565 is not supported.

@liyang5945
Copy link
Author

@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

@FASTSHIFT
Copy link
Collaborator

FASTSHIFT commented Jun 25, 2024

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.

@lvgl-bot
Copy link

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 lvgl-bot added the stale label Jul 11, 2024
@kisvegabor
Copy link
Member

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.

@lvgl-bot lvgl-bot removed the stale label Jul 29, 2024
@lvgl-bot
Copy link

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 lvgl-bot added the stale label Aug 15, 2024
@lvgl-bot
Copy link

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 lvgl-bot closed this as not planned Won't fix, can't repro, duplicate, stale Aug 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants