Draw Descriptors
Overview
Each Draw Task type has its own draw descriptor type. For
example, lv_draw_label_dsc_t
is used for label drawing,
lv_draw_image_dsc_t
is used for image drawing.
When an lv_draw_...
function is called, it creates a Draw Task, copies the draw
descriptor into a malloc
ed memory block, and frees it automatically when
needed. Therefore, local draw descriptor variables can be safely used.
Relation to Styles
In most cases, style properties map 1-to-1 to draw descriptor fields. For example:
label_dsc.color
corresponds to thetext_color
style property.shadow_dsc.width
,line_dsc.opa
, andarc_dsc.width
map toshadow_width
,line_opa
, andarc_width
in styles.
See Style Properties to see the list of style properties and what they mean.
Base Draw Descriptor
In each draw descriptor there is a generic "base descriptor" with
lv_draw_dsc_base_t
type and with base
in its name. For example
label_dsc.base
. This struct
stores useful information about which Widget
and part created the draw descriptor. See all the fields in
lv_draw_dsc_base_t
.
In an LV_EVENT_DRAW_TASK_ADDED
event, the elements of the base draw
descriptor are very useful to identify the Draw Task. 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);
draw_dsc.obj; /* The Widget for which the draw descriptor was created */
draw_dsc.part; /* The Widget part for which the draw descriptor was created
E.g. LV_PART_INDICATOR */
draw_dsc.id1; /* A Widget type specific ID (e.g. table row index).
See the docs of the given Widget. */
draw_dsc.id2;
draw_dsc.layer; /* The target layer.
Required when a new Draw Tasks are also created */
Simple Initilialzation
Before using a draw descriptor it needs to be initialized with the related function. For example, lv_draw_label_dsc_init(&my_label_draw_dsc).
After initialization, each field of the draw descriptor can be set. The default values are quite sane and reasonable, so usually only a few fields need modification. For example:
/* In LV_EVENT_DRAW_MAIN */
lv_draw_label_dsc_t my_label_draw_dsc;
lv_draw_label_dsc_init(&my_label_draw_dsc);
my_label_draw_dsc.font = &my_font;
my_label_draw_dsc.color = lv_color_hex(0xff0000);
my_label_draw_dsc.text = "Hello";
lv_area_t a = {10, 10, 200, 50}; /* Draw label here */
lv_draw_label(lv_event_get_layer(e), &my_label_draw_dsc, &a);
Initilialzation for Widgets
When rendering a part of a Widget, helper functions can initialize draw descriptors based on the styles, and a specific Widget part in the current state.
For example:
/* In LV_EVENT_DRAW_MAIN */
lv_draw_rect_dsc_t cur_dsc;
lv_draw_rect_dsc_init(&cur_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_CURSOR, &cur_dsc);
cur_dsc.fill_color = lv_color_hex(0xff0000); /* Modify if needed */
lv_draw_rect(layer, &cur_dsc, &area);
The lv_obj_init_draw_...
functions automatically initialize the fields of
the base descriptor.
Modify the draw descriptors
In LV_EVENT_DRAW_TASK_ADDED
, the draw descriptor of the draw_task
can be
accessed (using lv_draw_task_get_label_dsc()
and similar functions)
and modified (to change color, text, font, etc.). This means that in
LV_EVENT_DRAW_TASK_ADDED
, the draw_task
s and draw descriptors
are already initialized and it's enough to change only a few specific values.
For example:
/* In LV_EVENT_DRAW_TASK_ADDED */
lv_draw_task_t * t = lv_event_get_draw_task(e);
lv_draw_label_dsc_t * draw_dsc = lv_draw_task_get_label_dsc(t);
/* Check a few things in `draw_dsc->base` */
/* Make the color lighter for longer texts */
draw_dsc->color = lv_color_lighten(draw_dsc->color,
LV_MIN(lv_strlen(draw_dsc->text) * 5, 255));
/* Create new Draw Tasks if needed by calling
* `lv_draw_...(draw_dsc->base.layer, ...)` functions */
Rectangle Draw Descriptor
lv_draw_rect_dsc_t
is a helper descriptor that combines:
Fill
Border
Outline (a border with its own styles)
Shadow
Background image (an image with its own styles)
into a single call.
lv_obj_init_draw_rect_dsc(obj, part, &dsc);
initializes a draw descriptor
from a Widget, and lv_draw_rect(layer, &dsc, area)
draws the rectangle in a
specified area.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50
/**
* Draw a rectangle to the canvas
*/
void lv_example_canvas_3(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_rect_dsc_t dsc;
lv_draw_rect_dsc_init(&dsc);
dsc.bg_color = lv_palette_main(LV_PALETTE_RED);
dsc.border_color = lv_palette_main(LV_PALETTE_BLUE);
dsc.border_width = 3;
dsc.outline_color = lv_palette_main(LV_PALETTE_GREEN);
dsc.outline_width = 2;
dsc.outline_pad = 2;
dsc.outline_opa = LV_OPA_50;
dsc.radius = 5;
dsc.border_width = 3;
lv_area_t coords = {10, 10, 40, 30};
lv_draw_rect(&layer, &dsc, &coords);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
Fill Draw Descriptor
The main fields of lv_draw_fill_dsc_t
are straightforward. It has a
radius, opacity, and color to draw a rectangle. If opacity is 0, no draw task will
be created.
lv_draw_fill_dsc_init(&dsc) initializes a fill Draw Task.
lv_draw_sw_fill(layer, &dsc, area) creates a Draw Task to fill an area.
lv_draw_task_get_fill_dsc(draw_task) retrieves the fill descriptor from a Draw Task.
Gradients
The grad
field of the fill descriptor (or lv_grad_dsc_t
in general)
supports:
Horizontal
Vertical
Skew
Radial
Conical
gradient types.
The following show some example gradients.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
/**
* Using the background style properties
*/
void lv_example_style_2(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 5);
/*Make a gradient*/
lv_style_set_bg_opa(&style, LV_OPA_COVER);
static lv_grad_dsc_t grad;
grad.dir = LV_GRAD_DIR_VER;
grad.stops_count = 2;
grad.stops[0].color = lv_palette_lighten(LV_PALETTE_GREY, 1);
grad.stops[0].opa = LV_OPA_COVER;
grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);
grad.stops[1].opa = LV_OPA_COVER;
/*Shift the gradient to the bottom*/
grad.stops[0].frac = 128;
grad.stops[1].frac = 192;
lv_style_set_bg_grad(&style, &grad);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}
#endif
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
/**
* Simulate metallic knob using conical gradient
* For best effect set LV_GRADIENT_MAX_STOPS to 8 or at least 3
*/
void lv_example_style_16(void)
{
#if LV_GRADIENT_MAX_STOPS >= 8
static const lv_color_t grad_colors[8] = {
LV_COLOR_MAKE(0xe8, 0xe8, 0xe8),
LV_COLOR_MAKE(0xff, 0xff, 0xff),
LV_COLOR_MAKE(0xfa, 0xfa, 0xfa),
LV_COLOR_MAKE(0x79, 0x79, 0x79),
LV_COLOR_MAKE(0x48, 0x48, 0x48),
LV_COLOR_MAKE(0x4b, 0x4b, 0x4b),
LV_COLOR_MAKE(0x70, 0x70, 0x70),
LV_COLOR_MAKE(0xe8, 0xe8, 0xe8),
};
#elif LV_GRADIENT_MAX_STOPS >= 3
static const lv_color_t grad_colors[3] = {
LV_COLOR_MAKE(0xe8, 0xe8, 0xe8),
LV_COLOR_MAKE(0xff, 0xff, 0xff),
LV_COLOR_MAKE(0x79, 0x79, 0x79),
};
#else
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0xe8, 0xe8, 0xe8),
LV_COLOR_MAKE(0x79, 0x79, 0x79),
};
#endif
/*Create a style with gradient background and shadow*/
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 500);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_shadow_color(&style, lv_color_black());
lv_style_set_shadow_width(&style, 50);
lv_style_set_shadow_offset_x(&style, 20);
lv_style_set_shadow_offset_y(&style, 20);
lv_style_set_shadow_opa(&style, LV_OPA_50);
/*First define a color gradient. In this example we use a gray color map with random values.*/
static lv_grad_dsc_t grad;
lv_grad_init_stops(&grad, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
/*Make a conical gradient with the center in the middle of the object*/
#if LV_GRADIENT_MAX_STOPS >= 8
lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 0, 120, LV_GRAD_EXTEND_REFLECT);
#elif LV_GRADIENT_MAX_STOPS >= 3
lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 45, 125, LV_GRAD_EXTEND_REFLECT);
#else
lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 45, 110, LV_GRAD_EXTEND_REFLECT);
#endif
/*Set gradient as background*/
lv_style_set_bg_grad(&style, &grad);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, 200, 200);
lv_obj_center(obj);
}
#else
void lv_example_style_16(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_width(label, LV_PCT(80));
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS is not enabled");
lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_SCROLL_CIRCULAR);
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif /*LV_BUILD_EXAMPLES*/
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
/**
* Using radial gradient as background
*/
void lv_example_style_17(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0x9B, 0x18, 0x42),
LV_COLOR_MAKE(0x00, 0x00, 0x00),
};
int32_t width = lv_display_get_horizontal_resolution(NULL);
int32_t height = lv_display_get_vertical_resolution(NULL);
static lv_style_t style;
lv_style_init(&style);
/*First define a color gradient. In this example we use a purple to black color map.*/
static lv_grad_dsc_t grad;
lv_grad_init_stops(&grad, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
/*Make a radial gradient with the center in the middle of the object, extending to the farthest corner*/
lv_grad_radial_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, LV_GRAD_RIGHT, LV_GRAD_BOTTOM, LV_GRAD_EXTEND_PAD);
/*Set gradient as background*/
lv_style_set_bg_grad(&style, &grad);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, width, height);
lv_obj_center(obj);
}
#else
void lv_example_style_17(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_width(label, LV_PCT(80));
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS is not enabled");
lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_SCROLL_CIRCULAR);
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif /*LV_BUILD_EXAMPLES*/
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
/**
* Using various gradients for button background
*/
void lv_example_style_18(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0x26, 0xa0, 0xda),
LV_COLOR_MAKE(0x31, 0x47, 0x55),
};
/*Create a linear gradient going from the top left corner to the bottom at an angle, with reflected color map*/
static lv_style_t style_with_linear_gradient_bg;
static lv_grad_dsc_t linear_gradient_dsc; /*NOTE: the gradient descriptor must be static or global variable!*/
lv_style_init(&style_with_linear_gradient_bg);
lv_grad_init_stops(&linear_gradient_dsc, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
lv_grad_linear_init(&linear_gradient_dsc, lv_pct(0), lv_pct(0), lv_pct(20), lv_pct(100), LV_GRAD_EXTEND_REFLECT);
lv_style_set_bg_grad(&style_with_linear_gradient_bg, &linear_gradient_dsc);
lv_style_set_bg_opa(&style_with_linear_gradient_bg, LV_OPA_COVER);
/*Create a radial gradient with the center in the top left 1/3rd of the object, extending to the bottom right corner, with reflected color map*/
static lv_style_t style_with_radial_gradient_bg;
static lv_grad_dsc_t radial_gradient_dsc; /*NOTE: the gradient descriptor must be static or global variable!*/
lv_style_init(&style_with_radial_gradient_bg);
lv_grad_init_stops(&radial_gradient_dsc, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
lv_grad_radial_init(&radial_gradient_dsc, lv_pct(30), lv_pct(30), lv_pct(100), lv_pct(100), LV_GRAD_EXTEND_REFLECT);
lv_style_set_bg_grad(&style_with_radial_gradient_bg, &radial_gradient_dsc);
lv_style_set_bg_opa(&style_with_radial_gradient_bg, LV_OPA_COVER);
/*Create buttons with different gradient styles*/
lv_obj_t * btn;
lv_obj_t * label;
/*Simple horizontal gradient*/
btn = lv_button_create(lv_screen_active());
lv_obj_set_style_bg_color(btn, grad_colors[0], 0);
lv_obj_set_style_bg_grad_color(btn, grad_colors[1], 0);
lv_obj_set_style_bg_grad_dir(btn, LV_GRAD_DIR_HOR, 0);
lv_obj_set_size(btn, 150, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, -100);
label = lv_label_create(btn);
lv_label_set_text(label, "Horizontal");
lv_obj_center(label);
/*Simple vertical gradient*/
btn = lv_button_create(lv_screen_active());
lv_obj_set_style_bg_color(btn, grad_colors[0], 0);
lv_obj_set_style_bg_grad_color(btn, grad_colors[1], 0);
lv_obj_set_style_bg_grad_dir(btn, LV_GRAD_DIR_VER, 0);
lv_obj_set_size(btn, 150, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, -40);
label = lv_label_create(btn);
lv_label_set_text(label, "Vertical");
lv_obj_center(label);
/*Complex linear gradient*/
btn = lv_button_create(lv_screen_active());
lv_obj_add_style(btn, &style_with_linear_gradient_bg, 0);
lv_obj_set_size(btn, 150, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 20);
label = lv_label_create(btn);
lv_label_set_text(label, "Linear");
lv_obj_center(label);
/*Complex radial gradient*/
btn = lv_button_create(lv_screen_active());
lv_obj_add_style(btn, &style_with_radial_gradient_bg, 0);
lv_obj_set_size(btn, 150, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 80);
label = lv_label_create(btn);
lv_label_set_text(label, "Radial");
lv_obj_center(label);
}
#else
void lv_example_style_18(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_width(label, LV_PCT(80));
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS is not enabled");
lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_SCROLL_CIRCULAR);
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif /*LV_BUILD_EXAMPLES*/
For each gradient type, multiple color and opacity values can be assigned. These are
called "stops". The maximum number of stops is limited to
LV_GRADIENT_MAX_STOPS
.
A gradient is basically a transition of colors and opacities between stops.
Besides just setting the color and opacity of each stop, it is also possible to set where they start relative to the whole gradient area.
For example with 3 stops it can be set like this:
10% red: 0–10% fully red
60% green: 10–60% transition from red to green, 60% is fully green
65% blue: fast transition from green to blue between 60%–65%. After 65% fully blue.
The position of the stops are called fractions or offsets and are 8 bit values where 0 is 0% and 255 is 100% of the whole gradient area.
lv_grad_init_stops(grad_dsc, colors, opas, fracs, cnt) initializes a gradient descriptor with stops containing the color, opacity and fraction of each stop.
static const lv_color_t colors[2] = {
LV_COLOR_MAKE(0xe8, 0xe8, 0xe8),
LV_COLOR_MAKE(0x79, 0x79, 0x79),
};
static const lv_opa_t opas[2] = {
170,
255,
};
static const uint8_t fracs[2] = {
170,
255,
};
lv_grad_init_stops(&grad, colors, opas, fracs, sizeof(colors) / sizeof(lv_color_t));
If the opacity array is NULL
255 will be used for each stop. If the fractions
array is NULL
the colors will be distributed evenly. For example with 3 colors:
0%, 50%, 100%
Padding
Linear, radial, and conic gradients are defined between two points or angles. You can define how to pad the areas outside of the start and end points or angles:
LV_GRAD_EXTEND_PAD
: Repeat the same colorLV_GRAD_EXTEND_REPEAT
: Repeat the patternLV_GRAD_EXTEND_REFLECT
: Repeat the pattern normally and mirrored alternately
Horizontal and Vertical Gradients
The simplest and usually fastest gradient types are horizontal and vertical gradients.
After initializing the stops with lv_grad_init_stops call either lv_grad_horizontal_init(&grad_dsc) or lv_grad_vertical_init(&grad_dsc) to get a horizontal or vertical gradient descriptor.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL
static void position_bullet(lv_event_t * e, lv_point_t * p)
{
lv_indev_t * indev = lv_event_get_param(e);
lv_indev_get_point(indev, p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p->x -= lv_obj_get_x(parent);
p->y -= lv_obj_get_y(parent);
int32_t w = lv_obj_get_width(parent);
int32_t h = lv_obj_get_height(parent);
lv_obj_set_pos(bullet, LV_CLAMP(5, p->x, w - 20), LV_CLAMP(5, p->y, h - 20));
}
static void frac_1_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
if(lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v) != LV_STYLE_RES_FOUND) {
LV_LOG_WARN("style prop not found");
}
else {
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
dsc->stops[0].frac = (uint8_t)LV_CLAMP(0, p.x * 255 / lv_obj_get_width(parent), 255);
lv_obj_invalidate(parent);
}
}
static void frac_2_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
if(lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v) != LV_STYLE_RES_FOUND) {
LV_LOG_WARN("style prop not found");
}
else {
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
dsc->stops[1].frac = (uint8_t)LV_CLAMP(0, p.x * 255 / lv_obj_get_width(parent), 255);
lv_obj_invalidate(parent);
}
}
/**
* Play with a simple horizontal gradient.
* Adjust the stop positions of the gradient.
*/
void lv_example_grad_1(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0xff, 0x00, 0x00),
LV_COLOR_MAKE(0x00, 0xff, 0x00),
};
static const lv_opa_t grad_opa[2] = {
LV_OPA_100,
LV_OPA_0,
};
static const uint8_t frac[2] = {
20 * 255 / 100, /*20%*/
80 * 255 / 100, /*80%*/
};
static lv_style_t style;
lv_style_init(&style);
static lv_grad_dsc_t grad_dsc;
lv_grad_init_stops(&grad_dsc, grad_colors, grad_opa, frac, sizeof(grad_colors) / sizeof(lv_color_t));
lv_grad_horizontal_init(&grad_dsc);
/*Set gradient as background*/
lv_style_set_bg_grad(&style, &grad_dsc);
lv_style_set_border_width(&style, 2);
lv_style_set_pad_all(&style, 0);
lv_style_set_radius(&style, 12);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, lv_pct(80), lv_pct(80));
lv_obj_center(obj);
lv_obj_t * frac_1 = lv_button_create(obj);
lv_obj_set_size(frac_1, 15, 15);
lv_obj_set_style_bg_color(frac_1, lv_color_hex(0xff00ff), 0);
lv_obj_add_event_cb(frac_1, frac_1_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(frac_1, 5);
lv_obj_set_pos(frac_1, lv_pct(20), lv_pct(50));
lv_obj_t * frac_2 = lv_button_create(obj);
lv_obj_set_size(frac_2, 15, 15);
lv_obj_set_style_bg_color(frac_2, lv_color_hex(0xffff00), 0);
lv_obj_add_event_cb(frac_2, frac_2_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(frac_2, 5);
lv_obj_set_pos(frac_2, lv_pct(80), lv_pct(50));
}
#endif
Linear Gradients
The liniear (or skew) gradinet are similar to horizontal or vertical gradient but the angle of the gradient can be controlled.
The linear gradient will be rendered along a line defined by 2 points.
After initializing the stops with lv_grad_init_stops()
call
lv_grad_linear_init(&grad_dsc, from_x, from_y, to_x, to_y, LV_GRAD_EXTEND_...)
with your point values and extend pattern strategy to get a linear gradient descriptor.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
static void position_bullet(lv_event_t * e, lv_point_t * p)
{
lv_indev_t * indev = lv_event_get_param(e);
lv_indev_get_point(indev, p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p->x -= lv_obj_get_x(parent);
p->y -= lv_obj_get_y(parent);
int32_t w = lv_obj_get_width(parent);
int32_t h = lv_obj_get_height(parent);
lv_obj_set_pos(bullet, LV_CLAMP(5, p->x, w - 20), LV_CLAMP(5, p->y, h - 20));
}
static void start_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
dsc->params.linear.start.x = p.x;
dsc->params.linear.start.y = p.y;
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
lv_obj_invalidate(parent);
}
static void end_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
dsc->params.linear.end.x = p.x;
dsc->params.linear.end.y = p.y;
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
lv_obj_invalidate(parent);
}
/**
* Play with the linear gradient.
* Adjust the 2 point in between the a linear gradient can be drawn (can be skew as well)
*/
void lv_example_grad_2(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0xff, 0x00, 0x00),
LV_COLOR_MAKE(0x00, 0xff, 0x00),
};
static const lv_opa_t grad_opa[2] = {
LV_OPA_100,
LV_OPA_0,
};
static lv_style_t style;
lv_style_init(&style);
/*First define a color gradient. In this example we use a purple to black color map.*/
static lv_grad_dsc_t grad;
lv_grad_init_stops(&grad, grad_colors, grad_opa, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
lv_grad_linear_init(&grad, 100, 100, 200, 150, LV_GRAD_EXTEND_PAD);
/*Set gradient as background*/
lv_style_set_bg_grad(&style, &grad);
lv_style_set_border_width(&style, 2);
lv_style_set_pad_all(&style, 0);
lv_style_set_radius(&style, 12);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, lv_pct(80), lv_pct(80));
lv_obj_center(obj);
lv_obj_t * start = lv_button_create(obj);
lv_obj_set_size(start, 15, 15);
lv_obj_set_style_bg_color(start, lv_color_hex(0x0000ff), 0);
lv_obj_add_event_cb(start, start_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(start, 5);
lv_obj_set_pos(start, 100, 100);
lv_obj_t * end = lv_button_create(obj);
lv_obj_set_size(end, 15, 15);
lv_obj_set_style_bg_color(end, lv_color_hex(0x00ffff), 0);
lv_obj_add_event_cb(end, end_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(end, 5);
lv_obj_set_pos(end, 200, 150);
}
#else
void lv_example_grad_2(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS needs to be enabled");
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif
Radial Gradients
The radial gradient is described by two circles: an outer circle and an inner circle (also called the focal point). The gradient will be calculated between the focal point's circle and the edge of the outer circle.
If the center of the focal point and the center of the main circle are the same, the gradient will spread evenly in all directions. If the center points are not the same, the gradient will have an egg shape.
The focal point's circle should be inside the main circle.
After initializing the stops with lv_grad_init_stops()
, the outer
circle can be set by:
lv_grad_radial_init(&grad_dsc, center_x, center_y, edge_x, edge_y, LV_GRAD_EXTEND_...)
For both the center and edge coordinates, px
or lv_pct()
values can be used.
The inner circle (focal point) can be set with: lv_grad_radial_set_focal(&grad_dsc, center_x, center_y, radius)
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
static void position_bullet(lv_event_t * e, lv_point_t * p)
{
lv_indev_t * indev = lv_event_get_param(e);
lv_indev_get_point(indev, p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p->x -= lv_obj_get_x(parent);
p->y -= lv_obj_get_y(parent);
int32_t w = lv_obj_get_width(parent);
int32_t h = lv_obj_get_height(parent);
lv_obj_set_pos(bullet, LV_CLAMP(5, p->x, w - 20), LV_CLAMP(5, p->y, h - 20));
}
static void focal_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
dsc->params.radial.focal.x = p.x;
dsc->params.radial.focal.y = p.y;
dsc->params.radial.focal_extent.x = p.x + 10;
dsc->params.radial.focal_extent.y = p.y;
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
lv_obj_invalidate(parent);
}
static void end_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
dsc->params.radial.end.x = p.x;
dsc->params.radial.end.y = p.y;
dsc->params.radial.end_extent.x = p.x + 100;
dsc->params.radial.end_extent.y = p.y;
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
lv_obj_invalidate(parent);
}
/**
* Play with the radial gradient
* Adjust the end circle and focal point position.
* The radius of the end circle and an focal point are hardcoded in the example.
*/
void lv_example_grad_3(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0xff, 0x00, 0x00),
LV_COLOR_MAKE(0x00, 0xff, 0x00),
};
static const lv_opa_t grad_opa[2] = {
LV_OPA_100,
LV_OPA_0,
};
static lv_style_t style;
lv_style_init(&style);
/*First define a color gradient. In this example we use a purple to black color map.*/
static lv_grad_dsc_t grad;
lv_grad_init_stops(&grad, grad_colors, grad_opa, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
/*Init a radial gradient where the center is at 100;100
*and the edge of the circle is at 200;100.
*Try LV_GRAD_EXTEND_REFLECT and LV_GRAD_EXTEND_REPEAT too. */
lv_grad_radial_init(&grad, 100, 100, 200, 100, LV_GRAD_EXTEND_PAD);
/*The gradient will be calculated between the focal point's circle and the
*edge of the circle. If the center of the focal point and the
*center of the main circle is the same, the gradient will spread
*evenly in all directions. The focal point should be inside the
*main circle.*/
lv_grad_radial_set_focal(&grad, 50, 50, 10);
/*Set the widget containing the gradient*/
lv_style_set_bg_grad(&style, &grad);
lv_style_set_border_width(&style, 2);
lv_style_set_pad_all(&style, 0);
lv_style_set_radius(&style, 12);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, lv_pct(80), lv_pct(80));
lv_obj_center(obj);
lv_obj_t * focal = lv_button_create(obj);
lv_obj_set_size(focal, 15, 15);
lv_obj_set_style_bg_color(focal, lv_color_hex(0x0000ff), 0);
lv_obj_add_event_cb(focal, focal_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(focal, 5);
lv_obj_set_pos(focal, 50, 50);
lv_obj_t * end = lv_button_create(obj);
lv_obj_set_size(end, 15, 15);
lv_obj_set_style_bg_color(end, lv_color_hex(0x00ffff), 0);
lv_obj_add_event_cb(end, end_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(end, 5);
lv_obj_set_pos(end, 100, 100);
}
#else
void lv_example_grad_3(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS needs to be enabled");
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif
Conic Gradients
The conic gradient is defined between the angles of a circle, and colors are mapped to each angle.
After initializing the stops with lv_grad_init_stops()
, the conic gradient
can be set up with:
lv_grad_conical_init(&grad, center_x, center_y, angle_start, angle_end, LV_GRAD_EXTEND_...)
For both the center and edge coordinates, px
or lv_pct()
values can be used.
The zero angle is on the right-hand side, and 90 degrees is at the bottom.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
static void position_bullet(lv_event_t * e, lv_point_t * p)
{
lv_indev_t * indev = lv_event_get_param(e);
lv_indev_get_point(indev, p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p->x -= lv_obj_get_x(parent);
p->y -= lv_obj_get_y(parent);
int32_t w = lv_obj_get_width(parent);
int32_t h = lv_obj_get_height(parent);
lv_obj_set_pos(bullet, LV_CLAMP(5, p->x, w - 20), LV_CLAMP(5, p->y, h - 20));
}
static void start_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p.x -= lv_obj_get_width(parent) / 2;
p.y -= lv_obj_get_height(parent) / 2;
dsc->params.conical.start_angle = lv_atan2(p.y, p.x);
lv_obj_invalidate(parent);
}
static void end_event_cb(lv_event_t * e)
{
lv_style_t * style = lv_event_get_user_data(e);
lv_style_value_t v;
lv_style_get_prop(style, LV_STYLE_BG_GRAD, &v);
lv_grad_dsc_t * dsc = (lv_grad_dsc_t *)v.ptr;
lv_point_t p;
position_bullet(e, &p);
lv_obj_t * bullet = lv_event_get_target(e);
lv_obj_t * parent = lv_obj_get_parent(bullet);
p.x -= lv_obj_get_width(parent) / 2;
p.y -= lv_obj_get_height(parent) / 2;
dsc->params.conical.end_angle = lv_atan2(p.y, p.x);
lv_obj_invalidate(parent);
}
/**
* Play with the conical gradient
*/
void lv_example_grad_4(void)
{
static const lv_color_t grad_colors[2] = {
LV_COLOR_MAKE(0xff, 0x00, 0x00),
LV_COLOR_MAKE(0x00, 0xff, 0x00),
};
static const lv_opa_t grad_opa[2] = {
LV_OPA_100,
LV_OPA_0,
};
static lv_style_t style;
lv_style_init(&style);
/*First define a color gradient. In this example we use a purple to black color map.*/
static lv_grad_dsc_t grad;
lv_grad_init_stops(&grad, grad_colors, grad_opa, NULL, sizeof(grad_colors) / sizeof(lv_color_t));
lv_grad_conical_init(&grad, lv_pct(50), lv_pct(50), 0, 180, LV_GRAD_EXTEND_PAD);
/*Set gradient as background*/
lv_style_set_bg_grad(&style, &grad);
lv_style_set_border_width(&style, 2);
lv_style_set_pad_all(&style, 0);
lv_style_set_radius(&style, 12);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_set_size(obj, lv_pct(80), lv_pct(80));
lv_obj_center(obj);
lv_obj_t * start = lv_button_create(obj);
lv_obj_set_size(start, 15, 15);
lv_obj_set_style_bg_color(start, lv_color_hex(0x0000ff), 0);
lv_obj_add_event_cb(start, start_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(start, 5);
lv_obj_set_pos(start, lv_pct(80), lv_pct(50));
lv_obj_t * end = lv_button_create(obj);
lv_obj_set_size(end, 15, 15);
lv_obj_set_style_bg_color(end, lv_color_hex(0x00ffff), 0);
lv_obj_add_event_cb(end, end_event_cb, LV_EVENT_PRESSING, &style);
lv_obj_set_ext_click_area(end, 5);
lv_obj_set_pos(end, lv_pct(20), lv_pct(50));
}
#else
void lv_example_grad_4(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "LV_USE_DRAW_SW_COMPLEX_GRADIENTS needs to be enabled");
lv_obj_center(label);
}
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
#endif
Border Draw Descriptor
The lv_draw_border_dsc_t
border descriptor has radius, opacity,
width, color, and side fields. If the opacity or width is 0, no Draw Task will
be created.
side
can contain ORed values of lv_border_side_t
, such as
LV_BORDER_SIDE_BOTTOM
. LV_BORDER_SIDE_ALL
applies to all sides, while LV_BORDER_SIDE_INTERNAL
is used by
higher layers (e.g. a table Widget) to calculate border sides. However, the drawing
routine receives only simpler values.
The following functions are used for border drawing:
lv_draw_border_dsc_init(&dsc) initializes a border Draw Task.
lv_draw_sw_border(layer, &dsc, area) creates a Draw Task to draw a border inward from its area.
lv_draw_task_get_border_dsc(draw_task) retrieves the border descriptor from a Draw Task.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
/**
* Using the border style properties
*/
void lv_example_style_3(void)
{
static lv_style_t style;
lv_style_init(&style);
/*Set a background color and a radius*/
lv_style_set_radius(&style, 10);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));
/*Add border to the bottom+right*/
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_width(&style, 5);
lv_style_set_border_opa(&style, LV_OPA_50);
lv_style_set_border_side(&style, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}
#endif
Outlines
The outline is similar to the border but is drawn outside the object's draw area.
In practice, there is no dedicated outline descriptor like
lv_draw_outline_dsc_t
, because from the rendering perspective, the
outline is simply another border rendered outside the object's bounds.
The outline is used only in lv_draw_rect_dsc_t
for convenience. The two
differences compared to borders in lv_draw_rect_dsc_t
are:
There is an
outline_pad
property to specify the gap between the target area and the inner side of the outline. It can be negative. For example, ifoutline_pad = -width
, the outline will resemble a border.There is no
border_side
property for the outline. It's always rendered as a full rectangle.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
/**
* Using the outline style properties
*/
void lv_example_style_4(void)
{
static lv_style_t style;
lv_style_init(&style);
/*Set a background color and a radius*/
lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));
/*Add outline*/
lv_style_set_outline_width(&style, 2);
lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_outline_pad(&style, 8);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}
#endif
Box Shadow Draw Descriptor
The lv_draw_box_shadow_dsc_t
box shadow descriptor describes a rounded
rectangle-shaped shadow. It cannot generate shadows for arbitrary shapes, text, or
images. It includes the following fields:
- radius:
Radius, LV_RADIUS_CIRCLE.
- color:
Shadow color.
- width:
Shadow width (blur radius).
- spread:
Expands the rectangle in all directions; can be negative.
- ofs_x:
Horizontal offset.
- ofs_y:
Vertical offset.
- opa:
Opacity (0–255 range). Values like
LV_OPA_TRANSP
,LV_OPA_10
, etc., can also be used.- bg_cover:
Set to 1 if the background will cover the shadow (a hint for the renderer to skip masking).
Note: Rendering large shadows may be slow or memory-intensive.
The following functions are used for box shadow drawing:
lv_draw_box_shadow_dsc_init(&dsc) initializes a box shadow Draw Task.
lv_draw_sw_box_shadow(layer, &dsc, area) creates a Draw Task for a rectangle's shadow. The shadow's size and position depend on the width, spread, and offset.
lv_draw_task_get_box_shadow_dsc(draw_task) retrieves the box shadow descriptor from a Draw Task.
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
/**
* Using the Shadow style properties
*/
void lv_example_style_5(void)
{
static lv_style_t style;
lv_style_init(&style);
/*Set a background color and a radius*/
lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));
/*Add a shadow*/
lv_style_set_shadow_width(&style, 55);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_BLUE));
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}
#endif
Image Draw Descriptor
The lv_draw_image_dsc_t
image descriptor defines the parameters for
image drawing. It is a complex descriptor with the following options:
- src:
The image source, either a pointer to lv_image_dsc_t or a file path.
- opa:
Opacity in the 0–255 range. Options like
LV_OPA_TRANSP
,LV_OPA_10
, etc., can also be used.- clip_radius:
Clips the corners of the image with this radius. Use LV_RADIUS_CIRCLE for the maximum radius.
- rotation:
Image rotation in 0.1-degree units (e.g., 234 means 23.4°).
- scale_x:
Horizontal scaling (zoom) of the image. 256 (LV_SCALE_NONE) means no zoom, 512 doubles the size, and 128 halves it.
- scale_y:
Same as
scale_x
but for vertical scaling.- skew_x:
Horizontal skew (parallelogram-like transformation) in 0.1-degree units (e.g., 456 means 45.6°).
- skew_y:
Vertical skew, similar to
skew_x
.- pivot:
The pivot point for transformations (scaling and rotation). (0,0) is the top-left corner of the image and can be set outside the image.
- bitmap_mask_src:
Pointer to an A8 or L8 image descriptor used to mask the image. The mask is always center-aligned.
- recolor:
Mixes this color with the image. For
LV_COLOR_FORMAT_A8
, this will be the visible pixels' color.- recolor_opa:
Intensity of recoloring (0 means no recoloring, 255 means full cover).
- blend_mode:
Defines how to blend image pixels with the background. See
lv_blend_mode_t
for more details.- antialias:
Set to 1 to enable anti-aliasing for transformations.
- tile:
Tiles the image (repeats it both horizontally and vertically) if the image is smaller than the image_area field in lv_draw_image_dsc_t.
- image_area:
Indicates the original, non-clipped area where the image is drawn. This is essential for:
Layer rendering, where only part of a layer may be rendered and
clip_radius
needs the original image dimensions.Tiling, where the draw area is larger than the image.
- sup:
Internal field to store information about the palette or color of A8 images.
Functions for image drawing:
lv_draw_image_dsc_init(&dsc) initializes an image draw descriptor.
lv_draw_image(layer, &dsc, area) creates a task to draw an image in a given area.
lv_draw_task_get_image_dsc(draw_task) retrieves the image descriptor from a task.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50
/**
* Draw an image to the canvas
*/
void lv_example_canvas_6(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE(CANVAS_WIDTH, CANVAS_HEIGHT, 32, LV_DRAW_BUF_STRIDE_ALIGN)];
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
LV_IMAGE_DECLARE(img_star);
lv_draw_image_dsc_t dsc;
lv_draw_image_dsc_init(&dsc);
dsc.src = &img_star;
lv_area_t coords = {10, 10, 10 + img_star.header.w - 1, 10 + img_star.header.h - 1};
lv_draw_image(&layer, &dsc, &coords);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE
/**
* Using the Image style properties
*/
void lv_example_style_6(void)
{
static lv_style_t style;
lv_style_init(&style);
/*Set a background color and a radius*/
lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 3));
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_image_recolor(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_image_recolor_opa(&style, LV_OPA_50);
lv_style_set_transform_rotation(&style, 300);
/*Create an object with the new style*/
lv_obj_t * obj = lv_image_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
LV_IMAGE_DECLARE(img_cogwheel_argb);
lv_image_set_src(obj, &img_cogwheel_argb);
lv_obj_center(obj);
}
#endif
Layers - Special Images
Layers are treated as images, so an lv_draw_image_dsc_t
can describe
how layers are blended into their parent layers. All image features apply to
layers as well.
lv_draw_layer(layer, &dsc, area)
initializes the blending of a layer back to
its parent layer. Additionally, image-drawing-related functions can be used for
layers.
For more details, see Layers.
Label Draw Descriptor
The lv_draw_label_dsc_t
label descriptor provides extensive options
for controlling text rendering:
- text:
The text to render.
- font:
Font to use, with support for fallback fonts.
- color:
Text color.
- opa:
Text opacity.
- line_space:
Additional space between lines.
- letter_space:
Additional space between characters.
- ofs_x:
Horizontal text offset.
- ofs_y:
Vertical text offset.
- sel_start:
Index of the first character for selection (not byte index).
LV_DRAW_LABEL_NO_TXT_SEL
means no selection.- sel_end:
Index of the last character for selection.
- sel_color:
Color of selected characters.
- sel_bg_color:
Background color for selected characters.
- align:
Text alignment. See
lv_text_align_t
.- bidi_dir:
Base direction for right-to-left text rendering (e.g., Arabic). See
lv_base_dir_t
.- decor:
Text decoration, e.g., underline. See
lv_text_decor_t
.- flag:
Flags for text rendering. See
lv_text_flag_t
.- text_length:
Number of characters to render (0 means render until 0).
- text_local:
Set to 1 to allocate a buffer and copy the text.
- text_static:
Indicates
text
is constant and its pointer can be cached.- hint:
Pointer to externally stored data to speed up rendering. See
lv_draw_label_hint_t
.
Functions for text drawing:
lv_draw_label_dsc_init(&dsc) initializes a label draw descriptor.
lv_draw_label(layer, &dsc, area) creates a task to render text in an area.
lv_draw_character(layer, &dsc, point, unicode_letter) creates a task to draw a character at a specific point.
lv_draw_task_get_label_dsc(draw_task) retrieves the label descriptor from a task.
For character-specific drawing in draw units, use
lv_draw_label_iterate_characters(draw_unit, draw_dsc, area, callback).
This iterates through all characters, calculates their positions, and calls the
callback for rendering each character. For callback details, see
lv_draw_glyph_cb_t
.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_FONT_MONTSERRAT_18 && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50
/**
* Draw a text to the canvas
*/
void lv_example_canvas_4(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_label_dsc_t dsc;
lv_draw_label_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.font = &lv_font_montserrat_18;
dsc.decor = LV_TEXT_DECOR_UNDERLINE;
dsc.text = "Hello";
lv_area_t coords = {10, 10, 30, 60};
lv_draw_label(&layer, &dsc, &coords);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL
/**
* Using the text style properties
*/
void lv_example_style_8(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 5);
lv_style_set_bg_opa(&style, LV_OPA_COVER);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 2));
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_pad_all(&style, 10);
lv_style_set_text_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_text_letter_space(&style, 5);
lv_style_set_text_line_space(&style, 20);
lv_style_set_text_decor(&style, LV_TEXT_DECOR_UNDERLINE);
/*Create an object with the new style*/
lv_obj_t * obj = lv_label_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_label_set_text(obj, "Text of\n"
"a label");
lv_obj_center(obj);
}
#endif
Arc Draw Descriptor
The lv_draw_arc_dsc_t
arc descriptor defines arc rendering with
these fields:
- color:
Arc color.
- img_src:
Image source for the arc, or NULL if unused.
- width:
Arc thickness.
- start_angle:
Starting angle in degrees (e.g., 0° is 3 o'clock, 90° is 6 o'clock).
- end_angle:
Ending angle.
- center:
Arc center point.
- radius:
Arc radius.
- opa:
Arc opacity (0–255).
- rounded:
Rounds the arc ends.
Functions for arc drawing:
lv_draw_arc_dsc_init(&dsc) initializes an arc descriptor.
lv_draw_arc(layer, &dsc) creates a task to render an arc.
lv_draw_task_get_arc_dsc(draw_task) retrieves arc descriptor from task.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50
/**
* Draw an arc to the canvas
*/
void lv_example_canvas_5(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_arc_dsc_t dsc;
lv_draw_arc_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.width = 5;
dsc.center.x = 25;
dsc.center.y = 25;
dsc.width = 10;
dsc.radius = 15;
dsc.start_angle = 0;
dsc.end_angle = 220;
lv_draw_arc(&layer, &dsc);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_ARC
/**
* Using the Arc style properties
*/
void lv_example_style_7(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_arc_color(&style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_arc_width(&style, 4);
/*Create an object with the new style*/
lv_obj_t * obj = lv_arc_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
}
#endif
Line Draw Descriptor
The lv_draw_line_dsc_t
line descriptor defines line rendering with
these fields:
- p1:
First point of line (supports floating-point coordinates).
- p2:
Second point of line (supports floating-point coordinates).
- color:
Line color.
- width:
Line thickness.
- opa:
Line opacity (0–255).
- dash_width:
Length of dashes (0 means no dashes).
- dash_gap:
Length of gaps between dashes (0 means no dashes).
- round_start:
Rounds the line start.
- round_end:
Rounds the line end.
- raw_end:
Set to 1 to skip end calculations if they are unnecessary.
Functions for line drawing:
lv_draw_line_dsc_init(&dsc) initializes a line descriptor.
lv_draw_line(layer, &dsc) creates a task to draw a line.
lv_draw_task_get_line_dsc(draw_task) retrieves line descriptor.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS&& LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 50
#define CANVAS_HEIGHT 50
/**
* Draw a line to the canvas
*/
void lv_example_canvas_7(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_line_dsc_t dsc;
lv_draw_line_dsc_init(&dsc);
dsc.color = lv_palette_main(LV_PALETTE_RED);
dsc.width = 4;
dsc.round_end = 1;
dsc.round_start = 1;
dsc.p1.x = 15;
dsc.p1.y = 15;
dsc.p2.x = 35;
dsc.p2.y = 10;
lv_draw_line(&layer, &dsc);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LINE
/**
* Using the line style properties
*/
void lv_example_style_9(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_line_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_line_width(&style, 6);
lv_style_set_line_rounded(&style, true);
/*Create an object with the new style*/
lv_obj_t * obj = lv_line_create(lv_screen_active());
lv_obj_add_style(obj, &style, 0);
static lv_point_precise_t p[] = {{10, 30}, {30, 50}, {100, 0}};
lv_line_set_points(obj, p, 3);
lv_obj_center(obj);
}
#endif
Triangle Draw Descriptor
Triangles are defined by lv_draw_triangle_dsc_t
, which includes:
- p[3]:
3 points for the triangle's vertices.
- color:
Triangle color.
- opa:
Triangle opacity.
- grad:
Gradient options. If
grad.dir
is notLV_GRAD_DIR_NONE
, thecolor
field is ignored. Theopa
field adjusts overall opacity.
Functions for triangle drawing: - lv_draw_triangle_dsc_init(&dsc) initializes a triangle descriptor. - lv_draw_triangle(layer, &dsc) creates a task to draw a triangle. - lv_draw_task_get_triangle_dsc(draw_task) retrieves triangle descriptor.
C code
View on GitHub#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 150
#define CANVAS_HEIGHT 150
/**
* Draw a triangle to the canvas
*/
void lv_example_canvas_9(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_triangle_dsc_t tri_dsc;
lv_draw_triangle_dsc_init(&tri_dsc);
tri_dsc.p[0].x = 10;
tri_dsc.p[0].y = 10;
tri_dsc.p[1].x = 100;
tri_dsc.p[1].y = 30;
tri_dsc.p[2].x = 50;
tri_dsc.p[2].y = 100;
tri_dsc.grad.stops_count = 2;
tri_dsc.grad.dir = LV_GRAD_DIR_VER;
tri_dsc.grad.stops[0].color = lv_color_hex(0xff0000);
tri_dsc.grad.stops[0].frac = 64; /*Start at 25%*/
tri_dsc.grad.stops[0].opa = LV_OPA_COVER;
tri_dsc.grad.stops[1].color = lv_color_hex(0x0000ff);
tri_dsc.grad.stops[1].opa = LV_OPA_TRANSP;
tri_dsc.grad.stops[1].frac = 3 * 64; /*End at 75%*/
tri_dsc.opa = 128; /*Set the overall opacity to 50%*/
lv_draw_triangle(&layer, &tri_dsc);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
Vector Draw Descriptor
TODO
Masking Operation
There are several options to mask parts of a layer, Widget, or drawing:
Radius of Rectangles: Set the radius style property or the
radius
in the draw descriptors. This creates rounded rectangles, borders, outlines, etc.. However, the content of subsequent renderings will not be masked out in the corners.Clip Radius of Images: Similar to rectangles, images can also be rendered with a
radius
. Since layer drawing and image drawing are handled the same way, this works for layers as well.You can draw various content on a layer and then render the layer with a
clip_radius
, masking out all the content on the corners.Rectangle Mask Draw Task: A special Draw Task can mask out a rectangle from a layer by setting the alpha channel of certain pixels to 0. To achieve this:
Create an
lv_draw_mask_rect_dsc_t
descriptor.Set
area
,radius
, andkeep_outside
parameters. Ifkeep_outside
is set to 1, areas outside ofarea
remain unchanged. Otherwise, they are cleared.Call lv_draw_mask_rect(layer, &dsc).
Note: The layer must have a color format with an alpha channel, typically LV_COLOR_FORMAT_ARGB8888.
In most cases, the "Clip Radius of Images" method is better because it blends the layer with a radius mask on the fly, avoiding a dedicated masking step. However, the "Rectangle Mask Draw Task" is useful when multiple areas need clearing or when the area to be masked differs from the layer area.
Clip Corner Style Property: Enabling
..._style_clip_corner
in a local or global style allows LVGL to create a layer for the top and bottom corner areas of a Widget. It renders the children there and blends it by settingclip_radius
to the layer.Bitmap Masking for Images: Using
..._style_bitmap_mask
orbitmap_mask
inlv_draw_image_dsc_t
allows setting an A8 or L8 image as a mask for an image/layer during blending.Limitation: The mask always aligns to the center, and only one bitmap mask can be used for an image/layer.
When
..._style_bitmap_mask
is used, LVGL automatically creates a layer, renders the Widgets there, and applies the bitmap mask during blending.Alternatively, the
bitmap_mask
property in the draw descriptor can be used directly for image drawing.
By using the Canvas Widget with an
LV_COLOR_FORMAT_L8
buffer, bitmap masks can be rendered dynamically.