# Color Format (/main-modules/display/color_format)



Every display renders in one color format. A newly created display starts in
<ApiLink name="LV_COLOR_FORMAT_DEFAULT" /> (see `lv_conf.h`), which names the format
you want explicitly rather than just its size:

| `LV_COLOR_FORMAT_DEFAULT`                                 | Size          | Notes                                                                                         |
| --------------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------- |
| <ApiLink name="LV_COLOR_FORMAT_I1" />                     | 1 bit/pixel   | Monochrome, horizontally mapped buffers only. See [Monochrome Displays](#monochrome-displays) |
| <ApiLink name="LV_COLOR_FORMAT_L8" />                     | 1 byte/pixel  | Gray-scale                                                                                    |
| <ApiLink name="LV_COLOR_FORMAT_RGB565" />                 | 2 bytes/pixel | Little-endian                                                                                 |
| <ApiLink name="LV_COLOR_FORMAT_RGB565_SWAPPED" />         | 2 bytes/pixel | Big-endian. See [Swapping Endian-ness](#swapping-endian-ness)                                 |
| <ApiLink name="LV_COLOR_FORMAT_RGB888" />                 | 3 bytes/pixel |                                                                                               |
| <ApiLink name="LV_COLOR_FORMAT_XRGB8888" />               | 4 bytes/pixel | Alpha byte present but ignored                                                                |
| <ApiLink name="LV_COLOR_FORMAT_ARGB8888" />               | 4 bytes/pixel | Real alpha channel. See [Transparent Screens](#transparent-screens)                           |
| <ApiLink name="LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED" /> | 4 bytes/pixel | Alpha 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
<ApiLink name="lv_display_set_color_format" display="lv_display_set_color_format(display, LV_COLOR_FORMAT_...)" />.

Changing it needs cooperation from the display driver:

* its [Flush Callback](/main-modules/display/setup) has to handle the pixel layout it
  is handed, and
* the draw buffer(s) must already be large enough for the new format.
  <ApiLink name="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.

<Callout type="info">
  `LV_COLOR_DEPTH` still exists and is still readable from your own code, but it is now
  **derived** from <ApiLink name="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](/changelog/migration-v10).
</Callout>

Swapping Endian-ness [#swapping-endian-ness]

LVGL supports the <ApiLink name="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 [#monochrome-displays]

LVGL supports rendering directly in a 1-bit format for monochrome displays.
To enable it, set `LV_COLOR_FORMAT_DEFAULT` to <ApiLink name="LV_COLOR_FORMAT_I1" /> or use
<ApiLink name="lv_display_set_color_format" display="lv_display_set_color_format(display, LV_COLOR_FORMAT_I1)" />.

The <ApiLink name="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:

```c title=" " lineNumbers=1
MSB           LSB
```

bits       7 6 5 4 3 2 1 0

are represented on the display as:

```c title=" " lineNumbers=1
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 (<ApiLink name="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](/main-modules/display/setup) 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 <ApiLink name="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 <ApiLink name="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:

```c title=" " lineNumbers=1
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 [#transparent-screens]

Usually, the opacity of the Screen is <ApiLink name="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 <ApiLink name="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:

* Set the screen's `bg_opa` to transparent:
  <ApiLink name="lv_obj_set_style_bg_opa" display="lv_obj_set_style_bg_opa(lv_screen_active(), LV_OPA_TRANSP, LV_PART_MAIN)" />
* Set the bottom layer's `bg_opa` to transparent:
  <ApiLink name="lv_obj_set_style_bg_opa" display="lv_obj_set_style_bg_opa(lv_layer_bottom(), LV_OPA_TRANSP, LV_PART_MAIN)" />
* Set a color format with alpha channel. E.g.
  <ApiLink name="lv_display_set_color_format" display="lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888)" />
