Draw callback does not draw in an object that is otherwise completely empty. Why?

I am using draw callback, and it works, but there is a case when it does not:

lv_obj_t *bare = lv_obj_create(parent);
lv_obj_remove_style_all(bare);
lv_obj_set_size(bare, ...);
lv_obj_align(bare, ...);
lv_obj_add_event_cb(bare, event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
lv_obj_add_flag(bare, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

and elsewhere, call lv_obj_invalidate(bare); when necessary.

And the area stays empty.

But if I, for example, lv_obj_set_style_border_width(bare, 1, LV_PART_MAIN);, I get my drawing. Whereas with lv_obj_set_style_border_width(bare, 0, LV_PART_MAIN); I don’t.

Or if I create it as a label, and add some text, just one blank, I see the drawing. But without text, I don’t.

Why is it so?
Is there a cleaner way to make draw work in an empty object?

LVGL will only draw what is necessary, there will be no draw callback for a border for example if the object does not have a border. Border width of 0 amounts to no border, so none is drawn.

What are you trying to do? You can draw on an otherwise empty object if you use a canvas Canvas (lv_canvas) — LVGL documentation .

LVGL will only draw what is necessary,

I know that. And I (hope that) I informed the library that drawing is necessary, by registering a draw callback.

there will be no draw callback for a border for example if the object does not have a border. Border width of 0 amounts to no border, so none is drawn.

That’s right, and that’s my point. I don’t want the draw callback for the border. I don’t want the border. I only what things that my draw callback creates, and nothing else.

What are you trying to do?

I am trying to draw things in a given rectangular area on screen using draw primitives that are available for use in a draw callback.

You can draw on an otherwise empty object if you use a canvas

I prefer not to take care of allocating and freeing draw buffers myself. When I am in a drawing callback, draw primitives work without the need to create canvas. And I like it that way.

Like in this example.

Except, under some conditions they don’t work. And I want to understand those conditions.

Regards,

Eugene

Hmmm… You make a very valid use case here. I can see where you would want to have the callbacks made regardless of LVGL rendering or not.

@kisvegabor what do you think about this. I believe that the OP has a point. If a callback is registered then the callback should be called regardless of whether or not LVGL is going to render anything or not. I can see the use case for this where the OP might want to render some kind of a custom border and not want LVGL to render it’s own border but of you set the border width to 0 so LVGL doesn’t render one then the callback doesn’t get called and a custom one is not able to be rendered either.

It’s a very valid use case.

As a note:

I can understand not wanting to use the canvas because of having to manage the buffer that the canvas uses. where as rendering using one of the draw callbacks there is no need to manage the buffer like with the canvas. I believe the memory allocation is also going to be less to perform what is ultimately the same task.

I have a few questions regarding this.

Is the callback called before or after LVGL does it’s drawing/rendering?
If it is before LVGL does its drawing/rendering is there a way to stop LVGL from drawing/rendering for that specific event from inside of the callback?

From what I see on screen, I assume that custom callback is called after “native” draw functions. When I have a label object with text, and add my own draw callback to it, my drawing overwrites the text of the label.

Indeed my current workaround for the problem that I described here is to use label objects with the text of one whitespace character and no decorations. This way my draw callback is executed, and there is nothing else visible in the area. Seems hackish though…

Add an event callback for LV_EVENT_DRAW_MAIN to handle the primary draw portion.
Without this, the DRAW_TASK that hooks into the DRAW_MAIN section will not have any content to hook onto.

But then I am not getting draw_task, and would need to create it somehow myself, yes?

I wish all this were documented somewhere…

I told the head author of LVGL about this. I am sure he will chine in during the coming days. He doesn’t usually work on the project on the weekends.

Hey,

It’s really a valid point.

Transparent, zero sized, etc draw tasks should be discarded as early as possible, but the user really might rely on these.

Let’s see what’s happening:

  1. In case of label drawing, the draw tasks are discarded here
  2. If it’s not discarded the LV_EVENT_DRAW_TASK_ADDED is fired in lv_draw_finalize_task_creation here

I think a good compromise could be to not drop the draw tasks if LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS is enabled. I think it could work becasue

  • this flag is usually not enabled
  • the actual drawing routines will drop this empty/transparent draw tasks too

What do you think?

1 Like

For the bare object, if you want to draw a label,
one way is to draw on LV_EVENT_DRAW_MAIN.

And you can still receive LV_EVENT_DRAW_TASK_ADDED
to hook into the draw_task process or add additional drawings.

@kisvegabor

The way I believe it should work is the callback gets called and the user has the ability to “veto” any callbacks that would get called after it. In this case that would allow the user to decide whether or not they want the original widget rendering to happen.

The user can disable any draw tasks by setting e.g. opa=0.

That sounds reasonable to me (but I am no judge, I know too little about the inner working of lvgl)

I know it can be disabled but it also disable a users callback from being called.

So the widgets work by registering draw callbacks. Those callbacks are the first to get called and as a result there is no way to get in front of it if you want to render something custom. If you set the widgets color opacity to zero for a specific part all callbacks that are registered to that widget for that part don’t get called including the users. setting the opacity to zero from inside a user callback isn’t going to work because the rendering is already done because the widgets callbacks are what get called first. So there is no way to veto or stop the widget from rendering a part from a user callback for the same part,

I do not think that setting LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS is a viable option to use.

Here is the reason. If that is enabled and then creating draw tasks for every single widgets part even when there may not be a callback registered for that part or that part does have an opa of 0 but no user callback has been added is going to cause a lot of additional work. IDK if LVGL is written to check for the opactity level or the width in the actual draw functions so it knows to simply return because it would not be able to render anything. Or does it actually go through the steps to render something that isn’t going to be shown? That would be a lot of additional overhead.

A draw task should only be made IF and only IF a user callback has been added and the opacity or other properly that normally stops the callback from taking place has been set. If there is no user callback registered when say the opa is set to 0 then no draw task should be created.

Rendering functions should also return early if there is nothing to draw. So adding some extra draw tasks shouldn’t matter much.

Once a draw task is added and the event is called the user can

  • Modify the draw task. It can be transparent too and it will be dropped later. Maybe we can add an option to delete the draw tasks directly from the event.
  • Add a new draw task