diff --git a/docs/src/details/integration/bindings/api_json.rst b/docs/src/details/integration/bindings/api_json.rst index 690de315df..1096daa047 100644 --- a/docs/src/details/integration/bindings/api_json.rst +++ b/docs/src/details/integration/bindings/api_json.rst @@ -409,7 +409,7 @@ Here is a shortened example of what the output looks like. ], "structures":[ { - "name":"_lv_gradient_cache_t", + "name":"_lv_grad_cache_t", "type":{ "name":"struct", "json_type":"primitive_type" diff --git a/docs/src/details/main-modules/draw/draw_api.rst b/docs/src/details/main-modules/draw/draw_api.rst new file mode 100644 index 0000000000..628d7ce7cf --- /dev/null +++ b/docs/src/details/main-modules/draw/draw_api.rst @@ -0,0 +1,90 @@ +.. _draw_api: + +======== +Draw API +======== + +Where to Use the Drawing API +**************************** + +In most cases you use LVGL's Drawing API through the API of Widgets: by creating +buttons, labels, etc., and setting the their styles, positions, and other properties. +In these cases rendering (drawing) is handled internally and you doen't see the +:ref:`Drawing Pipeline ` at all. + +However there are three places where you can use LVGL's Drawing API directly. + +1. **In the draw events of the Widgets**: + There are event codes which are sent when the Widget needs to render itself: + + - :cpp:enumerator:`LV_EVENT_DRAW_MAIN_BEGIN`, :cpp:enumerator:`LV_EVENT_DRAW_MAIN`, + :cpp:enumerator:`LV_EVENT_DRAW_MAIN_END`: + Triggered before, during, and after a Widget is drawn, respectively. Widget + rendering typically occurs in :cpp:enumerator:`LV_EVENT_DRAW_MAIN`. + - :cpp:enumerator:`LV_EVENT_DRAW_POST_BEGIN`, :cpp:enumerator:`LV_EVENT_DRAW_POST`, + :cpp:enumerator:`LV_EVENT_DRAW_POST_END`: + Triggered before, during, and after all child Widgets are rendered, respectively. + This can be useful for overlay-like drawings, such as scrollbars which should be + rendered on top of any children. + + These are relevant if a new Widget is implemented and it uses custom drawing. + +2. **Modifying the created draw tasks**: + The when a draw task is created for a Widget :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED` + is sent. In this event the created draw task can be modified or new draw tasks + can be added. Typical use cases for this are modifying each bar of a bar chart, + or cells of a table. + + For performance reasons, this event is disabled by default. Enable it by setting + the :cpp:enumerator:`LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS` flag on the Widget(s) you + wish to emit this event. + +3. **Draw to the Canvas Widget**: + The drawing functions can be used directly to draw to a Canvas Widget. Doing so + renders custom drawing to a buffer which can be used later as an image or a mask. + + For more information see :ref:`lv_canvas`. + + + +Drawing API +*********** + +The main components of LVGL's Drawing API are the :cpp:func:`lv_draw_rect`, +:cpp:func:`lv_draw_label`, :cpp:func:`lv_draw_image`, and similar functions. +When they are called :cpp:type:`lv_draw_task_t` objects are created internally. + +These functions have the following parameters: + +- **Layer**: This is the target of the drawing. See details at :ref:`draw_layers`. +- **Draw Descriptor**: This is a large ``struct`` containing all the information + about the drawing. See details of the draw descriptors at :ref:`draw_descriptors`. +- **Area** (in some cases): Specifies where to draw. + + + +Coordinate System +***************** + +Some functions and draw descriptors require area or point parameters. These are +always **absolute coordinates** on the display. For example, if the target layer is +on a 800x480 display and the layer's area is (100,100) (200,200), to render a 10x10 +object in the middle, the coordinates (145,145) (154,154) should be used +(not (40,40) (49,49)). + +Exception: for the Canvas Widget the layer is always assumed to be at the (0,0) +coordinate, regardless of the Canvas Widget's position. + + + +API +*** + +.. API equals: + LV_EVENT_DRAW_MAIN_BEGIN + lv_draw_arc + lv_draw_image + lv_draw_label + lv_draw_line + lv_draw_mask_rect + lv_draw_triangle diff --git a/docs/src/details/main-modules/draw/draw_descriptors.rst b/docs/src/details/main-modules/draw/draw_descriptors.rst new file mode 100644 index 0000000000..af14d7ad90 --- /dev/null +++ b/docs/src/details/main-modules/draw/draw_descriptors.rst @@ -0,0 +1,705 @@ +.. _draw_descriptors: + +================ +Draw Descriptors +================ + +Overview +******** + +Each :ref:`Draw Task ` type has its own draw descriptor type. For +example, :cpp:type:`lv_draw_label_dsc_t` is used for label drawing, +:cpp:type:`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 the ``text_color`` style property. +- ``shadow_dsc.width``, ``line_dsc.opa``, and ``arc_dsc.width`` map to + ``shadow_width``, ``line_opa``, and ``arc_width`` in styles. + +See :ref:`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 +:cpp:type:`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 +:cpp:type:`lv_draw_dsc_base_t`. + +In an :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED` event, the elements of the base draw +descriptor are very useful to identify the Draw Task. For example: + +.. code-block:: c + + /* 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, :cpp:expr:`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: + +.. code-block:: c + + /* 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: + +.. code-block:: c + + /* 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 :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED`, the draw descriptor of the ``draw_task`` can be +accessed (using :cpp:type:`lv_draw_task_get_label_dsc()` and similar functions) +and modified (to change color, text, font, etc.). This means that in +:cpp:enumerator:`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: + +.. code-block:: c + + /* 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 +************************* + +:cpp:type:`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. + +.. lv_example:: widgets/canvas/lv_example_canvas_3 + :language: c + + + +Fill Draw Descriptor +******************** + +The main fields of :cpp:type:`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. + +- :cpp:expr:`lv_draw_fill_dsc_init(&dsc)` initializes a fill Draw Task. +- :cpp:expr:`lv_draw_sw_fill(layer, &dsc, area)` creates a Draw Task to fill an area. +- :cpp:expr:`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 :cpp:type:`lv_grad_dsc_t` in general) +supports: + +- Horizontal +- Vertical +- Skew +- Radial +- Conical + +gradient types. + +The following show some example gradients. + +.. lv_example:: styles/lv_example_style_2 + :language: c + +.. lv_example:: styles/lv_example_style_16 + :language: c + +.. lv_example:: styles/lv_example_style_17 + :language: c + +.. lv_example:: styles/lv_example_style_18 + :language: c + +For each gradient type, multiple color and opacity values can be assigned. These are +called "stops". The maximum number of stops is limited to +:c:macro:`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. + +:cpp:expr:`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. + +.. code-block:: c + + 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: + +- :cpp:enumerator:`LV_GRAD_EXTEND_PAD`: Repeat the same color +- :cpp:enumerator:`LV_GRAD_EXTEND_REPEAT`: Repeat the pattern +- :cpp:enumerator:`LV_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 :cpp:expr:`lv_grad_init_stops` call either +:cpp:expr:`lv_grad_horizontal_init(&grad_dsc)` or +:cpp:expr:`lv_grad_vertical_init(&grad_dsc)` to get a horizontal or vertical gradient +descriptor. + +.. lv_example:: grad/lv_example_grad_1 + :language: c + + +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 :cpp:func:`lv_grad_init_stops` call +:cpp:expr:`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. + +.. lv_example:: grad/lv_example_grad_2 + :language: c + + +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 :cpp:func:`lv_grad_init_stops`, the outer +circle can be set by: +:cpp:expr:`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: +:cpp:expr:`lv_grad_radial_set_focal(&grad_dsc, center_x, center_y, radius)` + +.. lv_example:: grad/lv_example_grad_3 + :language: c + + +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 :cpp:func:`lv_grad_init_stops`, the conic gradient +can be set up with: +:cpp:expr:`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. + +.. lv_example:: grad/lv_example_grad_4 + :language: c + + + +Border Draw Descriptor +********************** + +The :cpp:type:`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 :cpp:type:`lv_border_side_t`, such as +:cpp:enumerator:`LV_BORDER_SIDE_BOTTOM`. :cpp:enumerator:`LV_BORDER_SIDE_ALL` +applies to all sides, while :cpp:enumerator:`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: + +- :cpp:expr:`lv_draw_border_dsc_init(&dsc)` initializes a border Draw Task. +- :cpp:expr:`lv_draw_sw_border(layer, &dsc, area)` creates a Draw Task to draw a border + inward from its area. +- :cpp:expr:`lv_draw_task_get_border_dsc(draw_task)` retrieves the border descriptor + from a Draw Task. + +.. lv_example:: styles/lv_example_style_3 + :language: c + + +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 :cpp:type:`lv_draw_rect_dsc_t` for convenience. The two +differences compared to borders in :cpp:type:`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, if ``outline_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. + +.. lv_example:: styles/lv_example_style_4 + :language: c + + + +Box Shadow Draw Descriptor +************************** + +The :cpp:type:`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, :cpp:expr:`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: + +- :cpp:expr:`lv_draw_box_shadow_dsc_init(&dsc)` initializes a box shadow Draw Task. +- :cpp:expr:`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. +- :cpp:expr:`lv_draw_task_get_box_shadow_dsc(draw_task)` retrieves the box shadow + descriptor from a Draw Task. + +.. lv_example:: styles/lv_example_style_5 + :language: c + + +.. |deg| unicode:: U+000B0 .. DEGREE SIGN + +Image Draw Descriptor +********************* + +The :cpp:type:`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\ |deg|\ ). +: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\ |deg|\ ). +: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 :cpp:enumerator:`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 :cpp:type:`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: + + 1. Layer rendering, where only part of a layer may be rendered and + ``clip_radius`` needs the original image dimensions. + 2. 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: + +- :cpp:expr:`lv_draw_image_dsc_init(&dsc)` initializes an image draw descriptor. +- :cpp:expr:`lv_draw_image(layer, &dsc, area)` creates a task to draw an image in a given area. +- :cpp:expr:`lv_draw_task_get_image_dsc(draw_task)` retrieves the image descriptor from a task. + +.. lv_example:: widgets/canvas/lv_example_canvas_6 + :language: c + +.. lv_example:: styles/lv_example_style_6 + :language: c + + +Layers - Special Images +----------------------- + +Layers are treated as images, so an :cpp:type:`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 :ref:`layers`. + + + +Label Draw Descriptor +********************* + +The :cpp:type:`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 :cpp:type:`lv_text_align_t`. +:bidi_dir: Base direction for right-to-left text rendering (e.g., Arabic). + See :cpp:type:`lv_base_dir_t`. +:decor: Text decoration, e.g., underline. See :cpp:type:`lv_text_decor_t`. +:flag: Flags for text rendering. See :cpp:type:`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 :cpp:type:`lv_draw_label_hint_t`. + +Functions for text drawing: + +- :cpp:expr:`lv_draw_label_dsc_init(&dsc)` initializes a label draw descriptor. +- :cpp:expr:`lv_draw_label(layer, &dsc, area)` creates a task to render text in an area. +- :cpp:expr:`lv_draw_character(layer, &dsc, point, unicode_letter)` creates a task to + draw a character at a specific point. +- :cpp:expr:`lv_draw_task_get_label_dsc(draw_task)` retrieves the label descriptor from a task. + +For character-specific drawing in draw units, use +:cpp:expr:`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 +:cpp:type:`lv_draw_glyph_cb_t`. + +.. lv_example:: widgets/canvas/lv_example_canvas_4 + :language: c + +.. lv_example:: styles/lv_example_style_8 + :language: c + + + +Arc Draw Descriptor +******************* + +The :cpp:type:`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: + +- :cpp:expr:`lv_draw_arc_dsc_init(&dsc)` initializes an arc descriptor. +- :cpp:expr:`lv_draw_arc(layer, &dsc)` creates a task to render an arc. +- :cpp:expr:`lv_draw_task_get_arc_dsc(draw_task)` retrieves arc descriptor from task. + +.. lv_example:: widgets/canvas/lv_example_canvas_5 + :language: c + +.. lv_example:: styles/lv_example_style_7 + :language: c + + + +Line Draw Descriptor +******************** + +The :cpp:type:`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: + +- :cpp:expr:`lv_draw_line_dsc_init(&dsc)` initializes a line descriptor. +- :cpp:expr:`lv_draw_line(layer, &dsc)` creates a task to draw a line. +- :cpp:expr:`lv_draw_task_get_line_dsc(draw_task)` retrieves line descriptor. + +.. lv_example:: widgets/canvas/lv_example_canvas_7 + :language: c + +.. lv_example:: styles/lv_example_style_9 + :language: c + + + +Triangle Draw Descriptor +************************ + +Triangles are defined by :cpp:type:`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 not ``LV_GRAD_DIR_NONE``, the + ``color`` field is ignored. The ``opa`` field adjusts overall opacity. + +Functions for triangle drawing: +- :cpp:expr:`lv_draw_triangle_dsc_init(&dsc)` initializes a triangle descriptor. +- :cpp:expr:`lv_draw_triangle(layer, &dsc)` creates a task to draw a triangle. +- :cpp:expr:`lv_draw_task_get_triangle_dsc(draw_task)` retrieves triangle descriptor. + +.. lv_example:: widgets/canvas/lv_example_canvas_9 + :language: c + + + +Vector Draw Descriptor +********************** + +TODO + + + +Masking Operation +***************** + +There are several options to mask parts of a layer, Widget, or drawing: + +1. **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. + +2. **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. + +3. **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 :cpp:type:`lv_draw_mask_rect_dsc_t` descriptor. + - Set ``area``, ``radius``, and ``keep_outside`` parameters. If + ``keep_outside`` is set to 1, areas outside of ``area`` remain unchanged. + Otherwise, they are cleared. + - Call :cpp:expr:`lv_draw_mask_rect(layer, &dsc)`. + + Note: The layer must have a color format with an alpha channel, typically + :cpp:expr:`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. + +4. **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 setting ``clip_radius`` to the layer. + +5. **Bitmap Masking for Images**: + Using ``..._style_bitmap_mask`` or ``bitmap_mask`` in + :cpp:type:`lv_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 :cpp:enumerator:`LV_COLOR_FORMAT_L8` buffer, + bitmap masks can be rendered dynamically. + +.. lv_example:: widgets/canvas/lv_example_label_4 + :language: c + +.. lv_example:: widgets/canvas/lv_example_roller_3 + :language: c + + + +API +*** + +.. API equals: + lv_draw_arc + lv_draw_dsc_base_t + lv_draw_image + lv_draw_label + lv_draw_label_dsc_init + lv_draw_line + lv_draw_mask_rect + lv_draw_rect_dsc_t + lv_draw_task_get_label_dsc + lv_draw_triangle + LV_EVENT_DRAW_TASK_ADDED + lv_grad_dsc_t + lv_grad_horizontal_init diff --git a/docs/src/details/main-modules/draw/draw_layers.rst b/docs/src/details/main-modules/draw/draw_layers.rst new file mode 100644 index 0000000000..2a2773830e --- /dev/null +++ b/docs/src/details/main-modules/draw/draw_layers.rst @@ -0,0 +1,140 @@ +.. _draw_layers: + +=========== +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 :cpp:expr:`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 :cpp:func:`lv_draw_layer`. + +2. **Modifying the created Draw Tasks**: + In :cpp:enumerator:`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: + + .. code-block:: c + + /* 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: + + .. code-block:: c + + /* 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 :cpp:func:`lv_draw_layer_create`: + +.. code-block:: c + + 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 :ref:`Draw API ` and :ref:`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: + +.. code-block:: c + + 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 :ref:`Draw Units` when the first :ref:`Draw Task ` is dispatched. + +Layer buffers can be large, so ensure there is sufficient heap memory or increase +:c:macro:`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 :c:macro:`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 +:c:macro:`LV_DRAW_LAYER_MAX_MEMORY` in ``lv_conf.h``. If set to ``0``, there is no +limit. + + + +API +*** + +.. API equals: + lv_draw_layer_create + LV_EVENT_DRAW_TASK_ADDED + lv_event_get_layer diff --git a/docs/src/details/main-modules/draw.rst b/docs/src/details/main-modules/draw/draw_pipeline.rst similarity index 62% rename from docs/src/details/main-modules/draw.rst rename to docs/src/details/main-modules/draw/draw_pipeline.rst index bf2753d8a1..cf8cc18716 100644 --- a/docs/src/details/main-modules/draw.rst +++ b/docs/src/details/main-modules/draw/draw_pipeline.rst @@ -1,16 +1,26 @@ -.. _draw: +.. _draw_pipeline: -================ -Drawing Pipeline -================ +============= +Draw Pipeline +============= -Overview -******** +What is Drawing? +**************** -Drawing is writing pixel colors into a buffer where they will be delivered to a -display panel as pixels. Sometimes it involves computing those colors before they -are written (e.g. combining them with other colors when an object has partial opacity). +Drawing (also known as :dfn:`rendering`) is writing pixel colors into a buffer where +they will be delivered to a display panel as pixels. Sometimes this is done by +copying colors from places like background- and foreground-color properties. Other +times it involves computing those colors before they are written (e.g. combining them +with other colors when an object higher on the Z axis has partial opacity). + +The following sections cover the LVGL drawing logic and how to use it and optionally +tune it to fit your particular project (e.g. if you have a GPU or other resources +that you would like to get involved). + + +Draw-Pipeline Overview +********************** On modern computing hardware meant to be used with larger display panels, there are sometimes options for different ways drawing can be accomplished. For example, some @@ -18,20 +28,24 @@ MCUs come with hardware that is very good (and fast) at certain types of drawing tasks. Alternatively, you might have access to a drawing library that performs certain types of drawing tasks with great efficiency. To make it possible to utilize such facilities in the most efficient fashion, LVGL v9 and onwards implements a -"Drawing Pipeline", like an assembly line, where decisions are made as to which +:dfn:`Drawing Pipeline`, like an assembly line, where decisions are made as to which drawing tasks (:ref:`Draw Tasks`) are given to which "logic entities" (:ref:`Draw Units`) in order to be carried out. -This Pipeline is designed so that it is both flexible and extendable. As a -programmer, you can hook into it in order to provide LVGL with guidance as to what -Draw Units should receive what types of Draw Tasks, or replace LVGL's built-in -software rendering logic to any degree you choose. +This Pipeline is designed so that it is both flexible and extendable. You can use it +to perform custom rendering with a GPU, or replace the parts of the built-in software +rendering logic to any extent desired. + +Using events, it's also possible to modify :ref:`draw tasks` or insert new ones as +LVGL renders Widgets. + +The following sections describe the basic terminology and concepts of rendering. .. _draw tasks: Draw Tasks ----------- +********** A "Draw Task" (:cpp:type:`lv_draw_task_t`) is a package of information that is created at the beginning of the Drawing Pipeline when a request to draw is made. @@ -40,20 +54,20 @@ one or more Draw Tasks and pass them down the Drawing Pipeline. Each Draw Task carries all the information required to: - compute which :ref:`Draw Unit ` should receive this task, and -- give the Draw Unit all the details required to accomplish the drawing task. +- give the Draw Unit all the information required to accomplish the drawing task. A Draw Task carries the following information: -:type: defines the drawing algorithm involved (e.g. line, fill, border, image, - label, arc, triangle, etc.) -:area: defines the rectangle involved where drawing will occur +:type: defines the drawing algorithm involved (e.g. line, fill, + border, image, label, arc, triangle, etc.) +:area: defines the rectangle in which drawing will occur :transformation matrix: if :c:macro:`LV_DRAW_TRANSFORM_USE_MATRIX` is configured to '1' :state: waiting, queued, in progress, completed :drawing descriptor: carries details of the drawing to be performed -:preferred Draw Unit ID: the ID of the Draw Unit that should take this task +:preferred Draw Unit ID: identifier of the Draw Unit that should carry out this task :preference score: value describing the speed of the specified Draw Unit relative to software rendering (more on this below) -:next: a link to the next drawing task in the list. +:next: a link to the next Draw Task in the list. Draw Tasks are collected in a list and periodically dispatched to Draw Units. @@ -61,12 +75,15 @@ Draw Tasks are collected in a list and periodically dispatched to Draw Units. .. _draw units: Draw Units ----------- +********** A "Draw Unit" (based on :cpp:type:`lv_draw_unit_t`) is any "logic entity" that can generate the output required by a :ref:`Draw Task `. This can be a CPU -core, a GPU, a new rendering library for certain (or all) Draw Tasks, or anything -that can accomplish drawing. +core, a GPU, a custom rendering library for specific Draw Tasks, or any entity +capable of performing rendering. + +For a reference implementation of a draw unit, see +`lv_draw_sw.c `__. During LVGL's initialization (:cpp:func:`lv_init`), a list of Draw Units is created. If :c:macro:`LV_USE_DRAW_SW` is set to ``1`` in ``lv_conf.h`` (it is by default), the @@ -76,8 +93,8 @@ they are added to this list during LVGL's initialization. If you are adding you Draw Unit(s), you add each available drawing unit to that list by calling :cpp:expr:`lv_draw_create_unit(sizeof(your_draw_unit_t))`. With each call to that function, the newly-created draw unit is added to the head of that list, pushing -already-existing draw units further back in the list, making the earliest Draw Unit -created last in the list. The order of this list (and thus the order in which +already-existing draw units further back in the list, pushing the Draw Units created +earlier farther back in the list. The order of this list (and thus the order in which :ref:`Draw Task Evaluation` is performed) is governed by the order in which each Draw Unit is created. @@ -96,23 +113,29 @@ For an example of how draw-unit cration and initialization is done, see :cpp:func:`lv_draw_sw_init` in lv_draw_sw.c_ or the other draw units whose ``init`` functions are optionally called in :cpp:func:`lv_init`. +.. _lv_draw_sw.c: https://github.com/lvgl/lvgl/blob/master/src/draw/sw/lv_draw_sw.c + .. _draw task evaluation: Draw Task Evaluation --------------------- +******************** When each :ref:`Draw Task ` is created, each existing Draw Unit is "consulted" as to its "appropriateness" for the task. It does this through an "evaluation callback" function pointer (a.k.a. ``evaluate_cb``), which each Draw -Unit sets (for itself) during its initialization. Normally, that evaluation -optionally examines the existing "preference score" for the task mentioned above, -and if it can accomplish that type of task (e.g. line drawing) faster than other -Draw Units that have already reported, it writes its own "preference score" and -"preferred Draw Unit ID" to the respective fields in the task. In this way, by the -time the evaluation sequence is complete, the task will contain the score and the ID -of the Drawing Unit that will be used to perform that task when it is -:ref:`dispatched `. +Unit sets (for itself) during its initialization. Normally, that evaluation: + +- optionally examines the existing "preference score" for the task mentioned above, +- if it can accomplish that type of task (e.g. line drawing) faster than other + Draw Units that have already reported, it writes its own "preference score" and + "preferred Draw Unit ID" to the respective fields in the task. + +In this way, by the time the evaluation sequence is complete, the task will contain +the score and the ID of the Drawing Unit that will be used to perform that task when +it is :ref:`dispatched `. + +This logic, of course, can be overridden or redefined, depending on system design. As a side effect, this also ensures that the same Draw Unit will be selected consistently, depending on the type (and nature) of the drawing task, avoiding any @@ -128,7 +151,7 @@ will handle it. .. _draw task dispatching: Dispatching ------------ +*********** While collecting Draw Tasks LVGL frequently dispatches the collected Draw Tasks to their assigned Draw Units. This is handled via the ``dispatch_cb`` of the Draw Units. @@ -145,54 +168,32 @@ ready to be carried out. The ramifications of having multiple drawing threads a taken into account for this. -Layers ------- - -A layer is a buffer with a given area on which the pixel rendering occurrs. Each -display has a "main" layer, but during rendering additional layers might be created -internally to handle for example arbitrary Widget transformations. - - -Object Hierarchy ----------------- +Run-Time Object Hierarchy +************************* All of the above have this relationship at run time: -- LVGL +- LVGL (global) - - list of Draw Units - - list of Display(s) + - list of :ref:`Draw Units` + - list of :ref:`Display(s) ` - - Layer(s): Each Display has its own list of Layers + - Layer(s): Each :ref:`Display object ` has its own list of :ref:`draw_layers` - - Draw Tasks: Each Layer has its own list of Draw Tasks - - - -.. _draw_events: - -Events -****** - -- :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED` when each :ref:`Draw Task ` - is created and before it is dispatched to the :ref:`Draw Unit ` that - will handle it. - - - -.. admonition:: Further Reading - - Learn more about :ref:`lv_obj_events` emitted by all Widgets. - - Learn more about :ref:`events`. - - lv_draw_sw.c_ - - -.. _lv_draw_sw.c: https://github.com/lvgl/lvgl/blob/master/src/draw/sw/lv_draw_sw.c + - Draw Tasks: Each Layer has its own list of :ref:`Draw Tasks` API *** +.. API equals: + lv_draw_create_unit + lv_draw_get_next_available_task + lv_draw_label + lv_draw_rect + lv_draw_sw_init + lv_draw_task_t + LV_DRAW_TRANSFORM_USE_MATRIX + lv_draw_unit_t + LV_USE_DRAW_OPENGLES diff --git a/docs/src/details/main-modules/draw/index.rst b/docs/src/details/main-modules/draw/index.rst new file mode 100644 index 0000000000..81ce958a01 --- /dev/null +++ b/docs/src/details/main-modules/draw/index.rst @@ -0,0 +1,13 @@ +.. _draw: + +======= +Drawing +======= + +.. toctree:: + :maxdepth: 2 + + draw_pipeline + draw_api + draw_layers + draw_descriptors diff --git a/docs/src/details/main-modules/index.rst b/docs/src/details/main-modules/index.rst index 92c7f771b0..be1a82a2de 100644 --- a/docs/src/details/main-modules/index.rst +++ b/docs/src/details/main-modules/index.rst @@ -15,4 +15,4 @@ Main Modules timer animation fs - draw + draw/index diff --git a/examples/grad/index.rst b/examples/grad/index.rst index a8a4fc4152..71621f85cb 100644 --- a/examples/grad/index.rst +++ b/examples/grad/index.rst @@ -1,25 +1,23 @@ - - -Play with a simple horizontal gradient --------------------------------------- +Simple Horizontal Gradient +-------------------------- .. lv_example:: get_started/lv_example_grad_1 :language: c -Play with a linear (skew) gradient ----------------------------------- +Linear (Skew) Gradient +---------------------- .. lv_example:: get_started/lv_example_grad_2 :language: c -Play with a radial gradient ---------------------------- +Radial Gradient +--------------- .. lv_example:: get_started/lv_example_grad_3 :language: c -Play with a conical gradient ----------------------------- +Conical Gradient +---------------- .. lv_example:: get_started/lv_example_grad_4 :language: c