diff --git a/docs/porting/disp.rst b/docs/porting/disp.rst index d93a61188..6216fef6c 100644 --- a/docs/porting/disp.rst +++ b/docs/porting/disp.rst @@ -5,7 +5,7 @@ Display interface ================= To create a display for LVGL call -:cpp:expr:`lv_disp_t * disp = lv_disp_create(hor_res, ver_res)`. You can create +:cpp:expr:`lv_display_t * display = lv_display_create(hor_res, ver_res)`. You can create a multiple displays and a different driver for each (see below), Basic setup @@ -22,38 +22,39 @@ An example ``flush_cb`` looks like this: .. code:: c - void my_flush_cb(lv_disp_t * disp, const lv_area_t * area, lv_color_t * buf) + void my_flush_cb(lv_display_t * display, const lv_area_t * area, void * px_map) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one *`put_px` is just an example, it needs to be implemented by you.*/ + uint16_t * buf16 = (uint16_t)px_map; /*Let's say it's a 16 bit (RGB565) display*/ int32_t x, y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { - put_px(x, y, *buf); - buf++; + put_px(x, y, *buf16); + buf16++; } } /* IMPORTANT!!! * Inform LVGL that you are ready with the flushing and buf is not used anymore*/ - lv_disp_flush_ready(disp); + lv_display_flush_ready(disp); } -Use :cpp:expr:`lv_disp_set_flush_cb(disp, my_flush_cb)` to set a new ``flush_cb``. +Use :cpp:expr:`lv_display_set_flush_cb(disp, my_flush_cb)` to set a new ``flush_cb``. -:cpp:expr:`lv_disp_flush_ready(disp)` needs to be called when flushing is ready +:cpp:expr:`lv_display_flush_ready(disp)` needs to be called when flushing is ready to inform LVGL the buffer is not used anymore by the driver and it can render new content into it. LVGL might render the screen in multiple chunks and therefore call ``flush_cb`` multiple times. To see if the current one is the last chunk -of rendering use :cpp:expr:`lv_disp_flush_is_last(disp)`. +of rendering use :cpp:expr:`lv_display_flush_is_last(display)`. Draw buffers ------------ The draw buffers can be set with -:cpp:expr:`lv_disp_set_draw_buffers(disp, buf1, buf2, buf_size_px, render_mode)` +:cpp:expr:`lv_display_set_draw_buffers(display, buf1, buf2, buf_size_px, render_mode)` - ``buf1`` a buffer where LVGL can render - ``buf2`` a second optional buffer (see more details below) @@ -64,26 +65,27 @@ The draw buffers can be set with screen is smaller parts. This way the buffers can be smaller then the display to save RAM. At least 1/10 screen size buffer(s) are recommended. In ``flush_cb`` the rendered images needs to be - copied to the given area of the display. + copied to the given area of the display. In this mode if a button is pressed + only the button's area will be redrawn. - :cpp:enumerator:`LV_DISP_RENDER_MODE_DIRECT` The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer. This way the buffer always contain the whole image. If two buffer are used the rendered ares are automatically copied to the other buffer after flushing. Due to this in ``flush_cb`` typically - only a frame buffer address needs to be changed and always the - changed areas will be redrawn. - - :cpp:enumerator:`LV_DISP_RENDER_MODE_FULL` The buffer can smaller or screen - sized but LVGL will always redraw the whole screen even is only 1 + only a frame buffer address needs to be changed. If a button is pressed + only the button's area will be redrawn. + - :cpp:enumerator:`LV_DISP_RENDER_MODE_FULL` The buffer(s) has to be screen + sized and LVGL will always redraw the whole screen even if only 1 pixel has been changed. If two screen sized draw buffers are provided, LVGL's display handling works like "traditional" double buffering. This means the ``flush_cb`` callback only has to update - the address of the framebuffer (``color_p`` parameter). + the address of the frame buffer to the ``px_map`` parameter. Example: .. code:: c - static lv_color_t buf[LCD_HOR_RES * LCD_VER_RES / 10]; + static uint16_t buf[LCD_HOR_RES * LCD_VER_RES / 10]; lv_disp_set_draw_buffers(disp, buf, NULL, sizeof(buf), LV_DISP_RENDER_MODE_PARTIAL); One buffer @@ -91,8 +93,9 @@ One buffer If only one buffer is used LVGL draws the content of the screen into that draw buffer and sends it to the display via the ``flush_cb``. LVGL -then needs to wait until the content of the buffer is sent to the -display before drawing something new into it. +then needs to wait until :cpp:expr:`lv_display_flush_ready` is called +(that is the content of the buffer is sent to the +display) before drawing something new into it. Two buffers ^^^^^^^^^^^ @@ -110,111 +113,69 @@ Resolution ---------- To set the resolution of the display after creation use -:cpp:expr:`lv_disp_set_res(disp, hor_res, ver_res)` +:cpp:expr:`lv_display_set_resolution(display, hor_res, ver_res)` It's not mandatory to use the whole display for LVGL, however in some cases the physical resolution is important. For example the touchpad still sees the whole resolution and the values needs to be converted to the active LVGL display area. So the physical resolution and the offset of the active area can be set with -:cpp:expr:`lv_disp_set_physical_res(disp, hor_res, ver_res)` and -:cpp:expr:`lv_disp_set_offset(disp, x, y)` +:cpp:expr:`lv_display_set_physical_resolution(disp, hor_res, ver_res)` and +:cpp:expr:`lv_display_set_offset(disp, x, y)` + +Flush wait callback +------------------- + +By using :cpp:expr:`lv_display_flush_ready` LVGL will spin in a loop +while waiting for flushing. + +However with the help of :cpp:expr:`lv_display_set_flush_wait_cb` a custom +wait callback be set for flushing. This callback can use a semaphore, mutex, +or anything else to optimize while the waiting for flush. + +If ``flush_wait_cb`` is not set, LVGL assume that `lv_display_flush_ready` +is used. Rotation -------- LVGL supports rotation of the display in 90 degree increments. You can -select whether you'd like software rotation or hardware rotation. +select whether you would like software rotation or hardware rotation. The orientation of the display can be changed with -``lv_disp_set_rotation(disp, LV_DISP_ROTATION_0/90/180/270, true/false)``. +``lv_disp_set_rotation(disp, LV_DISPLAY_ROTATION_0/90/180/270)``. LVGL will swap the horizontal and vertical resolutions internally -according to the set degree. If the last parameter is ``true`` LVGL will -rotate the rendered image. If it's ``false`` the display driver should -rotate the rendered image. +according to the set degree. When changing the rotation +:cpp:expr:`LV_EVENT_SIZE_CHANGED` is sent to the display to allow +reconfiguring the hardware. In lack of hardware display rotation support +:cpp:expr:`lv_draw_sw_rotate` can be used to rotate the buffer in the +``flush_cb``. Color format ------------ -Set the color format of the display. The default is -:cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` which means LVGL render with the follow -formats depending on :c:macro:`LV_COLOR_DEPTH`: +The default color format of the display is set according to :c:macro:`LV_COLOR_DEPTH` +(see ``lv_conf.h``) - :c:macro:`LV_COLOR_DEPTH` ``32``: XRGB8888 (4 bytes/pixel) - :c:macro:`LV_COLOR_DEPTH` ``24``: RGB888 (3 bytes/pixel) - :c:macro:`LV_COLOR_DEPTH` ``16``: RGB565 (2 bytes/pixel) -- :c:macro:`LV_COLOR_DEPTH` ``8``: L8 (1 bytes/pixel) +- :c:macro:`LV_COLOR_DEPTH` ``8``: L8 (1 bytes/pixel) Not supported yet The ``color_format`` can be changed with -:cpp:expr:`lv_disp_set_color_depth(disp, LV_COLOR_FORMAT_...)` to the following -values: +:cpp:expr:`lv_display_set_color_depth(display, LV_COLOR_FORMAT_...)`. +Besides the default value :c:macro:`LV_COLOR_FORMAT_ARGB8888` can be +used as a well. -- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_ALPHA`: Append an alpha byte to the native format resulting - in A8L8, ARGB8565, ARGB8888 formats. -- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_REVERSE`: Reverse the byte order of the native format. Useful if the - rendered image is sent to the display via SPI and - the display needs the bytes in the opposite order. -- :cpp:enumerator:`LV_COLOR_FORMAT_L8`: Lightness only on 8 bit -- :cpp:enumerator:`LV_COLOR_FORMAT_A8`: Alpha only on 8 bit -- :cpp:enumerator:`LV_COLOR_FORMAT_I8`: Indexed (palette) 8 bit -- :cpp:enumerator:`LV_COLOR_FORMAT_A8L8`: Lightness on 8 bit with 8 bit alpha -- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB2222`: ARGB with 2 bit for each channel -- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`: 16 bit RGB565 format without alpha channel -- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8565`: 16 bit RGB565 format and 8 bit alpha channel -- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB1555`: 5 bit for each color channel and 1 bit for alpha -- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB4444`: 4 bit for each channel -- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`: 8 bit for each color channel with out alpha channel -- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`: 8 bit for each channel -- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`: 8 bit for each color channel and 8 bit placeholder for the alpha channel - -If the color format is set to non-native ``draw_ctx->buffer_convert`` -function will be called before calling ``flush_cb`` to convert the -native color format to the desired, therefore rendering in non-native -formats has a negative effect on performance. Learn more about -``draw_ctx`` `here `__. - -It's very important that draw buffer(s) should be large enough for both -the native format and the target color format. For example if -``LV_COLOR_DEPTH == 16`` and :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888` is selected -LVGL will choose the larger to figure out how many pixel can be -rendered at once. Therefore with :cpp:enumerator:`LV_DISP_RENDER_MODE_FULL` and the -larger pixel size needs to be chosen. - -:cpp:enumerator:`LV_DISP_RENDER_MODE_DIRECT` supports only the -:cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` format. - -Antialiasing ------------- - -:cpp:expr:`lv_disp_set_antialiasing(disp, true/false)` enables/disables the -antialiasing (edge smoothing) on the given display. +It's very important that draw buffer(s) should be large enough for any +selected color format. User data --------- -With :cpp:expr:`lv_disp_set_user_data(disp, p)` a pointer to a custom data can +With :cpp:expr:`lv_display_set_user_data(disp, p)` a pointer to a custom data can be stored in display object. -Events -****** - -:cpp:expr:`lv_disp_add_event_cb(disp, event_cb, LV_DISP_EVENT_..., user_data)` adds -an event handler to a display. The following events are sent: - -- :cpp:enumerator:`LV_DISP_EVENT_INVALIDATE_AREA` An area is invalidated (marked for redraw). - :cpp:expr:`lv_event_get_param(e)` returns a pointer to an :cpp:struct:`lv_area_t` - variable with the coordinates of the area to be invalidated. The area can - be freely modified is needed to adopt it the special requirement of the - display. Usually needed with monochrome displays to invalidate Nx8 - lines at once. -- :cpp:enumerator:`LV_DISP_EVENT_RENDER_START`: Called when rendering starts. -- :cpp:enumerator:`LV_DISP_EVENT_RENDER_READY`: Called when rendering is ready -- :cpp:enumerator:`LV_DISP_EVENT_RESOLUTION_CHANGED`: Called when the resolution changes due - to :cpp:func:`lv_disp_set_resolution` or :cpp:func:`lv_disp_set_rotation`. - -Other options -************* - Decoupling the display refresh timer ------------------------------------ @@ -246,6 +207,30 @@ select the display to refresh before :cpp:expr:`_lv_disp_refr_timer(NULL)`. If the performance monitor is enabled, the value of :c:macro:`LV_DEF_REFR_PERIOD` needs to be set to be consistent with the refresh period of the display to ensure that the statistical results are correct. + +Events +****** + +:cpp:expr:`lv_display_add_event_cb(disp, event_cb, LV_EVENT_..., user_data)` adds +an event handler to a display. The following events are sent: + +- :cpp:enumerator:`LV_EVENT_INVALIDATE_AREA` An area is invalidated (marked for redraw). + :cpp:expr:`lv_event_get_param(e)` returns a pointer to an :cpp:struct:`lv_area_t` + variable with the coordinates of the area to be invalidated. The area can + be freely modified if needed to adopt it the special requirement of the + display. Usually needed with monochrome displays to invalidate ``N x 8`` + rows or columns at once. +- :cpp:enumerator:`LV_EVENT_REFR_REQUEST`: Sent when something happened that requires redraw. +- :cpp:enumerator:`LV_EVENT_REFR_START`: Sent when a refreshing cycle starts. Sent even if there is nothing to redraw. +- :cpp:enumerator:`LV_EVENT_REFR_READY`: Sent when refreshing is ready (after rendering and calling the ``flush_cb``). Sent even if no redraw happened. +- :cpp:enumerator:`LV_EVENT_RENDER_START`: Sent when rendering starts. +- :cpp:enumerator:`LV_EVENT_RENDER_READY`: Sent when rendering is ready (before calling the ``flush_cb``) +- :cpp:enumerator:`LV_EVENT_FLUSH_START`: Sent before the ``flush_cb`` is called. +- :cpp:enumerator:`LV_EVENT_FLUSH_READY`: Sent when the ``flush_cb`` returned. +- :cpp:enumerator:`LV_EVENT_RESOLUTION_CHANGED`: Sent when the resolution changes due + to :cpp:func:`lv_display_set_resolution` or :cpp:func:`lv_display_set_rotation`. + + Further reading *************** diff --git a/docs/porting/draw.rst b/docs/porting/draw.rst index f51fd96b6..d9bdacb59 100644 --- a/docs/porting/draw.rst +++ b/docs/porting/draw.rst @@ -1,252 +1,75 @@ -============== -Add custom GPU -============== +========== +Custom GPU +========== LVGL has a flexible and extendable draw pipeline. You can hook it to do some rendering with a GPU or even completely replace the built-in software renderer. -Draw context -************ -The core structure of drawing is :cpp:type:`lv_draw_ctx_t`. It contains a -pointer to a buffer where drawing should happen and a couple of -callbacks to draw rectangles, texts, and other primitives. +Overview +******** -Fields ------- - -:cpp:type:`lv_draw_ctx_t` has the following fields: - -- ``void * buf`` Pointer to a buffer to draw into -- ``lv_area_t * buf_area`` The position and size of ``buf`` (absolute coordinates) -- ``const lv_area_t * clip_area`` The current clip area with absolute coordinates, always the same or smaller than ``buf_area``. All drawings should be clipped to this area. -- ``void (*draw_rect)()`` Draw a rectangle with shadow, gradient, border, etc. -- ``void (*draw_arc)()`` Draw an arc -- ``void (*draw_img_decoded)()`` Draw an (A)RGB image that is already decoded by LVGL. -- ``lv_res_t (*draw_img)()`` Draw an image before decoding it (it bypasses LVGL's internal image decoders) -- ``void (*draw_letter)()`` Draw a letter -- ``void (*draw_line)()`` Draw a line - ``void (*draw_polygon)()`` Draw a polygon -- ``void (*draw_bg)()`` Replace the buffer with a rect without decoration like radius or borders. -- ``void (*wait_for_finish)()`` Wait until all background operation are finished. (E.g. GPU operations) -- ``void * user_data`` Custom user data for arbitrary purpose - -(For the sake of simplicity the parameters of the callbacks are not shown here.) - -All ``draw_*`` callbacks receive a pointer to the current ``draw_ctx`` -as their first parameter. Among the other parameters there is a -descriptor that tells what to draw, e.g. for ``draw_rect`` it's called -:cpp:struct:`lv_draw_rect_dsc_t`, -for :cpp:func:`lv_draw_line` it's called :cpp:struct:`lv_draw_line_dsc_t`, -etc. - -To correctly render according to a ``draw_dsc`` you need to be familiar -with the `Boxing model `__ -of LVGL and the meanings of the fields. The name and meaning of the -fields are identical to name and meaning of the `Style properties `__. - -Initialization --------------- - -The :cpp:type:`lv_disp_t` has 4 fields related to the draw context: - -- ``lv_draw_ctx_t * draw_ctx`` Pointer to the ``draw_ctx`` of this display -- ``void (*draw_ctx_init)(struct _lv_disp_t * disp_drv, lv_draw_ctx_t * draw_ctx)`` Callback to initialize a ``draw_ctx`` -- ``void (*draw_ctx_deinit)(struct _lv_disp_t * disp_drv, lv_draw_ctx_t * draw_ctx)`` Callback to de-initialize a ``draw_ctx`` -- ``size_t draw_ctx_size`` Size of the draw context structure. E.g. :cpp:expr:`sizeof(lv_draw_sw_ctx_t)` - -When you ignore these fields, LVGL will set default values for callbacks -and size in :cpp:func:`lv_disp_drv_init` based on the configuration in -``lv_conf.h``. :cpp:func:`lv_disp_drv_register` will allocate a ``draw_ctx`` -based on ``draw_ctx_size`` and call :cpp:func:`draw_ctx_init` on it. - -However, you can overwrite the callbacks and the size values before -calling :cpp:func:`lv_disp_drv_register`. It makes it possible to use your own -``draw_ctx`` with your own callbacks. - -Software renderer -***************** - -LVGL's built in software renderer extends the basic :cpp:type:`lv_draw_ctx_t` -structure and sets the draw callbacks. It looks like this: - -.. code:: c - - typedef struct { - /** Include the basic draw_ctx type*/ - lv_draw_ctx_t base_draw; - - /** Blend a color or image to an area*/ - void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); - } lv_draw_sw_ctx_t; - -Set the draw callbacks in :cpp:func:`draw_ctx_init` like: - -.. code:: c - - draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect; - draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter; - ... - -Blend callback --------------- - -As you saw above the software renderer adds the ``blend`` callback -field. It's a special callback related to how the software renderer -works. All draw operations end up in the ``blend`` callback which can -either fill an area or copy an image to an area by considering an optional mask. - -The :cpp:struct:`lv_draw_sw_blend_dsc_t` parameter describes what and how to -blend. It has the following fields: - -- ``const lv_area_t * blend_area`` The area with absolute coordinates to draw - on ``draw_ctx->buf``. If ``src_buf`` is set, it's the coordinates of the image to blend. -- ``const lv_color_t * src_buf`` Pointer to an image to blend. If set, - ``color`` is ignored. If not set fill ``blend_area`` with ``color`` -- ``lv_color_t color`` Fill color. Used only if ``src_buf == NULL`` -- ``lv_opa_t * mask_buf`` NULL if ignored, or an alpha mask to apply on ``blend_area`` -- ``lv_draw_mask_res_t mask_res`` The result of the previous mask operation. (``LV_DRAW_MASK_RES_...``) -- ``const lv_area_t * mask_area`` The area of ``mask_buf`` with absolute coordinates -- ``lv_opa_t opa`` The overall opacity -- ``lv_blend_mode_t blend_mode`` E.g. :cpp:enumerator:`LV_BLEND_MODE_ADDITIVE` - -Extend the software renderer -**************************** - -New blend callback ------------------- - -Let's take a practical example: you would like to use your MCUs GPU for -color fill operations only. - -As all draw callbacks call ``blend`` callback to fill an area in the end -only the ``blend`` callback needs to be overwritten. - -First extend :cpp:struct:`lv_draw_sw_ctx_t`: - -.. code:: c +Draw task +--------- - /*We don't add new fields, so just for clarity add new type*/ - typedef lv_draw_sw_ctx_t my_draw_ctx_t; +When :cpp:expr`lv_draw_rect`, :cpp:expr`lv_draw_label` or similar functions are called +LVGL creates a so called draw task. - void my_draw_ctx_init(lv_disp_t * drv, lv_draw_ctx_t * draw_ctx) - { - /*Initialize the parent type first */ - lv_draw_sw_init_ctx(drv, draw_ctx); +Draw unit +--------- - /*Change some callbacks*/ - my_draw_ctx_t * my_draw_ctx = (my_draw_ctx_t *)draw_ctx; +The draw tasks are collected in a list and periodically dispatched to draw units. A +draw unit can a CPU core, a GPU, just a new rendering library for certain or all draw tasks, +or basically anything that can draw somehow. - my_draw_ctx->blend = my_draw_blend; - my_draw_ctx->base_draw.wait_for_finish = my_gpu_wait; - } - -After calling :cpp:expr:`lv_disp_draw_init(&drv)` you can assign the new -:cpp:func:`draw_ctx_init` callback and set ``draw_ctx_size`` to overwrite the -defaults: - -.. code:: c - - static lv_disp_t drv; - lv_disp_draw_init(&drv); - drv->hor_res = my_hor_res; - drv->ver_res = my_ver_res; - drv->flush_cb = my_flush_cb; - - /*New draw ctx settings*/ - drv->draw_ctx_init = my_draw_ctx_init; - drv->draw_ctx_size = sizeof(my_draw_ctx_t); - - lv_disp_drv_register(&drv); - -This way when LVGL calls ``blend`` it will call ``my_draw_blend`` and we -can do custom GPU operations. Here is a complete example: - -.. code:: c - - void my_draw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) - { - /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/ - lv_area_t blend_area; - if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; /*Fully clipped, nothing to do*/ - - /*Fill only non masked, fully opaque, normal blended and not too small areas*/ - if(dsc->src_buf == NULL && dsc->mask == NULL && dsc->opa >= LV_OPA_MAX && - dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) { - - /*Got the first pixel on the buffer*/ - int32_t dest_stride = lv_area_get_width(draw_ctx->buf_area); /*Width of the destination buffer*/ - lv_color_t * dest_buf = draw_ctx->buf; - dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); - - /*Make the blend area relative to the buffer*/ - lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); - - /*Call your custom gou fill function to fill blend_area, on dest_buf with dsc->color*/ - my_gpu_fill(dest_buf, dest_stride, &blend_area, dsc->color); - } - /*Fallback: the GPU doesn't support these settings. Call the SW renderer.*/ - else { - lv_draw_sw_blend_basic(draw_ctx, dsc); - } - } - -The implementation of wait callback is much simpler: - -.. code:: c - - void my_gpu_wait(lv_draw_ctx_t * draw_ctx) - { - while(my_gpu_is_working()); - - /*Call SW renderer's wait callback too*/ - lv_draw_sw_wait_for_finish(draw_ctx); - } - -New rectangle drawer +Draw task evaluation -------------------- -If your MCU has a more powerful GPU that can draw e.g. rounded -rectangles you can replace the original software drawer too. A custom -``draw_rect`` callback might look like this: +Different draw units might render slight different output for example for an image transformation or +a gradient. If such a draw task were assigned to a different draw units, the screen might jitter a +little bit. To resolve it each draw unit has an ``evaluate_cb`` which is called when a draw task is created. +Based on the type and parameters of the draw task each draw unit can decide if it want to assign the +draw task to itself. This way a certain type of draw task (e.g. rounded rectangle with horizontal +gradient) will be always assigned to the same draw unit. It avoid the above mentioned issue of +slight difference between draw units. -.. code:: c - void my_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) - { - if(lv_draw_mask_is_any(coords) == false && dsc->grad == NULL && dsc->bg_img_src == NULL && - dsc->shadow_width == 0 && dsc->blend_mode = LV_BLEND_MODE_NORMAL) - { - /*Draw the background*/ - my_bg_drawer(draw_ctx, coords, dsc->bg_color, dsc->radius); +Dispatching +----------- - /*Draw the border if any*/ - if(dsc->border_width) { - my_border_drawer(draw_ctx, coords, dsc->border_width, dsc->border_color, dsc->border_opa) - } +While collecting draw tasks LVGL frequently tries to dispatch the collected draw tasks to the draw units. +This handles via the ``dispatch_cb`` of the draw units. - /*Draw the outline if any*/ - if(dsc->outline_width) { - my_outline_drawer(draw_ctx, coords, dsc->outline_width, dsc->outline_color, dsc->outline_opa, dsc->outline_pad) - } - } - /*Fallback*/ - else { - lv_draw_sw_rect(draw_ctx, dsc, coords); - } - } +If a draw unit is busy with an other draw task, it just returns. However, it is available it can take a draw task. -``my_draw_rect`` can fully bypass the use of ``blend`` callback if -needed. +:cpp:expr:`lv_draw_get_next_available_task(layer, previous_task, draw_unit_id)` is a useful helper function which +returns an available draw task. "Available draw task" means that, all the draw tasks which should be drawn under a draw task +are ready and it is assigned to the given draw unit. -Fully custom draw engine -************************ -For example if your MCU/MPU supports a powerful vector graphics engine -you might use only that instead of LVGL's SW renderer. In this case, you -need to base the renderer on the basic :cpp:type:`lv_draw_ctx_t` (instead of -:cpp:struct:`lv_draw_sw_ctx_t`) and extend/initialize it as you wish. +Layers +------ + +A layer is a buffer with a given area on which rendering happens. Each display has a "main" layer, but +during rendering additional layers might be created internally to handle for example arbitrary widget transformations. + + +Hierarchy of modules +-------------------- + +All these together looks like this +- list of draw units +- display(s) + - layer(s): Each display has its own list of layers + - draw tasks: Each layer has its own list of draw tasks + +References +********** + +As a reference take a look at `lv_draw_sw.c `__ API *** + diff --git a/docs/porting/project.rst b/docs/porting/project.rst index 5d2d1c7c5..c14c7915a 100644 --- a/docs/porting/project.rst +++ b/docs/porting/project.rst @@ -15,8 +15,8 @@ Add lvgl to your project ------------------------ The graphics library itself is the ``lvgl`` directory. It contains a -couple of folders but to use ``lvgl`` you only need ``.c`` and ``.h`` -files from the ``src`` folder. +couple of folders but to use ``lvgl`` you only need the ``.c`` and ``.h`` +files in the ``src`` folder. Automatically add files ~~~~~~~~~~~~~~~~~~~~~~~ @@ -39,12 +39,12 @@ main Makefile: include $(LVGL_DIR)/$(LVGL_DIR_NAME)/lvgl.mk For integration with CMake take a look this section of the -`Documentation `__. +`Documentation `__. Other platforms and tools ~~~~~~~~~~~~~~~~~~~~~~~~~ -The `Get started `__ section contains many platform +The `Get started `__ section contains many platform specific descriptions e.g. for ESP32, Arduino, NXP, RT-Thread, NuttX, etc. @@ -61,8 +61,8 @@ Configuration file ------------------ There is a configuration header file for LVGL called **lv_conf.h**. You -modify this header to set the library's basic behavior, disable unused -modules and features, adjust the size of memory buffers in compile-time, +can modify this header to set the library's basic behavior, disable unused +modules and features, adjust the size of buffers in compile-time, etc. To get ``lv_conf.h`` **copy lvgl/lv_conf_template.h** next to the @@ -94,12 +94,12 @@ be set via the :c:macro:`LV_CONF_PATH` define. For example If :c:macro:`LV_CONF_SKIP` is defined, LVGL will not try to include ``lv_conf.h``. Instead you can pass the config defines using build -options. For example ``"-DLV_COLOR_DEPTH=32 -DLV_USE_BTN=1"``. The unset -options will get a default value which is the same as the ones in +options. For example ``"-DLV_COLOR_DEPTH=32 -DLV_USE_BUTTON=1"``. The unset +options will get a default value which is the same as the content of ``lv_conf_template.h``. LVGL also can be used via ``Kconfig`` and ``menuconfig``. You can use -``lv_conf.h`` together with Kconfig, but keep in mind that the value +``lv_conf.h`` together with Kconfig too, but keep in mind that the value from ``lv_conf.h`` or build settings (``-D...``) overwrite the values set in Kconfig. To ignore the configs from ``lv_conf.h`` simply remove its content, or define :c:macro:`LV_CONF_SKIP`. @@ -107,6 +107,8 @@ its content, or define :c:macro:`LV_CONF_SKIP`. To enable multi-instance feature, set :c:macro:`LV_GLOBAL_CUSTOM` in ``lv_conf.h`` and provide a custom function to :cpp:func:`lv_global_default` using ``__thread`` or ``pthread_key_t``. +It will allow running multiple LVGL instances by storing the global variables +in TLS (Thread Local Storage). For example: @@ -125,7 +127,7 @@ Initialization To use the graphics library you have to initialize it and setup required components. The order of the initialization is: -1. Call :cpp:func:`lv_init`. +1. Call :cpp:func:`lv_init()`. 2. Initialize your drivers. 3. Register the display and input devices drivers in LVGL. Learn more about `Display `__ and `Input diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index b8a867657..794d9e2dd 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -420,7 +420,7 @@ refr_finish: _lv_draw_sw_mask_cleanup(); #endif - lv_display_send_event(disp_refr, LV_EVENT_REFR_FINISH, NULL); + lv_display_send_event(disp_refr, LV_EVENT_REFR_READY, NULL); LV_TRACE_REFR("finished"); LV_PROFILER_END; @@ -1006,14 +1006,7 @@ static void draw_buf_flush(lv_display_t * disp) bool flushing_last = disp->flushing_last; if(disp->flush_cb) { - /*Rotate the buffer to the display's native orientation if necessary*/ - if(disp->rotation != LV_DISPLAY_ROTATION_0 && disp->sw_rotate) { - LV_LOG_WARN("SW rotation is not supported now"); - call_flush_cb(disp, &disp->refreshed_area, layer->buf); - } - else { - call_flush_cb(disp, &disp->refreshed_area, layer->buf); - } + call_flush_cb(disp, &disp->refreshed_area, layer->buf); } /*If there are 2 buffers swap them. With direct mode swap only on the last area*/ if(lv_display_is_double_buffered(disp) && (disp->render_mode != LV_DISPLAY_RENDER_MODE_DIRECT || flushing_last)) { diff --git a/src/display/lv_display.c b/src/display/lv_display.c index 2262d24bf..af02d9a02 100644 --- a/src/display/lv_display.c +++ b/src/display/lv_display.c @@ -720,13 +720,12 @@ lv_result_t lv_display_send_event(lv_display_t * disp, lv_event_code_t code, voi return res; } -void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation, bool sw_rotate) +void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation) { if(disp == NULL) disp = lv_display_get_default(); if(disp == NULL) return; disp->rotation = rotation; - disp->sw_rotate = sw_rotate; update_resolution(disp); } diff --git a/src/display/lv_display.h b/src/display/lv_display.h index d8b470601..d092a167a 100644 --- a/src/display/lv_display.h +++ b/src/display/lv_display.h @@ -159,10 +159,8 @@ void lv_display_set_offset(lv_display_t * disp, int32_t x, int32_t y); * Set the rotation of this display. LVGL will swap the horizontal and vertical resolutions internally. * @param disp pointer to a display (NULL to use the default display) * @param rotation `LV_DISPLAY_ROTATION_0/90/180/270` - * @param sw_rotate true: make LVGL rotate the rendered image; - * false: the display driver should rotate the rendered image */ -void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation, bool sw_rotate); +void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation); /** * Set the DPI (dot per inch) of the display. diff --git a/src/misc/lv_event.h b/src/misc/lv_event.h index f954851ce..563fed5e4 100644 --- a/src/misc/lv_event.h +++ b/src/misc/lv_event.h @@ -97,12 +97,12 @@ typedef enum { /** Events of optional LVGL components*/ LV_EVENT_INVALIDATE_AREA, - LV_EVENT_RENDER_START, - LV_EVENT_RENDER_READY, LV_EVENT_RESOLUTION_CHANGED, LV_EVENT_REFR_REQUEST, LV_EVENT_REFR_START, - LV_EVENT_REFR_FINISH, + LV_EVENT_REFR_READY, + LV_EVENT_RENDER_START, + LV_EVENT_RENDER_READY, LV_EVENT_FLUSH_START, LV_EVENT_FLUSH_FINISH, diff --git a/src/others/sysmon/lv_sysmon.c b/src/others/sysmon/lv_sysmon.c index 3859653a8..9dbdc2fd0 100644 --- a/src/others/sysmon/lv_sysmon.c +++ b/src/others/sysmon/lv_sysmon.c @@ -116,7 +116,7 @@ static void perf_monitor_disp_event_cb(lv_event_t * e) info->measured.refr_interval_sum += lv_tick_elaps(info->measured.refr_start); info->measured.refr_start = lv_tick_get(); break; - case LV_EVENT_REFR_FINISH: + case LV_EVENT_REFR_READY: info->measured.refr_elaps_sum += lv_tick_elaps(info->measured.refr_start); info->measured.refr_cnt++; break;