Draw Layers

A layer is a buffer with a specified area where pixel rendering occurs. Each display has a "main" layer, but additional layers may be created internally during rendering to handle tasks such as Widget transformations.

Getting the Current Layer

The first parameter of the lv_draw_rect/label/etc functions is a layer.

In most of the cases a layer is not created, but an existing layer is used to draw there.

The draw API can be used in these cases and the current layer can be used differently in each case:

  1. In draw events: In LV_EVENT_DRAW_MAIN/POST_BEGIN/... events the Widget is being rendered to a layer of the display or another temporary layer created earlier during rendering. The current target layer can be retrieved using lv_event_get_layer(e).

    It also possible to create new layers in these events, but the previous layer is also required since it will be the parent layer in lv_draw_layer().

  2. Modifying the created Draw Tasks: In LV_EVENT_DRAW_TASK_ADDED the draw tasks created by lv_draw_rect/label/etc can be modified. It's not required to know the current layer to modify a draw task. However, if something new also needs to be drawn with lv_draw_rect/label/etc the current layer is also required.

    The current layer can be read from the base draw descriptor. For example:

    /* In LV_EVENT_DRAW_TASK_ADDED */
    lv_draw_task_t * t = lv_event_get_draw_task(e);
    lv_draw_base_dsc_t * draw_dsc = lv_draw_task_get_draw_dsc(t);
    
    lv_layer_t * current_layer = draw_dsc.layer;
    
  3. Draw to the Canvas Widget: The canvas itself doesn't store a layer, but one can be easily created and used like this:

    /* Initialize a layer */
    lv_layer_t layer;
    lv_canvas_init_layer(canvas, &layer);
    
    /* Draw something here */
    
    /* Wait until the rendering is ready */
    lv_canvas_finish_layer(canvas, &layer);
    

Creating a New Layer

To create a new layer, use lv_draw_layer_create():

lv_area_t layer_area = {10, 10, 80, 50}; /* Area of the new layer */
lv_layer_t * new_layer = lv_draw_layer_create(parent_layer, LV_COLOR_FORMAT_RGB565, &layer_area);

Once the layer is created, draw tasks can be added to it by using the Draw API and Draw descriptors. In most cases this means calling the lv_draw_rect/label/etc functions.

Finally, the layer must be rendered to its parent layer. Since a layer behaves similarly to an image, it can be rendered the same way as images:

lv_draw_image_dsc_t image_draw_dsc;
lv_draw_image_dsc_init(&image_draw_dsc);
image_draw_dsc.src = new_layer; /* Source image is the new layer. */
/* Draw new layer to parent layer. */
lv_draw_layer(parent_layer, &image_draw_dsc, &layer_area);

Memory Considerations

Layer Buffers

The buffer for a layer (where rendering occurs) is not allocated at creation. Instead, it is allocated by Draw Units when the first Draw Task is dispatched.

Layer buffers can be large, so ensure there is sufficient heap memory or increase LV_MEM_SIZE in lv_conf.h.

Layer Types

To save memory, LVGL can render certain types of layers in smaller chunks:

  1. Simple Layers: Simple layers can be rendered in chunks. For example, with opa_layered = 140, only 10 lines of the layer can be rendered at a time, then the next 10 lines, and so on. This avoids allocating a large buffer for the entire layer. The buffer size for a chunk is set using LV_DRAW_LAYER_SIMPLE_BUF_SIZE in lv_conf.h.

  2. Transformed Layers: Transformed Widgets cannot be rendered in chunks because transformations often affect pixels outside the given area. For such layers, LVGL allocates a buffer large enough to render the entire transformed area without limits.

Memory Limit for Layers

The total memory available for layers at once is controlled by LV_DRAW_LAYER_MAX_MEMORY in lv_conf.h. If set to 0, there is no limit.

API