Color Format

The color format a display renders in: set the default with LV_COLOR_FORMAT_DEFAULT, or change it per display at runtime.

Edit on GitHub

Every display renders in one color format. A newly created display starts in LV_COLOR_FORMAT_DEFAULT (see lv_conf.h), which names the format you want explicitly rather than just its size:

LV_COLOR_FORMAT_DEFAULTSizeNotes
LV_COLOR_FORMAT_I11 bit/pixelMonochrome, horizontally mapped buffers only. See Monochrome Displays
LV_COLOR_FORMAT_L81 byte/pixelGray-scale
LV_COLOR_FORMAT_RGB5652 bytes/pixelLittle-endian
LV_COLOR_FORMAT_RGB565_SWAPPED2 bytes/pixelBig-endian. See Swapping Endian-ness
LV_COLOR_FORMAT_RGB8883 bytes/pixel
LV_COLOR_FORMAT_XRGB88884 bytes/pixelAlpha byte present but ignored
LV_COLOR_FORMAT_ARGB88884 bytes/pixelReal alpha channel. See Transparent Screens
LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED4 bytes/pixelAlpha channel, RGB pre-darkened by it. What compositors such as Wayland expect

The format belongs to the display, not to LVGL as a whole: two displays can render in different formats, and any display's format can be changed at any time with lv_display_set_color_format(display, LV_COLOR_FORMAT_...).

Changing it needs cooperation from the display driver:

  • its Flush Callback has to handle the pixel layout it is handed, and
  • the draw buffer(s) must already be large enough for the new format. lv_display_set_color_format re-labels the buffers the display already has, it does not resize them, so widening the format without providing bigger buffers will overrun them.

LV_COLOR_DEPTH still exists and is still readable from your own code, but it is now derived from LV_COLOR_FORMAT_DEFAULT rather than something you set. It is only a bit count, so it cannot tell the two RGB565 formats apart, nor the three 8888 ones. See the v10 migration guide.

Swapping Endian-ness

LVGL supports the LV_COLOR_FORMAT_RGB565_SWAPPED color format natively. By using this color format, no manual byte swapping is needed at all, simplifying display drivers and eliminating software overhead.

Note that this is not about swapping the Red and Blue channel but converting

RRRRR GGG | GGG BBBBB

to

GGG BBBBB | RRRRR GGG.

Monochrome Displays

LVGL supports rendering directly in a 1-bit format for monochrome displays. To enable it, set LV_COLOR_FORMAT_DEFAULT to LV_COLOR_FORMAT_I1 or use lv_display_set_color_format(display, LV_COLOR_FORMAT_I1).

The LV_COLOR_FORMAT_I1 format assumes that bytes are mapped to rows (i.e., the bits of a byte are written next to each other). The order of bits is MSB first, which means:

 
MSB           LSB

bits 7 6 5 4 3 2 1 0

are represented on the display as:

 
pixels     0 1 2 3 4 5 6 7
          Left         Right

Ensure that the LCD controller is configured accordingly.

Internally, LVGL rounds the redrawn areas to byte boundaries. Therefore, updated areas will:

  • start on an Nx8 coordinate, and
  • end on an Nx8 - 1 coordinate.

When setting up the buffers for rendering (lv_display_set_buffers), make the buffer 8 bytes larger. This is necessary because LVGL reserves 2 x 4 bytes in the buffer, as these are assumed to be used as a palette.

To skip the palette, include the following line in your Flush Callback function: px_map += 8.

As usual, monochrome displays support partial, full, and direct rendering modes as well. In full and direct modes, the buffer size should be large enough for the whole screen, meaning (horizontal_resolution x vertical_resolution / 8) + 8 bytes. As LVGL can not handle fractional width make sure to round the horizontal resolution to 8 bits (for example 90 to 96).

The lv_draw_i1_convert_to_vtiled function is used to convert a draw buffer in I1 color format from a row-wise (htiled) to a column-wise (vtiled) buffer layout. This conversion is necessary for certain display controllers that require a different draw buffer mapping. The function assumes that the buffer width and height are rounded to a multiple of 8. The bit order of the resulting vtiled buffer can be specified using the bit_order_lsb parameter.

For more details, refer to the implementation in lv_draw_i1_convert_to_vtiled.

To ensure that the redrawn areas start and end on byte boundaries, you can add a rounder callback to your display driver. This callback will round the width and height to the nearest multiple of 8.

Here is an example of how to implement and set a rounder callback:

 
static void my_rounder_cb(lv_event_t *e)
{
    lv_area_t *area = lv_event_get_param(e);

    /* Round the height to the nearest multiple of 8 */
    area->y1 = (area->y1 & ~0x7);
    area->y2 = (area->y2 | 0x7);
}

lv_display_add_event_cb(display, my_rounder_cb, LV_EVENT_INVALIDATE_AREA, display);

In this example, the my_rounder_cb function rounds the coordinates of the redrawn area to the nearest multiple of 8. The x1 and y1 coordinates are rounded down, while the x2 and y2 coordinates are rounded up. This ensures that the width and height of the redrawn area are always multiples of 8.

Transparent Screens

Usually, the opacity of the Screen is LV_OPA_COVER to provide a solid background for its children. If this is not the case (opacity < 100%) the display's bottom_layer will be visible. If the bottom layer's opacity is also not LV_OPA_COVER LVGL will have no solid background to draw.

This configuration (transparent Screen) could be useful to create, for example, on-screen display (OSD) menus where a video is played on a different hardware layer of the display panel, and a menu is overlaid on a higher layer.

To properly render a UI on a transparent Screen the Display's color format needs to be set to one with an alpha channel (for example LV_COLOR_FORMAT_ARGB8888).

In summary, to enable transparent screens and displays for OSD menu-like UIs:

Last updated on

On this page