mirror of
https://github.com/lvgl/lvgl.git
synced 2026-02-05 13:30:08 +08:00
feat(draw): add nanovg rendering backend (#8865)
Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
36
Kconfig
36
Kconfig
@@ -568,6 +568,38 @@ menu "LVGL configuration"
|
||||
default 2048
|
||||
depends on LV_USE_DRAW_EVE
|
||||
|
||||
config LV_USE_DRAW_NANOVG
|
||||
bool "Use NanoVG Renderer."
|
||||
default n
|
||||
depends on LV_USE_NANOVG && LV_USE_MATRIX
|
||||
|
||||
choice LV_NANOVG_BACKEND
|
||||
prompt "NanoVG OpenGL backend"
|
||||
default LV_NANOVG_BACKEND_GLES2
|
||||
depends on LV_USE_DRAW_NANOVG
|
||||
help
|
||||
Select which OpenGL implementation to use for NanoVG rendering.
|
||||
|
||||
config LV_NANOVG_BACKEND_GL2
|
||||
bool "OpenGL 2.0"
|
||||
config LV_NANOVG_BACKEND_GL3
|
||||
bool "OpenGL 3.0+"
|
||||
config LV_NANOVG_BACKEND_GLES2
|
||||
bool "OpenGL ES 2.0"
|
||||
config LV_NANOVG_BACKEND_GLES3
|
||||
bool "OpenGL ES 3.0+"
|
||||
endchoice
|
||||
|
||||
config LV_NANOVG_IMAGE_CACHE_CNT
|
||||
int "Draw image texture cache count"
|
||||
default 128
|
||||
depends on LV_USE_DRAW_NANOVG
|
||||
|
||||
config LV_NANOVG_LETTER_CACHE_CNT
|
||||
int "Draw letter cache count"
|
||||
default 512
|
||||
depends on LV_USE_DRAW_NANOVG
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Feature Configuration"
|
||||
@@ -1545,6 +1577,10 @@ menu "LVGL configuration"
|
||||
bool "Use ThorVG external"
|
||||
endchoice
|
||||
|
||||
config LV_USE_NANOVG
|
||||
bool "NanoVG library"
|
||||
default n
|
||||
|
||||
config LV_USE_LZ4
|
||||
bool "Enable LZ4 compress/decompress lib"
|
||||
choice
|
||||
|
||||
@@ -9,5 +9,6 @@ Running under Embedded Linux
|
||||
|
||||
overview
|
||||
opengl
|
||||
nanovg
|
||||
os/index
|
||||
drivers/index
|
||||
|
||||
131
docs/src/integration/embedded_linux/nanovg.rst
Normal file
131
docs/src/integration/embedded_linux/nanovg.rst
Normal file
@@ -0,0 +1,131 @@
|
||||
.. _nanovg_draw_unit:
|
||||
|
||||
================
|
||||
NanoVG Draw Unit
|
||||
================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
NanoVG is a lightweight, antialiased 2D vector graphics library built on top of OpenGL/OpenGL ES.
|
||||
The NanoVG draw unit integrates NanoVG as a hardware-accelerated rendering backend for LVGL,
|
||||
providing GPU-accelerated drawing for all standard LVGL widgets and graphics primitives.
|
||||
|
||||
Unlike the software renderer, NanoVG leverages the GPU for:
|
||||
|
||||
- Antialiased path rendering (rectangles, arcs, lines, triangles)
|
||||
- Hardware-accelerated image compositing with rotation and scaling
|
||||
- Efficient text rendering with font texture caching
|
||||
- Box shadows and gradients
|
||||
- Vector graphics support
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
- OpenGL 2.0+ / OpenGL ES 2.0+ / OpenGL ES 3.0+
|
||||
- An initialized OpenGL context (via GLFW, EGL, or custom setup)
|
||||
- Stencil buffer support (8-bit recommended)
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Enable the NanoVG draw unit in ``lv_conf.h``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Enable NanoVG library */
|
||||
#define LV_USE_NANOVG 1
|
||||
|
||||
/* Enable NanoVG draw unit */
|
||||
#define LV_USE_DRAW_NANOVG 1
|
||||
|
||||
/* Select OpenGL backend (choose one):
|
||||
* - LV_NANOVG_BACKEND_GL2: OpenGL 2.0
|
||||
* - LV_NANOVG_BACKEND_GL3: OpenGL 3.0+
|
||||
* - LV_NANOVG_BACKEND_GLES2: OpenGL ES 2.0
|
||||
* - LV_NANOVG_BACKEND_GLES3: OpenGL ES 3.0+
|
||||
*/
|
||||
#define LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES2
|
||||
|
||||
/* Optional: Adjust cache sizes */
|
||||
#define LV_NANOVG_IMAGE_CACHE_CNT 32 /* Image texture cache entries */
|
||||
#define LV_NANOVG_FBO_CACHE_CNT 8 /* Framebuffer object cache entries */
|
||||
|
||||
Supported Features
|
||||
==================
|
||||
|
||||
The NanoVG draw unit supports all standard LVGL drawing operations:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 30 70
|
||||
|
||||
* - Feature
|
||||
- Description
|
||||
* - Fill
|
||||
- Solid colors, gradients (linear/radial)
|
||||
* - Border
|
||||
- Rounded rectangles with customizable width
|
||||
* - Box Shadow
|
||||
- Hardware-accelerated shadow rendering
|
||||
* - Images
|
||||
- Rotation, scaling, tiling, recoloring
|
||||
* - Labels
|
||||
- Font rendering with texture atlas caching
|
||||
* - Lines
|
||||
- Antialiased lines with configurable width
|
||||
* - Arcs
|
||||
- Antialiased arc segments
|
||||
* - Triangles
|
||||
- Filled triangles
|
||||
* - Masks
|
||||
- Rectangle masks for clipping
|
||||
* - Layers
|
||||
- Off-screen rendering with FBO
|
||||
* - Canvas
|
||||
- Direct drawing to canvas buffers
|
||||
* - Vector Graphics
|
||||
- SVG-style path rendering (requires ``LV_USE_VECTOR_GRAPHIC``)
|
||||
|
||||
Supported Image Formats
|
||||
=======================
|
||||
|
||||
NanoVG supports zero-copy texture upload for these LVGL color formats:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 25 25 50
|
||||
|
||||
* - LVGL Format
|
||||
- GL Processing
|
||||
- Notes
|
||||
* - ``LV_COLOR_FORMAT_A8``
|
||||
- Alpha texture
|
||||
- Color tinting via shader
|
||||
* - ``LV_COLOR_FORMAT_ARGB8888``
|
||||
- BGR→RGB swizzle
|
||||
- Premultiplication handled in shader
|
||||
* - ``LV_COLOR_FORMAT_XRGB8888``
|
||||
- BGR→RGB + alpha=1
|
||||
- X channel ignored
|
||||
* - ``LV_COLOR_FORMAT_RGB888``
|
||||
- BGR→RGB swizzle
|
||||
- No alpha channel
|
||||
* - ``LV_COLOR_FORMAT_RGB565``
|
||||
- Direct upload
|
||||
- Note: LVGL uses BGR565 layout
|
||||
|
||||
Performance Tips
|
||||
================
|
||||
|
||||
1. **Minimize Layer Usage**: Each layer requires a framebuffer object (FBO) switch
|
||||
2. **Use Premultiplied Alpha**: Set ``LV_IMAGE_FLAGS_PREMULTIPLIED`` for pre-processed images
|
||||
3. **Cache Static Content**: NanoVG caches textures automatically; avoid recreating images
|
||||
4. **Batch Similar Operations**: Group widgets with similar styles for better GPU batching
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
- **Blur**: Not natively supported; Using this style will not affect the rendering results.
|
||||
- **Complex Gradients**: Limited to 2-color gradients (LVGL supports multi-stop)
|
||||
- **Layer Readback**: ``glReadPixels`` for canvas/layer is relatively slow
|
||||
@@ -79,3 +79,12 @@ animations, and interactive camera controls for embedded 3D visualization.
|
||||
|
||||
For complete implementation details, see :ref:`glTF <gltf>`.
|
||||
|
||||
NanoVG Draw Unit
|
||||
================
|
||||
|
||||
The NanoVG draw unit provides a hardware-accelerated 2D vector graphics rendering backend for LVGL.
|
||||
It leverages GPU capabilities for antialiased path rendering, efficient image compositing, and text rendering.
|
||||
|
||||
For complete implementation details, see :ref:`NanoVG Draw Unit <nanovg_draw_unit>`.
|
||||
|
||||
|
||||
|
||||
@@ -410,6 +410,26 @@
|
||||
#define LV_DRAW_EVE_WRITE_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
/** Use NanoVG Renderer
|
||||
* - Requires LV_USE_NANOVG, LV_USE_MATRIX.
|
||||
*/
|
||||
#define LV_USE_DRAW_NANOVG 0
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
/** Select OpenGL backend for NanoVG:
|
||||
* - LV_NANOVG_BACKEND_GL2: OpenGL 2.0
|
||||
* - LV_NANOVG_BACKEND_GL3: OpenGL 3.0+
|
||||
* - LV_NANOVG_BACKEND_GLES2: OpenGL ES 2.0
|
||||
* - LV_NANOVG_BACKEND_GLES3: OpenGL ES 3.0+
|
||||
*/
|
||||
#define LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES2
|
||||
|
||||
/** Draw image texture cache count. */
|
||||
#define LV_NANOVG_IMAGE_CACHE_CNT 128
|
||||
|
||||
/** Draw letter texture cache count. */
|
||||
#define LV_NANOVG_LETTER_CACHE_CNT 512
|
||||
#endif
|
||||
|
||||
/*=======================
|
||||
* FEATURE CONFIGURATION
|
||||
*=======================*/
|
||||
@@ -1022,6 +1042,9 @@
|
||||
* Requires LV_USE_VECTOR_GRAPHIC */
|
||||
#define LV_USE_THORVG_EXTERNAL 0
|
||||
|
||||
/** Enable NanoVG (vector graphics library) */
|
||||
#define LV_USE_NANOVG 0
|
||||
|
||||
/** Use lvgl built-in LZ4 lib */
|
||||
#define LV_USE_LZ4_INTERNAL 0
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ fout.write(
|
||||
#define LV_NEMA_HAL_CUSTOM 0
|
||||
#define LV_NEMA_HAL_STM32 1
|
||||
|
||||
#define LV_NANOVG_BACKEND_GL2 1
|
||||
#define LV_NANOVG_BACKEND_GL3 2
|
||||
#define LV_NANOVG_BACKEND_GLES2 3
|
||||
#define LV_NANOVG_BACKEND_GLES3 4
|
||||
|
||||
/** Handle special Kconfig options. */
|
||||
#ifndef LV_KCONFIG_IGNORE
|
||||
#include "lv_conf_kconfig.h"
|
||||
|
||||
@@ -951,6 +951,7 @@ static void refr_area(const lv_area_t * area_p, int32_t y_offset)
|
||||
layer_i = layer_i->next;
|
||||
}
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, tile_layer);
|
||||
if(disp_refr->layer_deinit) disp_refr->layer_deinit(disp_refr, tile_layer);
|
||||
}
|
||||
lv_free(tile_layers);
|
||||
|
||||
@@ -462,6 +462,7 @@ void lv_draw_layer_init(lv_layer_t * layer, lv_layer_t * parent_layer, lv_color_
|
||||
layer->color_format = color_format;
|
||||
|
||||
if(disp->layer_init) disp->layer_init(disp, layer);
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_CREATED, layer);
|
||||
|
||||
if(disp->layer_head) {
|
||||
lv_layer_t * tail = disp->layer_head;
|
||||
@@ -669,6 +670,7 @@ static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp)
|
||||
l2 = l2->next;
|
||||
}
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, layer_drawn);
|
||||
if(disp->layer_deinit) {
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("layer_deinit");
|
||||
disp->layer_deinit(disp, layer_drawn);
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
|
||||
#if !((LV_USE_DRAW_SW && LV_USE_THORVG) || LV_USE_DRAW_VG_LITE || (LV_USE_NEMA_GFX && LV_USE_NEMA_VG))
|
||||
#error "LV_USE_VECTOR_GRAPHIC requires (LV_USE_DRAW_SW and LV_USE_THORVG) or LV_USE_DRAW_VG_LITE or (LV_USE_NEMA_GFX and LV_USE_NEMA_VG)"
|
||||
#if !((LV_USE_DRAW_SW && LV_USE_THORVG) || LV_USE_DRAW_VG_LITE || (LV_USE_NEMA_GFX && LV_USE_NEMA_VG) || LV_USE_DRAW_NANOVG)
|
||||
#error "LV_USE_VECTOR_GRAPHIC requires (LV_USE_DRAW_SW and LV_USE_THORVG) or LV_USE_DRAW_VG_LITE or (LV_USE_NEMA_GFX and LV_USE_NEMA_VG) or LV_USE_DRAW_NANOVG"
|
||||
#endif
|
||||
|
||||
#include "../misc/lv_ll.h"
|
||||
|
||||
464
src/draw/nanovg/lv_draw_nanovg.c
Normal file
464
src/draw/nanovg/lv_draw_nanovg.c
Normal file
@@ -0,0 +1,464 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "../../display/lv_display.h"
|
||||
#include "../../core/lv_refr_private.h"
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
#include "lv_nanovg_fbo_cache.h"
|
||||
|
||||
#if LV_USE_OPENGLES && LV_USE_EGL
|
||||
#include "../../drivers/opengles/lv_opengles_private.h"
|
||||
#else
|
||||
#define NANOVG_GL_STATIC_LINK
|
||||
#endif
|
||||
|
||||
#if defined(NANOVG_GL2_IMPLEMENTATION)
|
||||
#ifdef NANOVG_GL_STATIC_LINK
|
||||
#include <GL/glew.h>
|
||||
#endif
|
||||
#define NVG_CTX_CREATE nvgCreateGL2
|
||||
#define NVG_CTX_DELETE nvgDeleteGL2
|
||||
#elif defined(NANOVG_GL3_IMPLEMENTATION)
|
||||
#ifdef NANOVG_GL_STATIC_LINK
|
||||
#include <GL/glew.h>
|
||||
#endif
|
||||
#define NVG_CTX_CREATE nvgCreateGL3
|
||||
#define NVG_CTX_DELETE nvgDeleteGL3
|
||||
#elif defined(NANOVG_GLES2_IMPLEMENTATION)
|
||||
#ifdef NANOVG_GL_STATIC_LINK
|
||||
#include <GLES2/gl2.h>
|
||||
#endif
|
||||
#define NVG_CTX_CREATE nvgCreateGLES2
|
||||
#define NVG_CTX_DELETE nvgDeleteGLES2
|
||||
#elif defined(NANOVG_GLES3_IMPLEMENTATION)
|
||||
#ifdef NANOVG_GL_STATIC_LINK
|
||||
#include <GLES3/gl3.h>
|
||||
#endif
|
||||
#define NVG_CTX_CREATE nvgCreateGLES3
|
||||
#define NVG_CTX_DELETE nvgDeleteGLES3
|
||||
#else
|
||||
#error "No NanoVG implementation defined"
|
||||
#endif
|
||||
|
||||
#include "../../libs/nanovg/nanovg_gl.h"
|
||||
#include "../../libs/nanovg/nanovg_gl_utils.h"
|
||||
|
||||
/* GL_BGRA may not be defined on all platforms */
|
||||
#ifndef GL_BGRA
|
||||
#ifdef GL_BGRA_EXT
|
||||
#define GL_BGRA GL_BGRA_EXT
|
||||
#else
|
||||
#define GL_BGRA 0x80E1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define NANOVG_DRAW_UNIT_ID 10
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static int32_t draw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
|
||||
static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
|
||||
static int32_t draw_delete(lv_draw_unit_t * draw_unit);
|
||||
static void draw_event_cb(lv_event_t * e);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_init(void)
|
||||
{
|
||||
lv_display_render_mode_t mode = lv_display_get_render_mode(NULL);
|
||||
if(mode != LV_DISPLAY_RENDER_MODE_FULL) {
|
||||
LV_LOG_ERROR("Detect render mode(%d) is not FULL. The rendering result may be incorrect.", mode);
|
||||
}
|
||||
|
||||
static bool initialized = false;
|
||||
if(initialized) return;
|
||||
initialized = true;
|
||||
|
||||
lv_draw_nanovg_unit_t * unit = lv_draw_create_unit(sizeof(lv_draw_nanovg_unit_t));
|
||||
unit->base_unit.dispatch_cb = draw_dispatch;
|
||||
unit->base_unit.evaluate_cb = draw_evaluate;
|
||||
unit->base_unit.delete_cb = draw_delete;
|
||||
unit->base_unit.event_cb = draw_event_cb;
|
||||
unit->base_unit.name = "NANOVG";
|
||||
|
||||
unit->vg = NVG_CTX_CREATE(0);
|
||||
LV_ASSERT_MSG(unit->vg != NULL, "NanoVG init failed");
|
||||
|
||||
lv_nanovg_utils_init(unit);
|
||||
lv_nanovg_image_cache_init(unit);
|
||||
lv_nanovg_fbo_cache_init(unit);
|
||||
lv_draw_nanovg_label_init(unit);
|
||||
}
|
||||
|
||||
int lv_nanovg_fb_get_image_handle(struct NVGLUframebuffer * fb)
|
||||
{
|
||||
LV_ASSERT_NULL(fb);
|
||||
return fb->image;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void draw_execute(lv_draw_nanovg_unit_t * u, lv_draw_task_t * t)
|
||||
{
|
||||
/* remember draw unit for access to unit's context */
|
||||
t->draw_unit = (lv_draw_unit_t *)u;
|
||||
lv_layer_t * layer = t->target_layer;
|
||||
|
||||
lv_matrix_t global_matrix;
|
||||
lv_matrix_identity(&global_matrix);
|
||||
if(layer->buf_area.x1 || layer->buf_area.y1) {
|
||||
lv_matrix_translate(&global_matrix, -layer->buf_area.x1, -layer->buf_area.y1);
|
||||
}
|
||||
|
||||
#if LV_DRAW_TRANSFORM_USE_MATRIX
|
||||
lv_matrix_t layer_matrix = t->matrix;
|
||||
lv_matrix_multiply(&global_matrix, &layer_matrix);
|
||||
#endif
|
||||
|
||||
/* NanoVG will output premultiplied image, set the flag correspondingly. */
|
||||
if(layer->draw_buf) {
|
||||
lv_draw_buf_set_flag(layer->draw_buf, LV_IMAGE_FLAGS_PREMULTIPLIED);
|
||||
}
|
||||
|
||||
nvgReset(u->vg);
|
||||
lv_nanovg_transform(u->vg, &global_matrix);
|
||||
|
||||
lv_nanovg_set_clip_area(u->vg, &t->clip_area);
|
||||
|
||||
switch(t->type) {
|
||||
case LV_DRAW_TASK_TYPE_FILL:
|
||||
lv_draw_nanovg_fill(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_BORDER:
|
||||
lv_draw_nanovg_border(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
|
||||
lv_draw_nanovg_box_shadow(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_LETTER:
|
||||
lv_draw_nanovg_letter(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_LABEL:
|
||||
lv_draw_nanovg_label(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_IMAGE:
|
||||
lv_draw_nanovg_image(t, t->draw_dsc, &t->area, -1);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_LAYER:
|
||||
lv_draw_nanovg_layer(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_LINE:
|
||||
lv_draw_nanovg_line(t, t->draw_dsc);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_ARC:
|
||||
lv_draw_nanovg_arc(t, t->draw_dsc, &t->area);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_TRIANGLE:
|
||||
lv_draw_nanovg_triangle(t, t->draw_dsc);
|
||||
break;
|
||||
|
||||
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
|
||||
lv_draw_nanovg_mask_rect(t, t->draw_dsc);
|
||||
break;
|
||||
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
case LV_DRAW_TASK_TYPE_VECTOR:
|
||||
lv_draw_nanovg_vector(t, t->draw_dsc);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LV_LOG_ERROR("unknown draw task type: %d", t->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_layer_changed(lv_layer_t * new_layer)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
if(!new_layer->user_data) {
|
||||
/* Bind the default framebuffer for normal rendering */
|
||||
nvgluBindFramebuffer(NULL);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
LV_PROFILER_BEGIN_TAG("nvgBindFramebuffer");
|
||||
nvgluBindFramebuffer(lv_nanovg_fbo_cache_entry_to_fb(new_layer->user_data));
|
||||
LV_PROFILER_END_TAG("nvgBindFramebuffer");
|
||||
|
||||
/* Clear the off-screen framebuffer */
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("glClear");
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
LV_PROFILER_DRAW_END_TAG("glClear");
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static void on_layer_readback(lv_draw_nanovg_unit_t * u, lv_layer_t * layer)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT_NULL(layer);
|
||||
|
||||
lv_cache_entry_t * entry = layer->user_data;
|
||||
|
||||
if(!entry) {
|
||||
LV_LOG_WARN("No entry available for layer: %p", (void *)layer);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!layer->draw_buf) {
|
||||
LV_LOG_WARN("No draw buffer available for layer: %p", (void *)layer);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
struct NVGLUframebuffer * fb = lv_nanovg_fbo_cache_entry_to_fb(entry);
|
||||
if(!fb) {
|
||||
LV_LOG_ERROR("No framebuffer available for layer: %p", (void *)layer);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Bind the FBO for reading */
|
||||
nvgluBindFramebuffer(fb);
|
||||
|
||||
int32_t w = lv_area_get_width(&layer->buf_area);
|
||||
int32_t h = lv_area_get_height(&layer->buf_area);
|
||||
lv_draw_buf_t * draw_buf = layer->draw_buf;
|
||||
|
||||
/* Read pixels from FBO */
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
|
||||
/* OpenGL reads bottom-to-top, but LVGL expects top-to-bottom */
|
||||
switch(draw_buf->header.cf) {
|
||||
case LV_COLOR_FORMAT_ARGB8888:
|
||||
case LV_COLOR_FORMAT_XRGB8888:
|
||||
case LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED:
|
||||
format = GL_BGRA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_RGB888:
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_RGB565:
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_LOG_WARN("Unsupported color format: %d", draw_buf->header.cf);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
for(int32_t y = 0; y < h; y++) {
|
||||
/* Reverse Y coordinate */
|
||||
void * row = lv_draw_buf_goto_xy(draw_buf, 0, h - 1 - y);
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("glReadPixels");
|
||||
glReadPixels(0, y, w, 1, format, type, row);
|
||||
LV_PROFILER_DRAW_END_TAG("glReadPixels");
|
||||
|
||||
if(draw_buf->header.cf == LV_COLOR_FORMAT_RGB888) {
|
||||
/* Swizzle RGB -> BGR */
|
||||
lv_color_t * px = row;
|
||||
for(int32_t x = 0; x < w; x++) {
|
||||
uint8_t r = px->blue;
|
||||
px->blue = px->red;
|
||||
px->red = r;
|
||||
px++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind back to default framebuffer */
|
||||
nvgluBindFramebuffer(NULL);
|
||||
|
||||
/* Mark draw_buf as modified */
|
||||
lv_draw_buf_flush_cache(draw_buf, NULL);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static int32_t draw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
|
||||
{
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)draw_unit;
|
||||
|
||||
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, NANOVG_DRAW_UNIT_ID);
|
||||
if(!t || t->preferred_draw_unit_id != NANOVG_DRAW_UNIT_ID) {
|
||||
lv_nanovg_end_frame(u);
|
||||
return LV_DRAW_UNIT_IDLE;
|
||||
}
|
||||
|
||||
if(u->current_layer != layer) {
|
||||
on_layer_changed(layer);
|
||||
u->current_layer = layer;
|
||||
}
|
||||
|
||||
if(!u->is_started) {
|
||||
const int32_t buf_w = lv_area_get_width(&layer->buf_area);
|
||||
const int32_t buf_h = lv_area_get_height(&layer->buf_area);
|
||||
|
||||
glViewport(0, 0, buf_w, buf_h);
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("nvgBeginFrame");
|
||||
nvgBeginFrame(u->vg, buf_w, buf_h, 1.0f);
|
||||
LV_PROFILER_DRAW_END_TAG("nvgBeginFrame");
|
||||
u->is_started = true;
|
||||
}
|
||||
|
||||
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
|
||||
|
||||
draw_execute(u, t);
|
||||
|
||||
t->state = LV_DRAW_TASK_STATE_FINISHED;
|
||||
|
||||
/*The draw unit is free now. Request a new dispatching as it can get a new task*/
|
||||
lv_draw_dispatch_request();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
|
||||
{
|
||||
LV_UNUSED(draw_unit);
|
||||
|
||||
switch(task->type) {
|
||||
case LV_DRAW_TASK_TYPE_FILL:
|
||||
case LV_DRAW_TASK_TYPE_BORDER:
|
||||
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
|
||||
case LV_DRAW_TASK_TYPE_LETTER:
|
||||
case LV_DRAW_TASK_TYPE_LABEL:
|
||||
case LV_DRAW_TASK_TYPE_IMAGE:
|
||||
case LV_DRAW_TASK_TYPE_LAYER:
|
||||
case LV_DRAW_TASK_TYPE_LINE:
|
||||
case LV_DRAW_TASK_TYPE_ARC:
|
||||
case LV_DRAW_TASK_TYPE_TRIANGLE:
|
||||
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
case LV_DRAW_TASK_TYPE_VECTOR:
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
/*The draw unit is not able to draw this task. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(task->preference_score > 80) {
|
||||
/* The draw unit is able to draw this task. */
|
||||
task->preference_score = 80;
|
||||
task->preferred_draw_unit_id = NANOVG_DRAW_UNIT_ID;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int32_t draw_delete(lv_draw_unit_t * draw_unit)
|
||||
{
|
||||
lv_draw_nanovg_unit_t * unit = (lv_draw_nanovg_unit_t *)draw_unit;
|
||||
lv_draw_nanovg_label_deinit(unit);
|
||||
lv_nanovg_fbo_cache_deinit(unit);
|
||||
lv_nanovg_image_cache_deinit(unit);
|
||||
lv_nanovg_utils_deinit(unit);
|
||||
NVG_CTX_DELETE(unit->vg);
|
||||
unit->vg = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void draw_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_draw_nanovg_unit_t * u = lv_event_get_current_target(e);
|
||||
lv_layer_t * layer = lv_event_get_param(e);
|
||||
|
||||
switch(lv_event_get_code(e)) {
|
||||
case LV_EVENT_CANCEL:
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("nvgCancelFrame");
|
||||
nvgCancelFrame(u->vg);
|
||||
LV_PROFILER_DRAW_END_TAG("nvgCancelFrame");
|
||||
lv_nanovg_clean_up(u);
|
||||
break;
|
||||
case LV_EVENT_CHILD_CREATED: {
|
||||
/* The internal rendering uses RGBA format, which is switched to LVGL BGRA format during readback. */
|
||||
lv_cache_entry_t * entry = lv_nanovg_fbo_cache_get(u, lv_area_get_width(&layer->buf_area),
|
||||
lv_area_get_height(&layer->buf_area), 0, NVG_TEXTURE_RGBA);
|
||||
layer->user_data = entry;
|
||||
}
|
||||
break;
|
||||
case LV_EVENT_CHILD_DELETED: {
|
||||
lv_cache_entry_t * entry = layer->user_data;
|
||||
if(entry) {
|
||||
lv_nanovg_fbo_cache_release(u, entry);
|
||||
layer->user_data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current_layer if it's being deleted, so next dispatch
|
||||
* will properly call on_layer_changed even if layer address is reused
|
||||
*/
|
||||
if(u->current_layer == layer) {
|
||||
u->current_layer = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LV_EVENT_SCREEN_LOAD_START:
|
||||
on_layer_readback(u, layer);
|
||||
break;
|
||||
case LV_EVENT_INVALIDATE_AREA:
|
||||
lv_nanovg_image_cache_drop(u, lv_event_get_param(e));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
48
src/draw/nanovg/lv_draw_nanovg.h
Normal file
48
src/draw/nanovg/lv_draw_nanovg.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_DRAW_NANOVG_H
|
||||
#define LV_DRAW_NANOVG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../misc/lv_types.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize NanoVG rendering
|
||||
*/
|
||||
void lv_draw_nanovg_init(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_DRAW_NANOVG_H*/
|
||||
177
src/draw/nanovg/lv_draw_nanovg_arc.c
Normal file
177
src/draw/nanovg/lv_draw_nanovg_arc.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_arc.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_math.h"
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, coords, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
float start_angle = dsc->start_angle;
|
||||
float end_angle = dsc->end_angle;
|
||||
float sweep_angle = end_angle - start_angle;
|
||||
|
||||
while(sweep_angle < 0) {
|
||||
sweep_angle += 360;
|
||||
}
|
||||
|
||||
while(sweep_angle > 360) {
|
||||
sweep_angle -= 360;
|
||||
}
|
||||
|
||||
/*If the angles are the same then there is nothing to draw*/
|
||||
if(nvg_math_is_zero(sweep_angle)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
|
||||
float radius_out = dsc->radius;
|
||||
float radius_in = dsc->radius - dsc->width;
|
||||
float cx = dsc->center.x;
|
||||
float cy = dsc->center.y;
|
||||
|
||||
enum NVGwinding winding = NVG_CCW;
|
||||
|
||||
if(nvg_math_is_equal(sweep_angle, 360)) {
|
||||
nvgCircle(u->vg, cx, cy, radius_out);
|
||||
|
||||
/* radius_in <= 0, normal fill circle */
|
||||
if(radius_in > 0) {
|
||||
nvgCircle(u->vg, cx, cy, radius_in);
|
||||
}
|
||||
winding = NVG_CW;
|
||||
}
|
||||
else {
|
||||
float start_angle_rad = NVG_MATH_RADIANS(start_angle);
|
||||
float end_angle_rad = NVG_MATH_RADIANS(end_angle);
|
||||
|
||||
if(radius_in > 0) {
|
||||
/* radius_out start point */
|
||||
float start_x = radius_out * NVG_MATH_COSF(start_angle_rad) + cx;
|
||||
float start_y = radius_out * NVG_MATH_SINF(start_angle_rad) + cy;
|
||||
|
||||
/* radius_in start point */
|
||||
float end_x = radius_in * NVG_MATH_COSF(end_angle_rad) + cx;
|
||||
float end_y = radius_in * NVG_MATH_SINF(end_angle_rad) + cy;
|
||||
|
||||
nvgMoveTo(u->vg, start_x, start_y);
|
||||
|
||||
/* radius_out arc */
|
||||
lv_nanovg_path_append_arc(u->vg,
|
||||
cx, cy,
|
||||
radius_out,
|
||||
start_angle,
|
||||
sweep_angle,
|
||||
false);
|
||||
|
||||
/* line to radius_in */
|
||||
nvgLineTo(u->vg, end_x, end_y);
|
||||
|
||||
/* radius_in arc */
|
||||
lv_nanovg_path_append_arc(u->vg,
|
||||
cx, cy,
|
||||
radius_in,
|
||||
end_angle,
|
||||
-sweep_angle,
|
||||
false);
|
||||
|
||||
/* close arc */
|
||||
nvgClosePath(u->vg);
|
||||
}
|
||||
else {
|
||||
/* draw a normal arc pie shape */
|
||||
lv_nanovg_path_append_arc(u->vg, cx, cy, radius_out, start_angle, sweep_angle, true);
|
||||
}
|
||||
|
||||
/* draw round */
|
||||
if(dsc->rounded && dsc->width > 0) {
|
||||
float round_radius = radius_out > dsc->width ? dsc->width / 2.0f : radius_out / 2.0f;
|
||||
float round_center = radius_out - round_radius;
|
||||
float rcx1 = cx + round_center * NVG_MATH_COSF(end_angle_rad);
|
||||
float rcy1 = cy + round_center * NVG_MATH_SINF(end_angle_rad);
|
||||
nvgCircle(u->vg, rcx1, rcy1, round_radius);
|
||||
|
||||
float rcx2 = cx + round_center * NVG_MATH_COSF(start_angle_rad);
|
||||
float rcy2 = cy + round_center * NVG_MATH_SINF(start_angle_rad);
|
||||
nvgCircle(u->vg, rcx2, rcy2, round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
if(dsc->img_src) {
|
||||
lv_image_header_t header;
|
||||
int image_handle = lv_nanovg_image_cache_get_handle(u, dsc->img_src, lv_color32_make(0, 0, 0, 0), 0, &header);
|
||||
if(image_handle < 0) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
/* move image to center */
|
||||
float img_half_w = header.w / 2.0f;
|
||||
float img_half_h = header.h / 2.0f;
|
||||
|
||||
NVGpaint paint = nvgImagePattern(u->vg,
|
||||
cx - img_half_w, cy - img_half_h,
|
||||
header.w, header.h, 0,
|
||||
image_handle,
|
||||
dsc->opa / (float)LV_OPA_COVER);
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
}
|
||||
else {
|
||||
lv_nanovg_fill(u->vg, winding, NVG_SOURCE_OVER, lv_nanovg_color_convert(dsc->color, dsc->opa));
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
383
src/draw/nanovg/lv_draw_nanovg_border.c
Normal file
383
src/draw/nanovg/lv_draw_nanovg_border.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_border.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_math.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define HAS_BORDER_SIDE(dsc_side, side) (((dsc_side) & (side)) == (side))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static enum NVGwinding path_append_inner_rect(NVGcontext * ctx,
|
||||
const lv_draw_border_dsc_t * dsc,
|
||||
int32_t x, int32_t y, int32_t w, int32_t h,
|
||||
float r);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, coords, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t w = lv_area_get_width(coords);
|
||||
int32_t h = lv_area_get_height(coords);
|
||||
float r_out = dsc->radius;
|
||||
if(dsc->radius) {
|
||||
float r_short = LV_MIN(w, h) / 2.0f;
|
||||
r_out = LV_MIN(r_out, r_short);
|
||||
}
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
|
||||
/* outer rect */
|
||||
lv_nanovg_path_append_rect(u->vg,
|
||||
coords->x1, coords->y1,
|
||||
w, h,
|
||||
r_out);
|
||||
|
||||
/* inner rect */
|
||||
enum NVGwinding winding = path_append_inner_rect(u->vg, dsc, coords->x1, coords->y1, w, h, r_out);
|
||||
|
||||
lv_nanovg_fill(
|
||||
u->vg,
|
||||
winding,
|
||||
NVG_SOURCE_OVER,
|
||||
lv_nanovg_color_convert(dsc->color, dsc->opa));
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static enum NVGwinding path_append_inner_rect(NVGcontext * ctx,
|
||||
const lv_draw_border_dsc_t * dsc,
|
||||
int32_t x, int32_t y, int32_t w, int32_t h,
|
||||
float r)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
const float half_w = w / 2.0f;
|
||||
const float half_h = h / 2.0f;
|
||||
const int32_t border_w = dsc->width;
|
||||
const float border_w_max = LV_MIN(half_w, half_h);
|
||||
|
||||
/* normal fill, no inner rect */
|
||||
if(border_w >= border_w_max) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NVG_CCW;
|
||||
}
|
||||
|
||||
const float r_in = r - border_w;
|
||||
|
||||
/* full border, simple rect */
|
||||
if(dsc->side == LV_BORDER_SIDE_FULL) {
|
||||
lv_nanovg_path_append_rect(ctx,
|
||||
x + border_w, y + border_w,
|
||||
w - border_w * 2, h - border_w * 2,
|
||||
r_in < 0 ? 0 : r_in);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NVG_CW;
|
||||
}
|
||||
|
||||
/* reset outer rect path */
|
||||
nvgBeginPath(ctx);
|
||||
|
||||
/* no-radius case */
|
||||
if(dsc->radius <= 0) {
|
||||
if(dsc->side & LV_BORDER_SIDE_TOP) {
|
||||
lv_nanovg_path_append_rect(ctx,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
border_w,
|
||||
0);
|
||||
}
|
||||
if(dsc->side & LV_BORDER_SIDE_LEFT) {
|
||||
lv_nanovg_path_append_rect(ctx,
|
||||
x,
|
||||
y,
|
||||
border_w,
|
||||
h,
|
||||
0);
|
||||
}
|
||||
if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
|
||||
lv_nanovg_path_append_rect(ctx,
|
||||
x,
|
||||
y + h - border_w,
|
||||
w,
|
||||
border_w,
|
||||
0);
|
||||
}
|
||||
if(dsc->side & LV_BORDER_SIDE_RIGHT) {
|
||||
lv_nanovg_path_append_rect(ctx,
|
||||
x + w - border_w,
|
||||
y,
|
||||
border_w,
|
||||
h,
|
||||
0);
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NVG_CCW;
|
||||
}
|
||||
|
||||
/* coordinate reference map: https://github.com/lvgl/lvgl/pull/6796 */
|
||||
const float c1_x = x + r;
|
||||
const float c1_y = y + r;
|
||||
const float c2_x = x + w - r;
|
||||
const float c2_y = c1_y;
|
||||
const float c3_x = c2_x;
|
||||
const float c3_y = y + h - r;
|
||||
const float c4_x = c1_x;
|
||||
const float c4_y = c3_y;
|
||||
|
||||
/* When border_w > r, No need to calculate the intersection of the arc and the line */
|
||||
if(r_in <= 0) {
|
||||
const float p1_x = x;
|
||||
const float p1_y = y + border_w;
|
||||
const float p2_x = x;
|
||||
const float p2_y = y + r;
|
||||
const float p3_x = x + r;
|
||||
const float p3_y = y;
|
||||
const float p4_x = x + border_w;
|
||||
const float p4_y = y;
|
||||
|
||||
const float p5_x = x + w - border_w;
|
||||
const float p5_y = y;
|
||||
const float p6_x = x + w - r;
|
||||
const float p6_y = y;
|
||||
const float p7_x = x + w;
|
||||
const float p7_y = y + r;
|
||||
const float p8_x = x + w;
|
||||
const float p8_y = y + border_w;
|
||||
|
||||
const float p9_x = x + w;
|
||||
const float p9_y = y + h - border_w;
|
||||
const float p10_x = x + w;
|
||||
const float p10_y = y + h - r;
|
||||
const float p11_x = x + w - r;
|
||||
const float p11_y = y + h;
|
||||
const float p12_x = x + w - border_w;
|
||||
const float p12_y = y + h;
|
||||
|
||||
const float p13_x = x + border_w;
|
||||
const float p13_y = y + h;
|
||||
const float p14_x = x + r;
|
||||
const float p14_y = y + h;
|
||||
const float p15_x = x;
|
||||
const float p15_y = y + h - r;
|
||||
const float p16_x = x;
|
||||
const float p16_y = y + h - border_w;
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
|
||||
nvgMoveTo(ctx, p16_x, p16_y);
|
||||
nvgLineTo(ctx, p9_x, p9_y);
|
||||
nvgLineTo(ctx, p10_x, p10_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p10_x, p10_y, c3_x, c3_y, p11_x, p11_y);
|
||||
nvgLineTo(ctx, p14_x, p14_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p14_x, p14_y, c4_x, c4_y, p15_x, p15_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_TOP) {
|
||||
nvgMoveTo(ctx, p1_x, p1_y);
|
||||
nvgLineTo(ctx, p2_x, p2_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p2_x, p2_y, c1_x, c1_y, p3_x, p3_y);
|
||||
nvgLineTo(ctx, p6_x, p6_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p6_x, p6_y, c2_x, c2_y, p7_x, p7_y);
|
||||
nvgLineTo(ctx, p8_x, p8_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_LEFT) {
|
||||
nvgMoveTo(ctx, p4_x, p4_y);
|
||||
nvgLineTo(ctx, p13_x, p13_y);
|
||||
nvgLineTo(ctx, p14_x, p14_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p14_x, p14_y, c4_x, c4_y, p15_x, p15_y);
|
||||
nvgLineTo(ctx, p2_x, p2_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p2_x, p2_y, c1_x, c1_y, p3_x, p3_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_RIGHT) {
|
||||
nvgMoveTo(ctx, p5_x, p5_y);
|
||||
nvgLineTo(ctx, p6_x, p6_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p6_x, p6_y, c2_x, c2_y, p7_x, p7_y);
|
||||
nvgLineTo(ctx, p10_x, p10_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p10_x, p10_y, c3_x, c3_y, p11_x, p11_y);
|
||||
nvgLineTo(ctx, p12_x, p12_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NVG_CCW;
|
||||
}
|
||||
|
||||
/* When border_w < r, Calculate the intersection of an arc and a line */
|
||||
|
||||
/* r^2 - r_in^2 = offset^2 */
|
||||
const float offset = NVG_MATH_SQRTF((2 * r - border_w) * border_w);
|
||||
const float sweep_alpha = NVG_MATH_DEGREES(NVG_MATH_ACOSF(r_in / r));
|
||||
const float sweep_beta = 90 - sweep_alpha;
|
||||
|
||||
const float p1_x = x + border_w;
|
||||
const float p1_y = y + r;
|
||||
const float p2_x = x;
|
||||
const float p2_y = y + r;
|
||||
const float p3_x = x + border_w;
|
||||
const float p3_y = y + r - offset;
|
||||
const float p4_x = x + r - offset;
|
||||
const float p4_y = y + border_w;
|
||||
const float p5_x = x + r;
|
||||
const float p5_y = y;
|
||||
const float p6_x = x + r;
|
||||
const float p6_y = y + border_w;
|
||||
|
||||
const float p7_x = x + w - r;
|
||||
const float p7_y = y + border_w;
|
||||
const float p8_x = x + w - r;
|
||||
const float p8_y = y;
|
||||
const float p10_x = x + w - border_w;
|
||||
const float p10_y = y + r - offset;
|
||||
const float p11_x = x + w;
|
||||
const float p11_y = y + r;
|
||||
const float p12_x = x + w - border_w;
|
||||
const float p12_y = y + r;
|
||||
|
||||
const float p13_x = x + w - border_w;
|
||||
const float p13_y = y + h - r;
|
||||
const float p14_x = x + w;
|
||||
const float p14_y = y + h - r;
|
||||
const float p16_x = x + w - r + offset;
|
||||
const float p16_y = y + h - border_w;
|
||||
const float p17_x = x + w - r;
|
||||
const float p17_y = y + h;
|
||||
const float p18_x = x + w - r;
|
||||
const float p18_y = y + h - border_w;
|
||||
|
||||
const float p19_x = x + r;
|
||||
const float p19_y = y + h - border_w;
|
||||
const float p20_x = x + r;
|
||||
const float p20_y = y + h;
|
||||
const float p21_x = x + r - offset;
|
||||
const float p21_y = y + h - border_w;
|
||||
const float p22_x = x + border_w;
|
||||
const float p22_y = y + h - r + offset;
|
||||
const float p23_x = x;
|
||||
const float p23_y = y + h - r;
|
||||
const float p24_x = x + border_w;
|
||||
const float p24_y = y + h - r;
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
|
||||
nvgMoveTo(ctx, p21_x, p21_y);
|
||||
nvgLineTo(ctx, p16_x, p16_y);
|
||||
lv_nanovg_path_append_arc(ctx, c3_x, c3_y, r, sweep_beta, sweep_alpha, false);
|
||||
nvgLineTo(ctx, p20_x, p20_y);
|
||||
lv_nanovg_path_append_arc(ctx, c4_x, c4_y, r, 90, sweep_alpha, false);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_TOP) {
|
||||
nvgMoveTo(ctx, p4_x, p4_y);
|
||||
lv_nanovg_path_append_arc(ctx, c1_x, c1_y, r, 270 - sweep_alpha, sweep_alpha, false);
|
||||
nvgLineTo(ctx, p8_x, p8_y);
|
||||
lv_nanovg_path_append_arc(ctx, c2_x, c2_y, r, 270, sweep_alpha, false);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_LEFT) {
|
||||
nvgMoveTo(ctx, p3_x, p3_y);
|
||||
nvgLineTo(ctx, p22_x, p22_y);
|
||||
lv_nanovg_path_append_arc(ctx, c4_x, c4_y, r, 90 + sweep_beta, sweep_alpha, false);
|
||||
nvgLineTo(ctx, p2_x, p2_y);
|
||||
lv_nanovg_path_append_arc(ctx, c1_x, c1_y, r, 180, sweep_alpha, false);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(dsc->side & LV_BORDER_SIDE_RIGHT) {
|
||||
nvgMoveTo(ctx, p10_x, p10_y);
|
||||
lv_nanovg_path_append_arc(ctx, c2_x, c2_y, r, 270 + sweep_beta, sweep_alpha, false);
|
||||
nvgLineTo(ctx, p14_x, p14_y);
|
||||
lv_nanovg_path_append_arc(ctx, c3_x, c3_y, r, 0, sweep_alpha, false);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
/* Draw the rounded corners adjacent to the border */
|
||||
|
||||
if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_LEFT)) {
|
||||
nvgMoveTo(ctx, p2_x, p2_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p2_x, p2_y, c1_x, c1_y, p5_x, p5_y);
|
||||
nvgLineTo(ctx, p6_x, p6_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p6_x, p6_y, c1_x, c1_y, p1_x, p1_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_RIGHT)) {
|
||||
nvgMoveTo(ctx, p8_x, p8_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p8_x, p8_y, c2_x, c2_y, p11_x, p11_y);
|
||||
nvgLineTo(ctx, p12_x, p12_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p12_x, p12_y, c2_x, c2_y, p7_x, p7_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT)) {
|
||||
nvgMoveTo(ctx, p20_x, p20_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p20_x, p20_y, c4_x, c4_y, p23_x, p23_y);
|
||||
nvgLineTo(ctx, p24_x, p24_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p24_x, p24_y, c4_x, c4_y, p19_x, p19_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT)) {
|
||||
nvgMoveTo(ctx, p14_x, p14_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p14_x, p14_y, c3_x, c3_y, p17_x, p17_y);
|
||||
nvgLineTo(ctx, p18_x, p18_y);
|
||||
lv_nanovg_path_append_arc_right_angle(ctx, p18_x, p18_y, c3_x, c3_y, p13_x, p13_y);
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NVG_CCW;
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
92
src/draw/nanovg/lv_draw_nanovg_box_shadow.c
Normal file
92
src/draw/nanovg/lv_draw_nanovg_box_shadow.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_box_shadow.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_box_shadow(lv_draw_task_t * t, const lv_draw_box_shadow_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
/*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/
|
||||
lv_area_t core_area;
|
||||
core_area.x1 = coords->x1 + dsc->ofs_x - dsc->spread;
|
||||
core_area.x2 = coords->x2 + dsc->ofs_x + dsc->spread;
|
||||
core_area.y1 = coords->y1 + dsc->ofs_y - dsc->spread;
|
||||
core_area.y2 = coords->y2 + dsc->ofs_y + dsc->spread;
|
||||
|
||||
/*Calculate the bounding box of the shadow*/
|
||||
lv_area_t shadow_area;
|
||||
shadow_area.x1 = core_area.x1 - dsc->width / 2 - 1;
|
||||
shadow_area.x2 = core_area.x2 + dsc->width / 2 + 1;
|
||||
shadow_area.y1 = core_area.y1 - dsc->width / 2 - 1;
|
||||
shadow_area.y2 = core_area.y2 + dsc->width / 2 + 1;
|
||||
|
||||
/*Get clipped draw area which is the real draw area.
|
||||
*It is always the same or inside `shadow_area`*/
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, &shadow_area, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
const NVGcolor icol = lv_nanovg_color_convert(dsc->color, dsc->opa);
|
||||
const NVGcolor ocol = lv_nanovg_color_convert(lv_color_black(), 0);
|
||||
|
||||
const int32_t w = lv_area_get_width(&shadow_area);
|
||||
const int32_t h = lv_area_get_height(&shadow_area);
|
||||
|
||||
NVGpaint paint = nvgBoxGradient(
|
||||
u->vg,
|
||||
shadow_area.x1, shadow_area.y1,
|
||||
w, h,
|
||||
dsc->radius, dsc->width, icol, ocol);
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
lv_nanovg_path_append_rect(u->vg, shadow_area.x1, shadow_area.y1, w, h, dsc->radius);
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
77
src/draw/nanovg/lv_draw_nanovg_fill.c
Normal file
77
src/draw/nanovg/lv_draw_nanovg_fill.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_fill.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, coords, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
|
||||
lv_nanovg_path_append_rect(u->vg,
|
||||
coords->x1, coords->y1,
|
||||
lv_area_get_width(coords), lv_area_get_height(coords),
|
||||
dsc->radius);
|
||||
|
||||
if(dsc->grad.dir != LV_GRAD_DIR_NONE) {
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
lv_nanovg_draw_grad_helper(u->vg, coords, &dsc->grad, NVG_CCW, NVG_SOURCE_OVER);
|
||||
#else
|
||||
LV_LOG_WARN("Gradient fill is not supported without VECTOR_GRAPHIC");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
lv_nanovg_fill(u->vg, NVG_CCW, NVG_SOURCE_OVER, lv_nanovg_color_convert(dsc->color, dsc->opa));
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
188
src/draw/nanovg/lv_draw_nanovg_grad.c
Normal file
188
src/draw/nanovg/lv_draw_nanovg_grad.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_grad.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG && LV_USE_VECTOR_GRAPHIC
|
||||
|
||||
#include "../../draw/lv_draw_vector_private.h"
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
bool lv_nanovg_grad_to_paint(NVGcontext * ctx, const lv_vector_gradient_t * grad, NVGpaint * paint)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
LV_ASSERT_NULL(grad);
|
||||
LV_ASSERT_NULL(paint);
|
||||
|
||||
if(grad->stops_count < 2) {
|
||||
LV_LOG_WARN("stops_count(%d) should be 2 for gradient", grad->stops_count);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
const NVGcolor icol = lv_nanovg_color_convert(grad->stops[0].color, grad->stops[0].opa);
|
||||
const NVGcolor ocol = lv_nanovg_color_convert(grad->stops[1].color, grad->stops[1].opa);
|
||||
|
||||
switch(grad->style) {
|
||||
case LV_VECTOR_GRADIENT_STYLE_LINEAR:
|
||||
*paint = nvgLinearGradient(ctx, grad->x1, grad->y1, grad->x2, grad->y2, icol, ocol);
|
||||
break;
|
||||
|
||||
case LV_VECTOR_GRADIENT_STYLE_RADIAL: {
|
||||
const float inr = grad->cr * grad->stops[0].frac / 255;
|
||||
const float outr = grad->cr * grad->stops[1].frac / 255;
|
||||
*paint = nvgRadialGradient(ctx, grad->cx, grad->cy, inr, outr, icol, ocol);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_LOG_WARN("Unsupported gradient style: %d", grad->style);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lv_nanovg_draw_grad(
|
||||
NVGcontext * ctx,
|
||||
const lv_vector_gradient_t * grad,
|
||||
enum NVGwinding winding,
|
||||
enum NVGcompositeOperation composite_operation)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
NVGpaint paint;
|
||||
if(!lv_nanovg_grad_to_paint(ctx, grad, &paint)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
nvgPathWinding(ctx, winding);
|
||||
nvgGlobalCompositeOperation(ctx, composite_operation);
|
||||
nvgFillPaint(ctx, paint);
|
||||
nvgFill(ctx);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_draw_grad_helper(
|
||||
NVGcontext * ctx,
|
||||
const lv_area_t * area,
|
||||
const lv_grad_dsc_t * grad_dsc,
|
||||
enum NVGwinding winding,
|
||||
enum NVGcompositeOperation composite_operation)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
LV_ASSERT_NULL(area);
|
||||
LV_ASSERT_NULL(grad_dsc);
|
||||
|
||||
lv_vector_gradient_t grad;
|
||||
lv_memzero(&grad, sizeof(grad));
|
||||
|
||||
grad.style = LV_VECTOR_GRADIENT_STYLE_LINEAR;
|
||||
grad.stops_count = grad_dsc->stops_count;
|
||||
lv_memcpy(grad.stops, grad_dsc->stops, sizeof(lv_grad_stop_t) * grad_dsc->stops_count);
|
||||
|
||||
/*convert to spread mode*/
|
||||
switch(grad_dsc->extend) {
|
||||
case LV_GRAD_EXTEND_PAD:
|
||||
grad.spread = LV_VECTOR_GRADIENT_SPREAD_PAD;
|
||||
break;
|
||||
case LV_GRAD_EXTEND_REPEAT:
|
||||
grad.spread = LV_VECTOR_GRADIENT_SPREAD_REPEAT;
|
||||
break;
|
||||
case LV_GRAD_EXTEND_REFLECT:
|
||||
grad.spread = LV_VECTOR_GRADIENT_SPREAD_REFLECT;
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("Unsupported gradient extend mode: %d", grad_dsc->extend);
|
||||
grad.spread = LV_VECTOR_GRADIENT_SPREAD_PAD;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(grad_dsc->dir) {
|
||||
case LV_GRAD_DIR_VER:
|
||||
grad.x1 = area->x1;
|
||||
grad.y1 = area->y1;
|
||||
grad.x2 = area->x1;
|
||||
grad.y2 = area->y2 + 1;
|
||||
break;
|
||||
|
||||
case LV_GRAD_DIR_HOR:
|
||||
grad.x1 = area->x1;
|
||||
grad.y1 = area->y1;
|
||||
grad.x2 = area->x2 + 1;
|
||||
grad.y2 = area->y1;
|
||||
break;
|
||||
|
||||
case LV_GRAD_DIR_LINEAR: {
|
||||
int32_t w = lv_area_get_width(area);
|
||||
int32_t h = lv_area_get_height(area);
|
||||
|
||||
grad.x1 = lv_pct_to_px(grad_dsc->params.linear.start.x, w) + area->x1;
|
||||
grad.y1 = lv_pct_to_px(grad_dsc->params.linear.start.y, h) + area->y1;
|
||||
grad.x2 = lv_pct_to_px(grad_dsc->params.linear.end.x, w) + area->x1;
|
||||
grad.y2 = lv_pct_to_px(grad_dsc->params.linear.end.y, h) + area->y1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LV_GRAD_DIR_RADIAL: {
|
||||
grad.style = LV_VECTOR_GRADIENT_STYLE_RADIAL;
|
||||
int32_t w = lv_area_get_width(area);
|
||||
int32_t h = lv_area_get_height(area);
|
||||
|
||||
grad.cx = lv_pct_to_px(grad_dsc->params.radial.focal.x, w) + area->x1;
|
||||
grad.cy = lv_pct_to_px(grad_dsc->params.radial.focal.y, h) + area->y1;
|
||||
int32_t end_extent_x = lv_pct_to_px(grad_dsc->params.radial.end_extent.x, w) + area->x1;
|
||||
int32_t end_extent_y = lv_pct_to_px(grad_dsc->params.radial.end_extent.y, h) + area->y1;
|
||||
grad.cr = LV_MAX(end_extent_x - grad.cx, end_extent_y - grad.cy);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_LOG_WARN("Unsupported gradient direction: %d", grad_dsc->dir);
|
||||
return;
|
||||
}
|
||||
|
||||
lv_nanovg_draw_grad(ctx, &grad, winding, composite_operation);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
229
src/draw/nanovg/lv_draw_nanovg_image.c
Normal file
229
src/draw/nanovg/lv_draw_nanovg_image.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_image.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_math.h"
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
#include "../lv_image_decoder_private.h"
|
||||
#include "../lv_draw_image_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void image_dsc_to_matrix(lv_matrix_t * matrix, int32_t x, int32_t y, const lv_draw_image_dsc_t * dsc);
|
||||
static bool is_power_of_2(uint32_t num);
|
||||
static void fill_repeat_tile_image(
|
||||
lv_draw_nanovg_unit_t * u,
|
||||
const lv_draw_image_dsc_t * dsc,
|
||||
const lv_area_t * coords,
|
||||
lv_area_t tile_area,
|
||||
const uint32_t img_w,
|
||||
const uint32_t img_h,
|
||||
const int image_handle);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords,
|
||||
int image_handle)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, &t->_real_area, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
/* Use coords as the fallback image width and height */
|
||||
const uint32_t img_w = dsc->header.w ? dsc->header.w : lv_area_get_width(coords);
|
||||
const uint32_t img_h = dsc->header.h ? dsc->header.h : lv_area_get_height(coords);
|
||||
bool use_repeat_tile = false;
|
||||
|
||||
if(image_handle < 0) {
|
||||
int image_flags = 0;
|
||||
|
||||
if(dsc->tile) {
|
||||
#ifdef NANOVG_GLES2_IMPLEMENTATION
|
||||
/* GLES2 does not support sampling non-power-of-2 textures in repeating mode. */
|
||||
if(!is_power_of_2(img_w) || !is_power_of_2(img_h)) {
|
||||
LV_LOG_TRACE("Unsupported image size %" LV_PRIu32 " x %" LV_PRIu32 ". Skipping for repeat mode.", img_w, img_h);
|
||||
use_repeat_tile = true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
LV_UNUSED(is_power_of_2);
|
||||
image_flags |= NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY;
|
||||
}
|
||||
}
|
||||
|
||||
image_handle = lv_nanovg_image_cache_get_handle(u, dsc->src, lv_color_to_32(dsc->recolor, dsc->opa), image_flags, NULL);
|
||||
}
|
||||
|
||||
if(image_handle < 0) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
/* original image matrix */
|
||||
lv_matrix_t image_matrix;
|
||||
lv_matrix_identity(&image_matrix);
|
||||
image_dsc_to_matrix(&image_matrix, coords->x1, coords->y1, dsc);
|
||||
lv_nanovg_transform(u->vg, &image_matrix);
|
||||
|
||||
int32_t img_ofs_x = 0;
|
||||
int32_t img_ofs_y = 0;
|
||||
int32_t rect_w = img_w;
|
||||
int32_t rect_h = img_h;
|
||||
|
||||
if(dsc->tile) {
|
||||
lv_area_t tile_area;
|
||||
if(lv_area_get_width(&dsc->image_area) >= 0) {
|
||||
tile_area = dsc->image_area;
|
||||
}
|
||||
else {
|
||||
tile_area = *coords;
|
||||
}
|
||||
|
||||
if(use_repeat_tile) {
|
||||
/* When alignment requirements are not met, simulate tiles by repeating the texture. */
|
||||
fill_repeat_tile_image(u, dsc, coords, tile_area, img_w, img_h, image_handle);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
img_ofs_x = tile_area.x1 - coords->x1;
|
||||
img_ofs_y = tile_area.y1 - coords->y1;
|
||||
rect_w = lv_area_get_width(coords);
|
||||
rect_h = lv_area_get_height(coords);
|
||||
}
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
lv_nanovg_path_append_rect(u->vg, 0, 0, rect_w, rect_h, dsc->clip_radius);
|
||||
|
||||
NVGpaint paint = nvgImagePattern(u->vg, img_ofs_x, img_ofs_y, img_w, img_h, 0, image_handle,
|
||||
dsc->opa / (float)LV_OPA_COVER);
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void image_dsc_to_matrix(lv_matrix_t * matrix, int32_t x, int32_t y, const lv_draw_image_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(matrix);
|
||||
LV_ASSERT_NULL(dsc);
|
||||
|
||||
int32_t rotation = dsc->rotation;
|
||||
int32_t scale_x = dsc->scale_x;
|
||||
int32_t scale_y = dsc->scale_y;
|
||||
|
||||
lv_matrix_translate(matrix, x, y);
|
||||
|
||||
if(rotation != 0 || scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
|
||||
lv_point_t pivot = dsc->pivot;
|
||||
lv_matrix_translate(matrix, pivot.x, pivot.y);
|
||||
|
||||
if(rotation != 0) {
|
||||
lv_matrix_rotate(matrix, rotation * 0.1f);
|
||||
}
|
||||
|
||||
if(scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
|
||||
lv_matrix_scale(
|
||||
matrix,
|
||||
(float)scale_x / LV_SCALE_NONE,
|
||||
(float)scale_y / LV_SCALE_NONE);
|
||||
}
|
||||
|
||||
lv_matrix_translate(matrix, -pivot.x, -pivot.y);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_power_of_2(uint32_t num)
|
||||
{
|
||||
uint32_t n = num > 0 ? num - 1 : 0;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n++;
|
||||
return n == num;
|
||||
}
|
||||
|
||||
static void fill_repeat_tile_image(
|
||||
lv_draw_nanovg_unit_t * u,
|
||||
const lv_draw_image_dsc_t * dsc,
|
||||
const lv_area_t * coords,
|
||||
lv_area_t tile_area,
|
||||
const uint32_t img_w,
|
||||
const uint32_t img_h,
|
||||
const int image_handle)
|
||||
{
|
||||
if(dsc->clip_radius) {
|
||||
LV_LOG_WARN("Unsupported clip radius for repeat mode.");
|
||||
}
|
||||
|
||||
const int32_t tile_x_start = tile_area.x1;
|
||||
while(tile_area.y1 <= coords->y2) {
|
||||
while(tile_area.x1 <= coords->x2) {
|
||||
const int32_t img_ofs_x = tile_area.x1 - coords->x1;
|
||||
const int32_t img_ofs_y = tile_area.y1 - coords->y1;
|
||||
|
||||
lv_area_t clipped_img_area;
|
||||
if(lv_area_intersect(&clipped_img_area, &tile_area, coords)) {
|
||||
nvgBeginPath(u->vg);
|
||||
lv_nanovg_path_append_rect(u->vg, img_ofs_x, img_ofs_y, img_w, img_h, 0);
|
||||
NVGpaint paint = nvgImagePattern(u->vg, img_ofs_x, img_ofs_y, img_w, img_h, 0, image_handle,
|
||||
dsc->opa / (float)LV_OPA_COVER);
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
}
|
||||
|
||||
tile_area.x1 += img_w;
|
||||
tile_area.x2 += img_w;
|
||||
}
|
||||
|
||||
tile_area.y1 += img_h;
|
||||
tile_area.y2 += img_h;
|
||||
tile_area.x1 = tile_x_start;
|
||||
tile_area.x2 = tile_x_start + img_w - 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
384
src/draw/nanovg/lv_draw_nanovg_label.c
Normal file
384
src/draw/nanovg/lv_draw_nanovg_label.c
Normal file
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_label.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
#include "../lv_draw_label_private.h"
|
||||
#include "../lv_draw_image_private.h"
|
||||
#include "../../misc/cache/lv_cache_entry_private.h"
|
||||
#include "../../misc/lv_pending.h"
|
||||
#include "../../libs/freetype/lv_freetype.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
/* context */
|
||||
lv_draw_nanovg_unit_t * u;
|
||||
|
||||
/* key */
|
||||
lv_font_glyph_dsc_t g_dsc;
|
||||
|
||||
/* value */
|
||||
int image_handle;
|
||||
} letter_item_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void draw_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
|
||||
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area);
|
||||
|
||||
static void letter_cache_release_cb(void * entry, void * user_data);
|
||||
static bool letter_create_cb(letter_item_t * item, void * user_data);
|
||||
static void letter_free_cb(letter_item_t * item, void * user_data);
|
||||
static lv_cache_compare_res_t letter_compare_cb(const letter_item_t * lhs, const letter_item_t * rhs);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_label_init(lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->letter_cache == NULL);
|
||||
LV_ASSERT(u->letter_pending == NULL);
|
||||
|
||||
const lv_cache_ops_t ops = {
|
||||
.compare_cb = (lv_cache_compare_cb_t)letter_compare_cb,
|
||||
.create_cb = (lv_cache_create_cb_t)letter_create_cb,
|
||||
.free_cb = (lv_cache_free_cb_t)letter_free_cb,
|
||||
};
|
||||
|
||||
u->letter_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(letter_item_t), LV_NANOVG_LETTER_CACHE_CNT, ops);
|
||||
lv_cache_set_name(u->letter_cache, "NVG_LETTER");
|
||||
u->letter_pending = lv_pending_create(sizeof(lv_cache_entry_t *), 4);
|
||||
lv_pending_set_free_cb(u->letter_pending, letter_cache_release_cb, u->letter_cache);
|
||||
}
|
||||
|
||||
void lv_draw_nanovg_label_deinit(lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->letter_cache);
|
||||
LV_ASSERT(u->letter_pending);
|
||||
|
||||
lv_pending_destroy(u->letter_pending);
|
||||
u->letter_pending = NULL;
|
||||
|
||||
lv_cache_destroy(u->letter_cache, NULL);
|
||||
u->letter_cache = NULL;
|
||||
}
|
||||
|
||||
void lv_draw_nanovg_letter(lv_draw_task_t * t, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_ASSERT_NULL(t);
|
||||
LV_ASSERT_NULL(dsc);
|
||||
LV_ASSERT_NULL(coords);
|
||||
|
||||
if(dsc->opa <= LV_OPA_MIN)
|
||||
return;
|
||||
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_draw_glyph_dsc_t glyph_dsc;
|
||||
lv_draw_glyph_dsc_init(&glyph_dsc);
|
||||
glyph_dsc.opa = dsc->opa;
|
||||
glyph_dsc.bg_coords = NULL;
|
||||
glyph_dsc.color = dsc->color;
|
||||
glyph_dsc.rotation = dsc->rotation;
|
||||
glyph_dsc.pivot = dsc->pivot;
|
||||
|
||||
lv_draw_unit_draw_letter(t, &glyph_dsc, &(lv_point_t) {
|
||||
.x = coords->x1, .y = coords->y1
|
||||
},
|
||||
dsc->font, dsc->unicode, draw_letter_cb);
|
||||
|
||||
if(glyph_dsc._draw_buf) {
|
||||
lv_draw_buf_destroy(glyph_dsc._draw_buf);
|
||||
glyph_dsc._draw_buf = NULL;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_draw_nanovg_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_draw_label_iterate_characters(t, dsc, coords, draw_letter_cb);
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static inline void convert_letter_matrix(lv_matrix_t * matrix, const lv_draw_glyph_dsc_t * dsc)
|
||||
{
|
||||
lv_matrix_translate(matrix, dsc->letter_coords->x1, dsc->letter_coords->y1);
|
||||
|
||||
if(!dsc->rotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lv_point_t pivot = {
|
||||
.x = dsc->pivot.x,
|
||||
.y = dsc->g->box_h + dsc->g->ofs_y
|
||||
};
|
||||
lv_matrix_translate(matrix, pivot.x, pivot.y);
|
||||
lv_matrix_rotate(matrix, dsc->rotation / 10.0f);
|
||||
lv_matrix_translate(matrix, -pivot.x, -pivot.y);
|
||||
}
|
||||
|
||||
static bool draw_letter_clip_areas(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc, lv_area_t * letter_area,
|
||||
lv_area_t * cliped_area)
|
||||
{
|
||||
*letter_area = *dsc->letter_coords;
|
||||
|
||||
if(dsc->rotation) {
|
||||
const lv_point_t pivot = {
|
||||
.x = dsc->pivot.x,
|
||||
.y = dsc->g->box_h + dsc->g->ofs_y
|
||||
};
|
||||
|
||||
lv_image_buf_get_transformed_area(
|
||||
letter_area,
|
||||
lv_area_get_width(dsc->letter_coords),
|
||||
lv_area_get_height(dsc->letter_coords),
|
||||
dsc->rotation,
|
||||
LV_SCALE_NONE,
|
||||
LV_SCALE_NONE,
|
||||
&pivot);
|
||||
lv_area_move(letter_area, dsc->letter_coords->x1, dsc->letter_coords->y1);
|
||||
}
|
||||
|
||||
if(!lv_area_intersect(cliped_area, &t->clip_area, letter_area)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void draw_letter_bitmap(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc, int image_handle)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_area_t image_area;
|
||||
lv_area_t clip_area;
|
||||
if(!draw_letter_clip_areas(t, dsc, &image_area, &clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
if(!dsc->rotation) {
|
||||
float x = dsc->letter_coords->x1;
|
||||
float y = dsc->letter_coords->y1;
|
||||
float w = lv_area_get_width(dsc->letter_coords);
|
||||
float h = lv_area_get_height(dsc->letter_coords);
|
||||
|
||||
NVGpaint paint = nvgImagePattern(u->vg, x, y, w, h, 0, image_handle, 1.0f);
|
||||
paint.innerColor = paint.outerColor = nvgRGBA(dsc->color.red, dsc->color.green, dsc->color.blue, dsc->opa);
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
nvgRect(u->vg, x, y, w, h);
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
}
|
||||
else {
|
||||
/* TODO: draw rotated bitmap */
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static inline int letter_get_image_handle(lv_draw_nanovg_unit_t * u, lv_font_glyph_dsc_t * g_dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
letter_item_t search_key = { 0 };
|
||||
search_key.u = u;
|
||||
search_key.g_dsc = *g_dsc;
|
||||
search_key.g_dsc.entry = NULL; /* Exclude the cache entry from the key */
|
||||
|
||||
lv_cache_entry_t * cache_node_entry = lv_cache_acquire(u->letter_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
/* check if the cache is full */
|
||||
size_t free_size = lv_cache_get_free_size(u->letter_cache, NULL);
|
||||
if(free_size == 0) {
|
||||
LV_LOG_INFO("letter cache is full, release all pending cache entries");
|
||||
lv_nanovg_end_frame(u);
|
||||
}
|
||||
|
||||
cache_node_entry = lv_cache_acquire_or_create(u->letter_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
LV_LOG_ERROR("letter cache creating failed");
|
||||
LV_PROFILER_DRAW_END;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the new entry to the pending list */
|
||||
lv_pending_add(u->letter_pending, &cache_node_entry);
|
||||
|
||||
letter_item_t * letter_item = lv_cache_entry_get_data(cache_node_entry);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return letter_item->image_handle;
|
||||
}
|
||||
|
||||
static void letter_cache_release_cb(void * entry, void * user_data)
|
||||
{
|
||||
lv_cache_entry_t ** entry_p = entry;
|
||||
lv_cache_t * cache = user_data;
|
||||
lv_cache_release(cache, * entry_p, NULL);
|
||||
}
|
||||
|
||||
static bool letter_create_cb(letter_item_t * item, void * user_data)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_UNUSED(user_data);
|
||||
lv_font_glyph_dsc_t * g_dsc = &item->g_dsc;
|
||||
|
||||
const uint32_t w = g_dsc->box_w;
|
||||
const uint32_t h = g_dsc->box_h;
|
||||
|
||||
lv_draw_buf_t * image_buf = lv_nanovg_reshape_global_image(item->u, LV_COLOR_FORMAT_A8, w, h);
|
||||
if(!image_buf) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!lv_font_get_glyph_bitmap(g_dsc, image_buf)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("nvgCreateImage");
|
||||
item->image_handle = nvgCreateImage(item->u->vg, w, h, 0, NVG_TEXTURE_ALPHA, lv_draw_buf_goto_xy(image_buf, 0, 0));
|
||||
LV_PROFILER_DRAW_END_TAG("nvgCreateImage");
|
||||
|
||||
LV_LOG_TRACE("image_handle: %d", item->image_handle);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void letter_free_cb(letter_item_t * item, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_LOG_TRACE("image_handle: %d", item->image_handle);
|
||||
nvgDeleteImage(item->u->vg, item->image_handle);
|
||||
item->image_handle = -1;
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static lv_cache_compare_res_t letter_compare_cb(const letter_item_t * lhs, const letter_item_t * rhs)
|
||||
{
|
||||
int cmp_res = lv_memcmp(&lhs->g_dsc, &rhs->g_dsc, sizeof(lv_font_glyph_dsc_t));
|
||||
if(cmp_res != 0) {
|
||||
return cmp_res > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void draw_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
|
||||
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area)
|
||||
{
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
if(glyph_draw_dsc) {
|
||||
switch(glyph_draw_dsc->format) {
|
||||
case LV_FONT_GLYPH_FORMAT_A1:
|
||||
case LV_FONT_GLYPH_FORMAT_A2:
|
||||
case LV_FONT_GLYPH_FORMAT_A3:
|
||||
case LV_FONT_GLYPH_FORMAT_A4:
|
||||
case LV_FONT_GLYPH_FORMAT_A8: {
|
||||
int image_handle = letter_get_image_handle(u, glyph_draw_dsc->g);
|
||||
if(image_handle < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_letter_bitmap(t, glyph_draw_dsc, image_handle);
|
||||
}
|
||||
break;
|
||||
|
||||
#if LV_USE_FREETYPE
|
||||
case LV_FONT_GLYPH_FORMAT_VECTOR: {
|
||||
if(lv_freetype_is_outline_font(glyph_draw_dsc->g->resolved_font)) {
|
||||
if(!glyph_draw_dsc->glyph_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: draw_letter_outline(t, glyph_draw_dsc); */
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* LV_USE_FREETYPE */
|
||||
|
||||
case LV_FONT_GLYPH_FORMAT_IMAGE: {
|
||||
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
|
||||
if(!glyph_draw_dsc->glyph_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_image_dsc_t image_dsc;
|
||||
lv_draw_image_dsc_init(&image_dsc);
|
||||
image_dsc.opa = glyph_draw_dsc->opa;
|
||||
image_dsc.src = glyph_draw_dsc->glyph_data;
|
||||
image_dsc.rotation = glyph_draw_dsc->rotation;
|
||||
lv_draw_nanovg_image(t, &image_dsc, glyph_draw_dsc->letter_coords, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
#if LV_USE_FONT_PLACEHOLDER
|
||||
case LV_FONT_GLYPH_FORMAT_NONE: {
|
||||
if(glyph_draw_dsc->bg_coords == NULL) break;
|
||||
/* Draw a placeholder rectangle*/
|
||||
lv_draw_border_dsc_t border_draw_dsc;
|
||||
lv_draw_border_dsc_init(&border_draw_dsc);
|
||||
border_draw_dsc.opa = glyph_draw_dsc->opa;
|
||||
border_draw_dsc.color = glyph_draw_dsc->color;
|
||||
border_draw_dsc.width = 1;
|
||||
lv_draw_nanovg_border(t, &border_draw_dsc, glyph_draw_dsc->bg_coords);
|
||||
}
|
||||
break;
|
||||
#endif /* LV_USE_FONT_PLACEHOLDER */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(fill_draw_dsc && fill_area) {
|
||||
lv_draw_nanovg_fill(t, fill_draw_dsc, fill_area);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
74
src/draw/nanovg/lv_draw_nanovg_layer.c
Normal file
74
src/draw/nanovg/lv_draw_nanovg_layer.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_layer.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_fbo_cache.h"
|
||||
#include "../lv_draw_image_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_layer(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
|
||||
const lv_area_t * coords)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
lv_layer_t * layer = (lv_layer_t *)draw_dsc->src;
|
||||
|
||||
if(!layer->user_data) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
int image_handle = lv_nanovg_fb_get_image_handle(lv_nanovg_fbo_cache_entry_to_fb(layer->user_data));
|
||||
if(image_handle <= 0) {
|
||||
LV_LOG_WARN("Invalid image handle: %d", image_handle);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_image_dsc_t new_draw_dsc = *draw_dsc;
|
||||
new_draw_dsc.src = NULL;
|
||||
lv_draw_nanovg_image(t, &new_draw_dsc, coords, image_handle);
|
||||
|
||||
lv_nanovg_end_frame(u);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
191
src/draw/nanovg/lv_draw_nanovg_line.c
Normal file
191
src/draw/nanovg/lv_draw_nanovg_line.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_line.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_math.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define SQ(x) ((x) * (x))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
float p1_x = dsc->p1.x;
|
||||
float p1_y = dsc->p1.y;
|
||||
float p2_x = dsc->p2.x;
|
||||
float p2_y = dsc->p2.y;
|
||||
|
||||
if(p1_x == p2_x && p1_y == p2_y) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
float half_w = dsc->width * 0.5f;
|
||||
|
||||
lv_area_t rel_clip_area;
|
||||
rel_clip_area.x1 = (int32_t)(LV_MIN(p1_x, p2_x) - half_w);
|
||||
rel_clip_area.x2 = (int32_t)(LV_MAX(p1_x, p2_x) + half_w);
|
||||
rel_clip_area.y1 = (int32_t)(LV_MIN(p1_y, p2_y) - half_w);
|
||||
rel_clip_area.y2 = (int32_t)(LV_MAX(p1_y, p2_y) + half_w);
|
||||
|
||||
if(!lv_area_intersect(&rel_clip_area, &rel_clip_area, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t dash_width = dsc->dash_width;
|
||||
int32_t dash_gap = dsc->dash_gap;
|
||||
int32_t dash_l = dash_width + dash_gap;
|
||||
|
||||
float dx = p2_x - p1_x;
|
||||
float dy = p2_y - p1_y;
|
||||
float inv_dl = nvg_math_inv_sqrtf(SQ(dx) + SQ(dy));
|
||||
float w_dx = dsc->width * dy * inv_dl;
|
||||
float w_dy = dsc->width * dx * inv_dl;
|
||||
float w2_dx = w_dx / 2;
|
||||
float w2_dy = w_dy / 2;
|
||||
|
||||
int32_t ndash = 0;
|
||||
if(dash_width && dash_l * inv_dl < 1.0f) {
|
||||
ndash = (int32_t)((1.0f / inv_dl + dash_l - 1) / dash_l);
|
||||
}
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
|
||||
/* head point */
|
||||
float head_start_x = p1_x + w2_dx;
|
||||
float head_start_y = p1_y - w2_dy;
|
||||
float head_end_x = p1_x - w2_dx;
|
||||
float head_end_y = p1_y + w2_dy;
|
||||
|
||||
/* tail point */
|
||||
float tail_start_x = p2_x - w2_dx;
|
||||
float tail_start_y = p2_y + w2_dy;
|
||||
float tail_end_x = p2_x + w2_dx;
|
||||
float tail_end_y = p2_y - w2_dy;
|
||||
|
||||
/*
|
||||
head_start tail_end
|
||||
*-----------------*
|
||||
/| |\
|
||||
/ | | \
|
||||
arc_c *( *p1 p2* )* arc_c
|
||||
\ | | /
|
||||
\| |/
|
||||
*-----------------*
|
||||
head_end tail_start
|
||||
*/
|
||||
|
||||
/* move to start point */
|
||||
nvgMoveTo(u->vg, head_start_x, head_start_y);
|
||||
|
||||
/* draw line head */
|
||||
if(dsc->round_start) {
|
||||
float arc_cx = p1_x - w2_dy;
|
||||
float arc_cy = p1_y - w2_dx;
|
||||
|
||||
/* start 90deg arc */
|
||||
lv_nanovg_path_append_arc_right_angle(u->vg,
|
||||
head_start_x, head_start_y,
|
||||
p1_x, p1_y,
|
||||
arc_cx, arc_cy);
|
||||
|
||||
/* end 90deg arc */
|
||||
lv_nanovg_path_append_arc_right_angle(u->vg,
|
||||
arc_cx, arc_cy,
|
||||
p1_x, p1_y,
|
||||
head_end_x, head_end_y);
|
||||
}
|
||||
else {
|
||||
nvgLineTo(u->vg, head_end_x, head_end_y);
|
||||
}
|
||||
|
||||
/* draw line body */
|
||||
nvgLineTo(u->vg, tail_start_x, tail_start_y);
|
||||
|
||||
/* draw line tail */
|
||||
if(dsc->round_end) {
|
||||
float arc_cx = p2_x + w2_dy;
|
||||
float arc_cy = p2_y + w2_dx;
|
||||
lv_nanovg_path_append_arc_right_angle(u->vg,
|
||||
tail_start_x, tail_start_y,
|
||||
p2_x, p2_y,
|
||||
arc_cx, arc_cy);
|
||||
lv_nanovg_path_append_arc_right_angle(u->vg,
|
||||
arc_cx, arc_cy,
|
||||
p2_x, p2_y,
|
||||
tail_end_x, tail_end_y);
|
||||
}
|
||||
else {
|
||||
nvgLineTo(u->vg, tail_end_x, tail_end_y);
|
||||
}
|
||||
|
||||
/* close draw line body */
|
||||
nvgLineTo(u->vg, head_start_x, head_start_y);
|
||||
|
||||
for(int32_t i = 0; i < ndash; i++) {
|
||||
float start_x = p1_x - w2_dx + dx * (i * dash_l + dash_width) * inv_dl;
|
||||
float start_y = p1_y + w2_dy + dy * (i * dash_l + dash_width) * inv_dl;
|
||||
|
||||
nvgMoveTo(u->vg, start_x, start_y);
|
||||
nvgLineTo(u->vg,
|
||||
p1_x + w2_dx + dx * (i * dash_l + dash_width) * inv_dl,
|
||||
p1_y - w2_dy + dy * (i * dash_l + dash_width) * inv_dl);
|
||||
nvgLineTo(u->vg,
|
||||
p1_x + w2_dx + dx * (i + 1) * dash_l * inv_dl,
|
||||
p1_y - w2_dy + dy * (i + 1) * dash_l * inv_dl);
|
||||
nvgLineTo(u->vg,
|
||||
p1_x - w2_dx + dx * (i + 1) * dash_l * inv_dl,
|
||||
p1_y + w2_dy + dy * (i + 1) * dash_l * inv_dl);
|
||||
nvgLineTo(u->vg, start_x, start_y);
|
||||
}
|
||||
|
||||
lv_nanovg_fill(
|
||||
u->vg,
|
||||
NVG_CCW,
|
||||
NVG_SOURCE_OVER,
|
||||
lv_nanovg_color_convert(dsc->color, dsc->opa));
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
81
src/draw/nanovg/lv_draw_nanovg_mask_rect.c
Normal file
81
src/draw/nanovg/lv_draw_nanovg_mask_rect.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_mask_rect.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "../../draw/lv_draw_mask_private.h"
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_mask_rect(lv_draw_task_t * t, const lv_draw_mask_rect_dsc_t * dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_area_t draw_area;
|
||||
|
||||
if(!lv_area_intersect(&draw_area, &dsc->area, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
|
||||
/* Nesting cropping regions using rounded rectangles and normal rectangles */
|
||||
lv_nanovg_path_append_rect(
|
||||
u->vg,
|
||||
dsc->area.x1, dsc->area.y1,
|
||||
lv_area_get_width(&dsc->area), lv_area_get_height(&dsc->area),
|
||||
dsc->radius);
|
||||
lv_nanovg_path_append_rect(
|
||||
u->vg,
|
||||
t->clip_area.x1, t->clip_area.y1,
|
||||
lv_area_get_width(&t->clip_area), lv_area_get_height(&t->clip_area),
|
||||
0);
|
||||
|
||||
/* Use NVG_DESTINATION_IN (Sa * D) blending mode to make the corners transparent */
|
||||
lv_nanovg_fill(
|
||||
u->vg,
|
||||
NVG_CCW,
|
||||
NVG_DESTINATION_IN,
|
||||
nvgRGBA(0, 0, 0, 0));
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
249
src/draw/nanovg/lv_draw_nanovg_private.h
Normal file
249
src/draw/nanovg/lv_draw_nanovg_private.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_DRAW_NANOVG_PRIVATE_H
|
||||
#define LV_DRAW_NANOVG_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
#include "../lv_draw.h"
|
||||
#include "../lv_draw_private.h"
|
||||
#include "../../draw/lv_draw_vector.h"
|
||||
#include "../../draw/lv_draw_arc.h"
|
||||
#include "../../draw/lv_draw_rect.h"
|
||||
#include "../../draw/lv_draw_image.h"
|
||||
#include "../../draw/lv_draw_label.h"
|
||||
#include "../../draw/lv_draw_line.h"
|
||||
#include "../../draw/lv_draw_triangle.h"
|
||||
#include "../../misc/lv_area_private.h"
|
||||
|
||||
#if !LV_USE_NANOVG
|
||||
#error "Require LV_USE_NANOVG = 1"
|
||||
#endif
|
||||
|
||||
#if !LV_USE_MATRIX
|
||||
#error "Require LV_USE_MATRIX = 1"
|
||||
#endif
|
||||
|
||||
#include "../../libs/nanovg/nanovg.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/* Select NanoVG OpenGL backend based on LV_NANOVG_BACKEND */
|
||||
#if LV_NANOVG_BACKEND == LV_NANOVG_BACKEND_GL2
|
||||
#define NANOVG_GL2_IMPLEMENTATION
|
||||
#elif LV_NANOVG_BACKEND == LV_NANOVG_BACKEND_GL3
|
||||
#define NANOVG_GL3_IMPLEMENTATION
|
||||
#elif LV_NANOVG_BACKEND == LV_NANOVG_BACKEND_GLES2
|
||||
#define NANOVG_GLES2_IMPLEMENTATION
|
||||
#elif LV_NANOVG_BACKEND == LV_NANOVG_BACKEND_GLES3
|
||||
#define NANOVG_GLES3_IMPLEMENTATION
|
||||
#else
|
||||
#error "Invalid LV_NANOVG_BACKEND value"
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_pending_t;
|
||||
struct NVGLUframebuffer;
|
||||
|
||||
typedef struct _lv_draw_nanovg_unit_t {
|
||||
lv_draw_unit_t base_unit;
|
||||
lv_layer_t * current_layer;
|
||||
NVGcontext * vg;
|
||||
bool is_started;
|
||||
lv_draw_buf_t * image_buf;
|
||||
|
||||
lv_cache_t * image_cache;
|
||||
struct _lv_pending_t * image_pending;
|
||||
lv_ll_t image_drop_ll;
|
||||
const void * image_drop_src;
|
||||
|
||||
lv_cache_t * letter_cache;
|
||||
struct _lv_pending_t * letter_pending;
|
||||
|
||||
lv_cache_t * fbo_cache;
|
||||
} lv_draw_nanovg_unit_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Draw arc on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to an arc descriptor
|
||||
* @param coords the coordinates of the arc
|
||||
*/
|
||||
void lv_draw_nanovg_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw border on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a border descriptor
|
||||
* @param coords the coordinates of the border
|
||||
*/
|
||||
void lv_draw_nanovg_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw box on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a box descriptor
|
||||
* @param coords the coordinates of the box
|
||||
*/
|
||||
void lv_draw_nanovg_box_shadow(lv_draw_task_t * t, const lv_draw_box_shadow_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Fill a rectangle on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a fill descriptor
|
||||
* @param coords the coordinates of the rectangle
|
||||
*/
|
||||
void lv_draw_nanovg_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw image on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to an image descriptor
|
||||
* @param coords the coordinates of the image
|
||||
* @param image_handle the handle of the image to draw
|
||||
*/
|
||||
void lv_draw_nanovg_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords,
|
||||
int image_handle);
|
||||
|
||||
/**
|
||||
* Initialize draw label on a NanoVG context
|
||||
* @param u pointer to a NanoVG unit
|
||||
*/
|
||||
void lv_draw_nanovg_label_init(lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Deinitialize draw label on a NanoVG context
|
||||
* @param u pointer to a NanoVG unit
|
||||
*/
|
||||
void lv_draw_nanovg_label_deinit(lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Draw letter on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a letter descriptor
|
||||
* @param coords the coordinates of the letter
|
||||
*/
|
||||
void lv_draw_nanovg_letter(lv_draw_task_t * t, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw label on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a label descriptor
|
||||
* @param coords the coordinates of the label
|
||||
*/
|
||||
void lv_draw_nanovg_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw layer on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param draw_dsc pointer to an image descriptor
|
||||
* @param coords the coordinates of the layer
|
||||
*/
|
||||
void lv_draw_nanovg_layer(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords);
|
||||
|
||||
/**
|
||||
* Draw line on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a line descriptor
|
||||
*/
|
||||
void lv_draw_nanovg_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc);
|
||||
|
||||
/**
|
||||
* Draw triangle on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a triangle descriptor
|
||||
*/
|
||||
void lv_draw_nanovg_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc);
|
||||
|
||||
/**
|
||||
* Draw mask rectangles on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a mask descriptor
|
||||
*/
|
||||
void lv_draw_nanovg_mask_rect(lv_draw_task_t * t, const lv_draw_mask_rect_dsc_t * dsc);
|
||||
|
||||
/**
|
||||
* Get image handle from framebuffer
|
||||
* @param fb the framebuffer to get the image handle from
|
||||
* @return the image handle
|
||||
*/
|
||||
int lv_nanovg_fb_get_image_handle(struct NVGLUframebuffer * fb);
|
||||
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
/**
|
||||
* Draw vector graphics on a NanoVG context
|
||||
* @param t pointer to a drawing task
|
||||
* @param dsc pointer to a vector descriptor
|
||||
*/
|
||||
void lv_draw_nanovg_vector(lv_draw_task_t * t, const lv_draw_vector_dsc_t * dsc);
|
||||
|
||||
/**
|
||||
* @brief Convert a gradient to a paint
|
||||
* @param ctx the nanovg context
|
||||
* @param grad the gradient descriptor
|
||||
* @param paint the paint to store the result
|
||||
*/
|
||||
bool lv_nanovg_grad_to_paint(NVGcontext * ctx, const lv_vector_gradient_t * grad, NVGpaint * paint);
|
||||
|
||||
/**
|
||||
* @brief Draw a gradient
|
||||
* @param ctx the nanovg context
|
||||
* @param grad the gradient descriptor
|
||||
* @param winding the fill rule
|
||||
* @param composite_operation the blend mode
|
||||
*/
|
||||
void lv_nanovg_draw_grad(
|
||||
NVGcontext * ctx,
|
||||
const lv_vector_gradient_t * grad,
|
||||
enum NVGwinding winding,
|
||||
enum NVGcompositeOperation composite_operation);
|
||||
|
||||
/**
|
||||
* @brief Draw a gradient with helper
|
||||
* @param ctx the nanovg context
|
||||
* @param area the area to draw the gradient on
|
||||
* @param grad_dsc the gradient descriptor
|
||||
* @param winding the fill rule
|
||||
* @param composite_operation the blend mode
|
||||
*/
|
||||
void lv_nanovg_draw_grad_helper(
|
||||
NVGcontext * ctx,
|
||||
const lv_area_t * area,
|
||||
const lv_grad_dsc_t * grad_dsc,
|
||||
enum NVGwinding winding,
|
||||
enum NVGcompositeOperation composite_operation);
|
||||
|
||||
#endif /*LV_USE_VECTOR_GRAPHIC*/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
|
||||
#endif /*LV_DRAW_NANOVG_PRIVATE_H*/
|
||||
85
src/draw/nanovg/lv_draw_nanovg_triangle.c
Normal file
85
src/draw/nanovg/lv_draw_nanovg_triangle.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_triangle.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_area_t tri_area;
|
||||
tri_area.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
|
||||
tri_area.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
|
||||
tri_area.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
|
||||
tri_area.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
|
||||
|
||||
lv_area_t clip_area;
|
||||
if(!lv_area_intersect(&clip_area, &tri_area, &t->clip_area)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
nvgBeginPath(u->vg);
|
||||
nvgMoveTo(u->vg, dsc->p[0].x, dsc->p[0].y);
|
||||
nvgLineTo(u->vg, dsc->p[1].x, dsc->p[1].y);
|
||||
nvgLineTo(u->vg, dsc->p[2].x, dsc->p[2].y);
|
||||
nvgClosePath(u->vg);
|
||||
|
||||
if(dsc->grad.dir != LV_GRAD_DIR_NONE) {
|
||||
#if LV_USE_VECTOR_GRAPHIC
|
||||
lv_nanovg_draw_grad_helper(u->vg, &tri_area, &dsc->grad, NVG_CCW, NVG_SOURCE_OVER);
|
||||
#else
|
||||
LV_LOG_WARN("Gradient fill is not supported without VECTOR_GRAPHIC");
|
||||
#endif
|
||||
}
|
||||
else { /* normal fill */
|
||||
lv_nanovg_fill(
|
||||
u->vg,
|
||||
NVG_CCW,
|
||||
NVG_SOURCE_OVER,
|
||||
lv_nanovg_color_convert(dsc->color, dsc->opa));
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
312
src/draw/nanovg/lv_draw_nanovg_vector.c
Normal file
312
src/draw/nanovg/lv_draw_nanovg_vector.c
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* @file lv_draw_nanovg_vector.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG && LV_USE_VECTOR_GRAPHIC
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
#include "../lv_draw_vector_private.h"
|
||||
#include "../lv_image_decoder_private.h"
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define OPA_MIX(opa1, opa2) LV_UDIV255((opa1) * (opa2))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_path_ctx_t * dsc);
|
||||
static void lv_path_to_nvg(NVGcontext * ctx, const lv_vector_path_t * src, lv_fpoint_t * offset);
|
||||
static enum NVGcompositeOperation lv_blend_to_nvg(lv_vector_blend_t blend);
|
||||
static enum NVGwinding lv_fill_to_nvg(lv_vector_fill_t fill_rule);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_draw_nanovg_vector(lv_draw_task_t * t, const lv_draw_vector_dsc_t * dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
if(dsc->task_list == NULL) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_layer_t * layer = dsc->base.layer;
|
||||
if(layer->draw_buf == NULL) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
|
||||
|
||||
nvgGlobalAlpha(u->vg, t->opa / (float)LV_OPA_COVER);
|
||||
|
||||
lv_vector_for_each_destroy_tasks(dsc->task_list, task_draw_cb, u);
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static NVGcolor lv_color32_to_nvg(lv_color32_t color, lv_opa_t opa)
|
||||
{
|
||||
uint8_t a = LV_UDIV255(color.alpha * opa);
|
||||
return nvgRGBA(color.red, color.green, color.blue, a);
|
||||
}
|
||||
|
||||
static void draw_fill(lv_draw_nanovg_unit_t * u, const lv_vector_fill_dsc_t * fill_dsc, const lv_fpoint_t * offset,
|
||||
enum NVGcompositeOperation comp_op)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
const enum NVGwinding winding = lv_fill_to_nvg(fill_dsc->fill_rule);
|
||||
|
||||
lv_nanovg_transform(u->vg, &fill_dsc->matrix);
|
||||
|
||||
switch(fill_dsc->style) {
|
||||
case LV_VECTOR_DRAW_STYLE_SOLID: {
|
||||
lv_nanovg_fill(u->vg, winding, comp_op, lv_color32_to_nvg(fill_dsc->color, fill_dsc->opa));
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_DRAW_STYLE_PATTERN: {
|
||||
const lv_draw_image_dsc_t * img_dsc = &fill_dsc->img_dsc;
|
||||
lv_image_header_t header;
|
||||
int image_handle = lv_nanovg_image_cache_get_handle(u, img_dsc->src, lv_color_to_32(img_dsc->recolor,
|
||||
img_dsc->recolor_opa), 0, &header);
|
||||
if(image_handle < 0) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
float offset_x = 0;
|
||||
float offset_y = 0;
|
||||
|
||||
if(fill_dsc->fill_units == LV_VECTOR_FILL_UNITS_OBJECT_BOUNDING_BOX) {
|
||||
offset_x = offset->x;
|
||||
offset_y = offset->y;
|
||||
}
|
||||
|
||||
NVGpaint paint = nvgImagePattern(u->vg, offset_x, offset_y, header.w, header.h, 0, image_handle,
|
||||
img_dsc->opa / (float)LV_OPA_COVER);
|
||||
|
||||
nvgFillPaint(u->vg, paint);
|
||||
nvgFill(u->vg);
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_DRAW_STYLE_GRADIENT: {
|
||||
lv_nanovg_draw_grad(u->vg, &fill_dsc->gradient, winding, comp_op);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("unsupported style: %d", fill_dsc->style);
|
||||
break;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static void draw_stroke(lv_draw_nanovg_unit_t * u, const lv_vector_stroke_dsc_t * stroke_dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_nanovg_transform(u->vg, &stroke_dsc->matrix);
|
||||
|
||||
nvgStrokeColor(u->vg, lv_color32_to_nvg(stroke_dsc->color, stroke_dsc->opa));
|
||||
nvgStrokeWidth(u->vg, stroke_dsc->width);
|
||||
|
||||
switch(stroke_dsc->style) {
|
||||
case LV_VECTOR_DRAW_STYLE_SOLID:
|
||||
break;
|
||||
|
||||
case LV_VECTOR_DRAW_STYLE_GRADIENT: {
|
||||
NVGpaint paint;
|
||||
if(!lv_nanovg_grad_to_paint(u->vg, &stroke_dsc->gradient, &paint)) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
nvgStrokePaint(u->vg, paint);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_LOG_WARN("unsupported style: %d", stroke_dsc->style);
|
||||
break;
|
||||
}
|
||||
|
||||
nvgStroke(u->vg);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_path_ctx_t * dsc)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
lv_draw_nanovg_unit_t * u = ctx;
|
||||
|
||||
/* clear area */
|
||||
if(!path) {
|
||||
NVGcolor c = lv_color32_to_nvg(dsc->fill_dsc.color, dsc->fill_dsc.opa);
|
||||
nvgBeginPath(u->vg);
|
||||
lv_nanovg_path_append_area(u->vg, &dsc->scissor_area);
|
||||
lv_nanovg_fill(u->vg, NVG_CCW, NVG_COPY, c);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
if(dsc->fill_dsc.opa == LV_OPA_TRANSP && dsc->stroke_dsc.opa == LV_OPA_TRANSP) {
|
||||
LV_LOG_TRACE("Full transparent, no need to draw");
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
nvgSave(u->vg);
|
||||
lv_nanovg_transform(u->vg, &dsc->matrix);
|
||||
|
||||
lv_fpoint_t offset = {0, 0};
|
||||
lv_path_to_nvg(u->vg, path, &offset);
|
||||
|
||||
lv_nanovg_set_clip_area(u->vg, &dsc->scissor_area);
|
||||
|
||||
const enum NVGcompositeOperation comp_op = lv_blend_to_nvg(dsc->blend_mode);
|
||||
nvgGlobalCompositeOperation(u->vg, comp_op);
|
||||
|
||||
if(dsc->fill_dsc.opa) {
|
||||
draw_fill(u, &dsc->fill_dsc, &offset, comp_op);
|
||||
}
|
||||
|
||||
if(dsc->stroke_dsc.opa) {
|
||||
draw_stroke(u, &dsc->stroke_dsc);
|
||||
}
|
||||
|
||||
nvgRestore(u->vg);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static void lv_path_to_nvg(NVGcontext * ctx, const lv_vector_path_t * src, lv_fpoint_t * offset)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
float min_x = FLT_MAX;
|
||||
float min_y = FLT_MAX;
|
||||
float max_x = -FLT_MAX;
|
||||
float max_y = -FLT_MAX;
|
||||
|
||||
#define CMP_BOUNDS(point) \
|
||||
do { \
|
||||
if((point)->x < min_x) min_x = (point)->x; \
|
||||
if((point)->y < min_y) min_y = (point)->y; \
|
||||
if((point)->x > max_x) max_x = (point)->x; \
|
||||
if((point)->y > max_y) max_y = (point)->y; \
|
||||
} while(0)
|
||||
|
||||
const lv_vector_path_op_t * ops = lv_array_front(&src->ops);
|
||||
const lv_fpoint_t * point = lv_array_front(&src->points);
|
||||
const uint32_t op_size = lv_array_size(&src->ops);
|
||||
|
||||
nvgBeginPath(ctx);
|
||||
|
||||
for(uint32_t i = 0; i < op_size; i++) {
|
||||
switch(ops[i]) {
|
||||
case LV_VECTOR_PATH_OP_MOVE_TO: {
|
||||
nvgMoveTo(ctx, point->x, point->y);
|
||||
CMP_BOUNDS(point);
|
||||
point++;
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_PATH_OP_LINE_TO: {
|
||||
nvgLineTo(ctx, point->x, point->y);
|
||||
CMP_BOUNDS(point);
|
||||
point++;
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_PATH_OP_QUAD_TO: {
|
||||
nvgQuadTo(ctx, point[0].x, point[0].y, point[1].x, point[1].y);
|
||||
CMP_BOUNDS(&point[0]);
|
||||
CMP_BOUNDS(&point[1]);
|
||||
point += 2;
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_PATH_OP_CUBIC_TO: {
|
||||
nvgBezierTo(ctx, point[0].x, point[0].y, point[1].x, point[1].y, point[2].x, point[2].y);
|
||||
CMP_BOUNDS(&point[0]);
|
||||
CMP_BOUNDS(&point[1]);
|
||||
CMP_BOUNDS(&point[2]);
|
||||
point += 3;
|
||||
}
|
||||
break;
|
||||
case LV_VECTOR_PATH_OP_CLOSE: {
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("unknown op: %d", ops[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offset->x = lroundf(min_x);
|
||||
offset->y = lroundf(min_y);
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static enum NVGcompositeOperation lv_blend_to_nvg(lv_vector_blend_t blend)
|
||||
{
|
||||
switch(blend) {
|
||||
case LV_VECTOR_BLEND_SRC_OVER:
|
||||
return NVG_SOURCE_OVER;
|
||||
case LV_VECTOR_BLEND_SRC_IN:
|
||||
return NVG_SOURCE_IN;
|
||||
case LV_VECTOR_BLEND_DST_OVER:
|
||||
return NVG_DESTINATION_OVER;
|
||||
case LV_VECTOR_BLEND_DST_IN:
|
||||
return NVG_DESTINATION_IN;
|
||||
case LV_VECTOR_BLEND_NONE:
|
||||
return NVG_COPY;
|
||||
default:
|
||||
LV_LOG_INFO("Unknown supported blend mode: %d", blend);
|
||||
return NVG_SOURCE_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
static enum NVGwinding lv_fill_to_nvg(lv_vector_fill_t fill_rule)
|
||||
{
|
||||
switch(fill_rule) {
|
||||
case LV_VECTOR_FILL_NONZERO:
|
||||
return NVG_CCW;
|
||||
case LV_VECTOR_FILL_EVENODD:
|
||||
return NVG_CW;
|
||||
default:
|
||||
LV_LOG_WARN("Unknown supported fill rule: %d", fill_rule);
|
||||
return NVG_CCW;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
176
src/draw/nanovg/lv_nanovg_fbo_cache.c
Normal file
176
src/draw/nanovg/lv_nanovg_fbo_cache.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file lv_nanovg_fbo_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_nanovg_fbo_cache.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "../../libs/nanovg/nanovg_gl_utils.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_NANOVG_FBO_CACHE_CNT 4
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
/* context */
|
||||
lv_draw_nanovg_unit_t * u;
|
||||
|
||||
/* key */
|
||||
int width;
|
||||
int height;
|
||||
int flags;
|
||||
enum NVGtexture format;
|
||||
|
||||
/* value */
|
||||
struct NVGLUframebuffer * fbo;
|
||||
} fbo_item_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static bool fbo_create_cb(fbo_item_t * item, void * user_data);
|
||||
static void fbo_free_cb(fbo_item_t * item, void * user_data);
|
||||
static lv_cache_compare_res_t fbo_compare_cb(const fbo_item_t * lhs, const fbo_item_t * rhs);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_nanovg_fbo_cache_init(lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->fbo_cache == NULL);
|
||||
|
||||
const lv_cache_ops_t ops = {
|
||||
.compare_cb = (lv_cache_compare_cb_t)fbo_compare_cb,
|
||||
.create_cb = (lv_cache_create_cb_t)fbo_create_cb,
|
||||
.free_cb = (lv_cache_free_cb_t)fbo_free_cb,
|
||||
};
|
||||
|
||||
u->fbo_cache = lv_cache_create(&lv_cache_class_lru_ll_count, sizeof(fbo_item_t), LV_NANOVG_FBO_CACHE_CNT, ops);
|
||||
lv_cache_set_name(u->fbo_cache, "NVG_FBO");
|
||||
}
|
||||
|
||||
void lv_nanovg_fbo_cache_deinit(lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->fbo_cache);
|
||||
|
||||
lv_cache_destroy(u->fbo_cache, NULL);
|
||||
u->fbo_cache = NULL;
|
||||
}
|
||||
|
||||
struct _lv_cache_entry_t * lv_nanovg_fbo_cache_get(lv_draw_nanovg_unit_t * u, int width, int height, int flags,
|
||||
int format)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_ASSERT_NULL(u);
|
||||
|
||||
fbo_item_t search_key = { 0 };
|
||||
search_key.u = u;
|
||||
search_key.width = width;
|
||||
search_key.height = height;
|
||||
search_key.flags = flags;
|
||||
search_key.format = format;
|
||||
|
||||
lv_cache_entry_t * cache_node_entry = lv_cache_acquire(u->fbo_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
cache_node_entry = lv_cache_acquire_or_create(u->fbo_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
LV_LOG_ERROR("FBO cache creating failed");
|
||||
LV_PROFILER_DRAW_END;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return cache_node_entry;
|
||||
}
|
||||
|
||||
void lv_nanovg_fbo_cache_release(struct _lv_draw_nanovg_unit_t * u, struct _lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT_NULL(entry);
|
||||
lv_cache_release(u->fbo_cache, entry, NULL);
|
||||
}
|
||||
|
||||
struct NVGLUframebuffer * lv_nanovg_fbo_cache_entry_to_fb(struct _lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
fbo_item_t * fbo_item = lv_cache_entry_get_data(entry);
|
||||
return fbo_item->fbo;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static bool fbo_create_cb(fbo_item_t * item, void * user_data)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
item->fbo = nvgluCreateFramebuffer(item->u->vg, item->width, item->height, item->flags, item->format);
|
||||
if(!item->fbo) {
|
||||
LV_LOG_ERROR("Failed to create FBO");
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return item->fbo != NULL;
|
||||
}
|
||||
|
||||
static void fbo_free_cb(fbo_item_t * item, void * user_data)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
nvgluDeleteFramebuffer(item->fbo);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static lv_cache_compare_res_t fbo_compare_cb(const fbo_item_t * lhs, const fbo_item_t * rhs)
|
||||
{
|
||||
if(lhs->width != rhs->width) {
|
||||
return lhs->width > rhs->width ? 1 : -1;
|
||||
}
|
||||
|
||||
if(lhs->height != rhs->height) {
|
||||
return lhs->height > rhs->height ? 1 : -1;
|
||||
}
|
||||
|
||||
if(lhs->flags != rhs->flags) {
|
||||
return lhs->flags > rhs->flags ? 1 : -1;
|
||||
}
|
||||
|
||||
if(lhs->format != rhs->format) {
|
||||
return lhs->format > rhs->format ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
85
src/draw/nanovg/lv_nanovg_fbo_cache.h
Normal file
85
src/draw/nanovg/lv_nanovg_fbo_cache.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file lv_nanovg_fbo_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_NANOVG_FBO_CACHE_H
|
||||
#define LV_NANOVG_FBO_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_draw_nanovg_unit_t;
|
||||
struct _lv_cache_entry_t;
|
||||
struct NVGLUframebuffer;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief Initialize the FBO cache
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_fbo_cache_init(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the FBO cache
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_fbo_cache_deinit(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* @brief Get the FBO from the cache, create a new one if not found
|
||||
* @param u pointer to the nanovg unit
|
||||
* @param width the width of the FBO
|
||||
* @param height the height of the FBO
|
||||
* @param flags the FBO flags
|
||||
* @param format the texture format
|
||||
* @return the FBO cache entry, or NULL if not found
|
||||
*/
|
||||
struct _lv_cache_entry_t * lv_nanovg_fbo_cache_get(struct _lv_draw_nanovg_unit_t * u, int width, int height, int flags,
|
||||
int format);
|
||||
|
||||
/**
|
||||
* @brief Release the FBO from the cache
|
||||
* @param u pointer to the nanovg unit
|
||||
* @param entry the FBO cache entry to release
|
||||
*/
|
||||
void lv_nanovg_fbo_cache_release(struct _lv_draw_nanovg_unit_t * u, struct _lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* @brief Convert a cache entry to a framebuffer
|
||||
* @param entry the FBO cache entry
|
||||
* @return the framebuffer pointer
|
||||
*/
|
||||
struct NVGLUframebuffer * lv_nanovg_fbo_cache_entry_to_fb(struct _lv_cache_entry_t * entry);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_NANOVG_FBO_CACHE_H*/
|
||||
354
src/draw/nanovg/lv_nanovg_image_cache.c
Normal file
354
src/draw/nanovg/lv_nanovg_image_cache.c
Normal file
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* @file lv_nanovg_image_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_nanovg_image_cache.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
#include "lv_nanovg_utils.h"
|
||||
#include "../lv_image_decoder_private.h"
|
||||
#include "../../misc/lv_pending.h"
|
||||
#include "../../misc/lv_iter.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
/* context */
|
||||
lv_draw_nanovg_unit_t * u;
|
||||
|
||||
/* key */
|
||||
lv_draw_buf_t src_buf;
|
||||
lv_color32_t color;
|
||||
int image_flags;
|
||||
|
||||
/* for drop search */
|
||||
const void * src;
|
||||
lv_image_src_t src_type;
|
||||
|
||||
/* value */
|
||||
int image_handle;
|
||||
} image_item_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void image_cache_release_cb(void * entry, void * user_data);
|
||||
static bool image_create_cb(image_item_t * item, void * user_data);
|
||||
static void image_free_cb(image_item_t * item, void * user_data);
|
||||
static lv_cache_compare_res_t image_compare_cb(const image_item_t * lhs, const image_item_t * rhs);
|
||||
static void image_cache_drop_collect_cb(void * elem);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_nanovg_image_cache_init(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->image_cache == NULL);
|
||||
LV_ASSERT(u->image_pending == NULL);
|
||||
|
||||
const lv_cache_ops_t ops = {
|
||||
.compare_cb = (lv_cache_compare_cb_t)image_compare_cb,
|
||||
.create_cb = (lv_cache_create_cb_t)image_create_cb,
|
||||
.free_cb = (lv_cache_free_cb_t)image_free_cb,
|
||||
};
|
||||
|
||||
u->image_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(image_item_t), LV_NANOVG_IMAGE_CACHE_CNT, ops);
|
||||
lv_cache_set_name(u->image_cache, "NVG_IMAGE");
|
||||
u->image_pending = lv_pending_create(sizeof(lv_cache_entry_t *), 4);
|
||||
lv_pending_set_free_cb(u->image_pending, image_cache_release_cb, u->image_cache);
|
||||
|
||||
lv_ll_init(&u->image_drop_ll, sizeof(image_item_t));
|
||||
}
|
||||
|
||||
void lv_nanovg_image_cache_deinit(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT(u->image_cache);
|
||||
LV_ASSERT(u->image_pending);
|
||||
|
||||
lv_pending_destroy(u->image_pending);
|
||||
u->image_pending = NULL;
|
||||
|
||||
lv_cache_destroy(u->image_cache, NULL);
|
||||
u->image_cache = NULL;
|
||||
}
|
||||
|
||||
int lv_nanovg_image_cache_get_handle(struct _lv_draw_nanovg_unit_t * u,
|
||||
const void * src,
|
||||
lv_color32_t color,
|
||||
int image_flags,
|
||||
lv_image_header_t * header)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_ASSERT_NULL(src);
|
||||
|
||||
lv_image_decoder_args_t args;
|
||||
lv_memzero(&args, sizeof(lv_image_decoder_args_t));
|
||||
|
||||
lv_image_decoder_dsc_t decoder_dsc;
|
||||
lv_result_t res = lv_image_decoder_open(&decoder_dsc, src, &args);
|
||||
if(res != LV_RESULT_OK) {
|
||||
lv_image_src_t type = lv_image_src_get_type(src);
|
||||
LV_UNUSED(type);
|
||||
LV_LOG_WARN("Failed to open image: type: %d, src: %p (%s)", type, src,
|
||||
type == LV_IMAGE_SRC_FILE ? (const char *)src : "var");
|
||||
LV_PROFILER_DRAW_END;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const lv_draw_buf_t * decoded = decoder_dsc.decoded;
|
||||
if(decoded == NULL || decoded->data == NULL) {
|
||||
lv_image_decoder_close(&decoder_dsc);
|
||||
LV_LOG_ERROR("image data is NULL");
|
||||
LV_PROFILER_DRAW_END;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(header) {
|
||||
*header = decoder_dsc.header;
|
||||
}
|
||||
|
||||
image_item_t search_key = { 0 };
|
||||
search_key.u = u;
|
||||
search_key.src_buf = *decoded;
|
||||
search_key.color = color;
|
||||
search_key.image_flags = image_flags;
|
||||
search_key.src = src;
|
||||
search_key.src_type = lv_image_src_get_type(src);
|
||||
|
||||
lv_cache_entry_t * cache_node_entry = lv_cache_acquire(u->image_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
/* check if the cache is full */
|
||||
size_t free_size = lv_cache_get_free_size(u->image_cache, NULL);
|
||||
if(free_size == 0) {
|
||||
LV_LOG_INFO("image cache is full, release all pending cache entries");
|
||||
lv_nanovg_end_frame(u);
|
||||
}
|
||||
|
||||
cache_node_entry = lv_cache_acquire_or_create(u->image_cache, &search_key, NULL);
|
||||
if(cache_node_entry == NULL) {
|
||||
LV_LOG_ERROR("image cache creating failed");
|
||||
lv_image_decoder_close(&decoder_dsc);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
lv_image_decoder_close(&decoder_dsc);
|
||||
|
||||
/* Add the new entry to the pending list */
|
||||
lv_pending_add(u->image_pending, &cache_node_entry);
|
||||
|
||||
image_item_t * image_item = lv_cache_entry_get_data(cache_node_entry);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
return image_item->image_handle;
|
||||
}
|
||||
|
||||
void lv_nanovg_image_cache_drop(struct _lv_draw_nanovg_unit_t * u, const void * src)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_UNUSED(src);
|
||||
if(src == NULL) {
|
||||
lv_cache_drop_all(u->image_cache, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
u->image_drop_src = src;
|
||||
|
||||
lv_iter_t * iter = lv_cache_iter_create(u->image_cache);
|
||||
LV_ASSERT_NULL(iter);
|
||||
|
||||
/* Collect all cache entries that match the drop source */
|
||||
lv_iter_inspect(iter, image_cache_drop_collect_cb);
|
||||
|
||||
image_item_t * drop_item;
|
||||
LV_LL_READ(&u->image_drop_ll, drop_item) {
|
||||
lv_cache_drop(u->image_cache, drop_item, NULL);
|
||||
}
|
||||
|
||||
lv_ll_clear(&u->image_drop_ll);
|
||||
lv_iter_destroy(iter);
|
||||
u->image_drop_src = NULL;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void image_cache_release_cb(void * entry, void * user_data)
|
||||
{
|
||||
lv_cache_entry_t ** entry_p = entry;
|
||||
lv_cache_t * cache = user_data;
|
||||
lv_cache_release(cache, * entry_p, NULL);
|
||||
}
|
||||
|
||||
static bool image_create_cb(image_item_t * item, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
const uint32_t w = item->src_buf.header.w;
|
||||
const uint32_t h = item->src_buf.header.h;
|
||||
const lv_color_format_t cf = item->src_buf.header.cf;
|
||||
const uint32_t stride = item->src_buf.header.stride;
|
||||
enum NVGtexture nvg_tex_type = NVG_TEXTURE_BGRA;
|
||||
|
||||
/* Determine texture type and pixel size based on color format */
|
||||
switch(cf) {
|
||||
case LV_COLOR_FORMAT_A8:
|
||||
nvg_tex_type = NVG_TEXTURE_ALPHA;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_ARGB8888:
|
||||
case LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED:
|
||||
nvg_tex_type = NVG_TEXTURE_BGRA;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_XRGB8888:
|
||||
nvg_tex_type = NVG_TEXTURE_BGRX;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_RGB888:
|
||||
nvg_tex_type = NVG_TEXTURE_BGR;
|
||||
break;
|
||||
|
||||
case LV_COLOR_FORMAT_RGB565:
|
||||
nvg_tex_type = NVG_TEXTURE_RGB565;
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_LOG_ERROR("Unsupported image format: %d", cf);
|
||||
return false;
|
||||
}
|
||||
|
||||
void * data = NULL;
|
||||
|
||||
/* Check if stride is tightly packed */
|
||||
uint32_t tight_stride = (w * lv_color_format_get_bpp(cf) + 7) >> 3;
|
||||
if(stride == tight_stride) {
|
||||
/* Stride matches, use source buffer directly (zero-copy) */
|
||||
data = lv_draw_buf_goto_xy(&item->src_buf, 0, 0);
|
||||
LV_LOG_TRACE("Image stride matches: %" LV_PRIu32, stride);
|
||||
}
|
||||
else {
|
||||
/* Stride doesn't match, need to copy with tight alignment */
|
||||
lv_draw_buf_t * tmp_buf = lv_nanovg_reshape_global_image(item->u, cf, w, h);
|
||||
if(!tmp_buf) {
|
||||
LV_LOG_ERROR("Failed to allocate temp buffer for stride conversion");
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_draw_buf_copy(tmp_buf, NULL, &item->src_buf, NULL);
|
||||
data = lv_draw_buf_goto_xy(tmp_buf, 0, 0);
|
||||
LV_LOG_TRACE("Image stride converted: %" LV_PRIu32 " -> %" LV_PRIu32, stride, tight_stride);
|
||||
}
|
||||
|
||||
int flags = item->image_flags;
|
||||
if(cf == LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED
|
||||
|| lv_draw_buf_has_flag(&item->src_buf, LV_IMAGE_FLAGS_PREMULTIPLIED)) {
|
||||
flags |= NVG_IMAGE_PREMULTIPLIED;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("nvgCreateImage");
|
||||
int image_handle = nvgCreateImage(item->u->vg, w, h, flags, nvg_tex_type, data);
|
||||
LV_PROFILER_DRAW_END_TAG("nvgCreateImage");
|
||||
|
||||
if(image_handle < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(item->src_type == LV_IMAGE_SRC_FILE) {
|
||||
item->src = lv_strdup(item->src);
|
||||
LV_ASSERT_MALLOC(item->src);
|
||||
}
|
||||
|
||||
item->image_handle = image_handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void image_free_cb(image_item_t * item, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
LV_LOG_TRACE("image_handle: %d", item->image_handle);
|
||||
nvgDeleteImage(item->u->vg, item->image_handle);
|
||||
item->image_handle = -1;
|
||||
|
||||
if(item->src_type == LV_IMAGE_SRC_FILE) {
|
||||
lv_free((void *)item->src);
|
||||
item->src = NULL;
|
||||
}
|
||||
|
||||
item->src_type = LV_IMAGE_SRC_UNKNOWN;
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
static lv_cache_compare_res_t image_compare_cb(const image_item_t * lhs, const image_item_t * rhs)
|
||||
{
|
||||
if(lhs->image_flags != rhs->image_flags) {
|
||||
return lhs->image_flags > rhs->image_flags ? 1 : -1;
|
||||
}
|
||||
|
||||
uint32_t lhs_color = *(uint32_t *)&lhs->color;
|
||||
uint32_t rhs_color = *(uint32_t *)&rhs->color;
|
||||
|
||||
if(lhs_color != rhs_color) {
|
||||
return lhs_color > rhs_color ? 1 : -1;
|
||||
}
|
||||
|
||||
int cmp_res = lv_memcmp(&lhs->src_buf, &rhs->src_buf, sizeof(lv_draw_buf_t));
|
||||
if(cmp_res != 0) {
|
||||
return cmp_res > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void image_cache_drop_collect_cb(void * elem)
|
||||
{
|
||||
/**
|
||||
* If the cache is deleted during the traversal process,
|
||||
* it will cause iter to become invalid.
|
||||
* Therefore, we will first add it to the drop collection list and postpone the deletion.
|
||||
*/
|
||||
LV_ASSERT_NULL(elem);
|
||||
image_item_t * item = elem;
|
||||
const void * src = item->u->image_drop_src;
|
||||
LV_ASSERT_NULL(src);
|
||||
lv_image_src_t src_type = lv_image_src_get_type(src);
|
||||
|
||||
if((src_type == LV_IMAGE_SRC_FILE && lv_strcmp(item->src, src) == 0)
|
||||
|| (src_type == LV_IMAGE_SRC_VARIABLE && item->src == src)) {
|
||||
image_item_t * drop_item = lv_ll_ins_tail(&item->u->image_drop_ll);
|
||||
LV_ASSERT_MALLOC(drop_item);
|
||||
*drop_item = *item;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
81
src/draw/nanovg/lv_nanovg_image_cache.h
Normal file
81
src/draw/nanovg/lv_nanovg_image_cache.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file lv_nanovg_image_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_NANOVG_IMAGE_CACHE_H
|
||||
#define LV_NANOVG_IMAGE_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "../lv_draw_image_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_draw_nanovg_unit_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief Initialize the image cache
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_image_cache_init(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the image cache
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_image_cache_deinit(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* @brief Get the image handle from the cache, create a new one if not found
|
||||
* @param u pointer to the nanovg unit
|
||||
* @param src the source image data
|
||||
* @param color the color to apply
|
||||
* @param image_flags the image flags
|
||||
* @param header the image header to fill (can be NULL)
|
||||
* @return the image handle, or -1 on failure
|
||||
*/
|
||||
int lv_nanovg_image_cache_get_handle(struct _lv_draw_nanovg_unit_t * u,
|
||||
const void * src,
|
||||
lv_color32_t color,
|
||||
int image_flags,
|
||||
lv_image_header_t * header);
|
||||
|
||||
/**
|
||||
* @brief Drop the image from the cache
|
||||
* @param u pointer to the nanovg unit
|
||||
* @param src the source image data
|
||||
*/
|
||||
void lv_nanovg_image_cache_drop(struct _lv_draw_nanovg_unit_t * u, const void * src);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_NANOVG_IMAGE_CACHE_H*/
|
||||
102
src/draw/nanovg/lv_nanovg_math.h
Normal file
102
src/draw/nanovg/lv_nanovg_math.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @file lv_nanovg_math.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_NANOVG_MATH_H
|
||||
#define LV_NANOVG_MATH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <float.h>
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define NVG_MATH_PI 3.14159265358979323846f
|
||||
#define NVG_MATH_HALF_PI 1.57079632679489661923f
|
||||
#define NVG_MATH_TWO_PI 6.28318530717958647692f
|
||||
#define NVG_DEG_TO_RAD 0.017453292519943295769236907684886f
|
||||
#define NVG_RAD_TO_DEG 57.295779513082320876798154814105f
|
||||
|
||||
#define NVG_MATH_TANF(x) tanf(x)
|
||||
#define NVG_MATH_SINF(x) sinf(x)
|
||||
#define NVG_MATH_COSF(x) cosf(x)
|
||||
#define NVG_MATH_ASINF(x) asinf(x)
|
||||
#define NVG_MATH_ACOSF(x) acosf(x)
|
||||
#define NVG_MATH_FABSF(x) fabsf(x)
|
||||
#define NVG_MATH_SQRTF(x) sqrtf(x)
|
||||
|
||||
#define NVG_MATH_RADIANS(deg) ((deg) * NVG_DEG_TO_RAD)
|
||||
#define NVG_MATH_DEGREES(rad) ((rad) * NVG_RAD_TO_DEG)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Check if the floating point number is zero
|
||||
* @param a the number to check
|
||||
* @return true if the number is zero, false otherwise
|
||||
*/
|
||||
static inline bool nvg_math_is_zero(float a)
|
||||
{
|
||||
return (NVG_MATH_FABSF(a) < FLT_EPSILON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two floating point numbers are equal
|
||||
* @param a the first number
|
||||
* @param b the second number
|
||||
* @return true if the numbers are equal, false otherwise
|
||||
*/
|
||||
static inline bool nvg_math_is_equal(float a, float b)
|
||||
{
|
||||
return nvg_math_is_zero(a - b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the inverse square root (1/sqrt(x))
|
||||
* @param number the input number
|
||||
* @return the inverse square root
|
||||
*/
|
||||
static inline float nvg_math_inv_sqrtf(float number)
|
||||
{
|
||||
/* From https://en.wikipedia.org/wiki/Fast_inverse_square_root#Avoiding_undefined_behavior */
|
||||
union {
|
||||
float f;
|
||||
int32_t i;
|
||||
} conv = { .f = number };
|
||||
conv.i = 0x5f3759df - (conv.i >> 1);
|
||||
conv.f *= 1.5F - (number * 0.5F * conv.f * conv.f);
|
||||
return conv.f;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_DRAW_NANOVG*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_NANOVG_MATH_H*/
|
||||
301
src/draw/nanovg/lv_nanovg_utils.c
Normal file
301
src/draw/nanovg/lv_nanovg_utils.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* @file lv_nanovg_utils.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_nanovg_utils.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "../../misc/lv_pending.h"
|
||||
#include "lv_draw_nanovg_private.h"
|
||||
#include "lv_nanovg_math.h"
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/* Magic number from https://spencermortensen.com/articles/bezier-circle/ */
|
||||
#define PATH_ARC_MAGIC 0.55191502449351f
|
||||
|
||||
#define SIGN(x) (nvg_math_is_zero(x) ? 0 : ((x) > 0 ? 1 : -1))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_nanovg_utils_init(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
}
|
||||
|
||||
void lv_nanovg_utils_deinit(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
|
||||
if(u->image_buf) {
|
||||
lv_draw_buf_destroy(u->image_buf);
|
||||
u->image_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void lv_nanovg_transform(NVGcontext * ctx, const lv_matrix_t * matrix)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
LV_ASSERT_NULL(matrix);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
nvgTransform(ctx,
|
||||
matrix->m[0][0],
|
||||
matrix->m[1][0],
|
||||
matrix->m[0][1],
|
||||
matrix->m[1][1],
|
||||
matrix->m[0][2],
|
||||
matrix->m[1][2]);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_set_clip_area(NVGcontext * ctx, const lv_area_t * area)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
LV_ASSERT_NULL(area);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
nvgScissor(ctx,
|
||||
area->x1, area->y1,
|
||||
lv_area_get_width(area), lv_area_get_height(area));
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_path_append_rect(NVGcontext * ctx, float x, float y, float w, float h, float r)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
if(r > 0) {
|
||||
const float half_w = w / 2.0f;
|
||||
const float half_h = h / 2.0f;
|
||||
|
||||
/*clamping cornerRadius by minimum size*/
|
||||
const float r_max = LV_MIN(half_w, half_h);
|
||||
|
||||
nvgRoundedRect(ctx, x, y, w, h, r > r_max ? r_max : r);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
nvgRect(ctx, x, y, w, h);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_path_append_area(NVGcontext * ctx, const lv_area_t * area)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
LV_ASSERT_NULL(area);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
nvgRect(ctx, area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area));
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_path_append_arc_right_angle(NVGcontext * ctx,
|
||||
float start_x, float start_y,
|
||||
float center_x, float center_y,
|
||||
float end_x, float end_y)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
float dx1 = center_x - start_x;
|
||||
float dy1 = center_y - start_y;
|
||||
float dx2 = end_x - center_x;
|
||||
float dy2 = end_y - center_y;
|
||||
|
||||
float c = SIGN(dx1 * dy2 - dx2 * dy1) * PATH_ARC_MAGIC;
|
||||
|
||||
nvgBezierTo(ctx,
|
||||
start_x - c * dy1, start_y + c * dx1,
|
||||
end_x - c * dy2, end_y + c * dx2,
|
||||
end_x, end_y);
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_path_append_arc(NVGcontext * ctx,
|
||||
float cx, float cy,
|
||||
float radius,
|
||||
float start_angle,
|
||||
float sweep,
|
||||
bool pie)
|
||||
{
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
if(radius <= 0) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
/* just circle */
|
||||
if(sweep >= 360.0f || sweep <= -360.0f) {
|
||||
nvgCircle(ctx, cx, cy, radius);
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
start_angle = NVG_MATH_RADIANS(start_angle);
|
||||
sweep = NVG_MATH_RADIANS(sweep);
|
||||
|
||||
int n_curves = (int)ceil(NVG_MATH_FABSF(sweep / NVG_MATH_HALF_PI));
|
||||
float sweep_sign = sweep < 0 ? -1.f : 1.f;
|
||||
float fract = fmodf(sweep, NVG_MATH_HALF_PI);
|
||||
fract = (nvg_math_is_zero(fract)) ? NVG_MATH_HALF_PI * sweep_sign : fract;
|
||||
|
||||
/* Start from here */
|
||||
float start_x = radius * NVG_MATH_COSF(start_angle);
|
||||
float start_y = radius * NVG_MATH_SINF(start_angle);
|
||||
|
||||
if(pie) {
|
||||
nvgMoveTo(ctx, cx, cy);
|
||||
nvgLineTo(ctx, start_x + cx, start_y + cy);
|
||||
}
|
||||
|
||||
for(int i = 0; i < n_curves; ++i) {
|
||||
float end_angle = start_angle + ((i != n_curves - 1) ? NVG_MATH_HALF_PI * sweep_sign : fract);
|
||||
float end_x = radius * NVG_MATH_COSF(end_angle);
|
||||
float end_y = radius * NVG_MATH_SINF(end_angle);
|
||||
|
||||
/* variables needed to calculate bezier control points */
|
||||
|
||||
/** get bezier control points using article:
|
||||
* (http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
|
||||
*/
|
||||
float ax = start_x;
|
||||
float ay = start_y;
|
||||
float bx = end_x;
|
||||
float by = end_y;
|
||||
float q1 = ax * ax + ay * ay;
|
||||
float q2 = ax * bx + ay * by + q1;
|
||||
float k2 = (4.0f / 3.0f) * ((NVG_MATH_SQRTF(2 * q1 * q2) - q2) / (ax * by - ay * bx));
|
||||
|
||||
/* Next start point is the current end point */
|
||||
start_x = end_x;
|
||||
start_y = end_y;
|
||||
|
||||
end_x += cx;
|
||||
end_y += cy;
|
||||
|
||||
float ctrl1_x = ax - k2 * ay + cx;
|
||||
float ctrl1_y = ay + k2 * ax + cy;
|
||||
float ctrl2_x = bx + k2 * by + cx;
|
||||
float ctrl2_y = by - k2 * bx + cy;
|
||||
|
||||
nvgBezierTo(ctx, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, end_x, end_y);
|
||||
start_angle = end_angle;
|
||||
}
|
||||
|
||||
if(pie) {
|
||||
nvgClosePath(ctx);
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_fill(NVGcontext * ctx, enum NVGwinding winding, enum NVGcompositeOperation composite_operation,
|
||||
NVGcolor color)
|
||||
{
|
||||
LV_ASSERT_NULL(ctx);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
nvgPathWinding(ctx, winding);
|
||||
nvgGlobalCompositeOperation(ctx, composite_operation);
|
||||
nvgFillColor(ctx, color);
|
||||
nvgFill(ctx);
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_end_frame(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
if(!u->is_started) {
|
||||
LV_PROFILER_DRAW_END;
|
||||
return;
|
||||
}
|
||||
|
||||
LV_PROFILER_DRAW_BEGIN_TAG("nvgEndFrame");
|
||||
nvgEndFrame(u->vg);
|
||||
LV_PROFILER_DRAW_END_TAG("nvgEndFrame");
|
||||
|
||||
lv_nanovg_clean_up(u);
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
void lv_nanovg_clean_up(struct _lv_draw_nanovg_unit_t * u)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
LV_PROFILER_DRAW_BEGIN;
|
||||
|
||||
lv_pending_remove_all(u->image_pending);
|
||||
lv_pending_remove_all(u->letter_pending);
|
||||
u->is_started = false;
|
||||
|
||||
LV_PROFILER_DRAW_END;
|
||||
}
|
||||
|
||||
lv_draw_buf_t * lv_nanovg_reshape_global_image(struct _lv_draw_nanovg_unit_t * u,
|
||||
lv_color_format_t cf,
|
||||
uint32_t w,
|
||||
uint32_t h)
|
||||
{
|
||||
LV_ASSERT_NULL(u);
|
||||
|
||||
uint32_t stride = (w * lv_color_format_get_bpp(cf) + 7) >> 3;
|
||||
lv_draw_buf_t * tmp_buf = lv_draw_buf_reshape(u->image_buf, cf, w, h, stride);
|
||||
|
||||
if(!tmp_buf) {
|
||||
if(u->image_buf) {
|
||||
lv_draw_buf_destroy(u->image_buf);
|
||||
u->image_buf = NULL;
|
||||
}
|
||||
|
||||
tmp_buf = lv_draw_buf_create(w, h, cf, stride);
|
||||
if(!tmp_buf) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
u->image_buf = tmp_buf;
|
||||
|
||||
return u->image_buf;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
189
src/draw/nanovg/lv_nanovg_utils.h
Normal file
189
src/draw/nanovg/lv_nanovg_utils.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @file lv_nanovg_utils.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_NANOVG_UTILS_H
|
||||
#define LV_NANOVG_UTILS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
|
||||
#include "../../misc/lv_assert.h"
|
||||
#include "../../misc/lv_matrix.h"
|
||||
#include "../../misc/lv_color.h"
|
||||
#include "../../libs/nanovg/nanovg.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_draw_nanovg_unit_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize NanoVG utilities
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_utils_init(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Deinitialize NanoVG utilities
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_utils_deinit(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Convert an LVGL matrix to a NanoVG transform (3x2 matrix)
|
||||
* @param xform the NanoVG transform array (6 floats)
|
||||
* @param matrix the LVGL matrix
|
||||
*/
|
||||
static inline void lv_nanovg_matrix_convert(float * xform, const lv_matrix_t * matrix)
|
||||
{
|
||||
LV_ASSERT_NULL(xform);
|
||||
LV_ASSERT_NULL(matrix);
|
||||
xform[0] = matrix->m[0][0];
|
||||
xform[1] = matrix->m[1][0];
|
||||
xform[2] = matrix->m[0][1];
|
||||
xform[3] = matrix->m[1][1];
|
||||
xform[4] = matrix->m[0][2];
|
||||
xform[5] = matrix->m[1][2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an LVGL color to a NanoVG color
|
||||
* @param color the LVGL color
|
||||
* @param opa the opacity
|
||||
* @return the NanoVG color
|
||||
*/
|
||||
static inline NVGcolor lv_nanovg_color_convert(lv_color_t color, lv_opa_t opa)
|
||||
{
|
||||
return nvgRGBA(color.red, color.green, color.blue, opa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transform matrix to the NanoVG context
|
||||
* @param ctx the NanoVG context
|
||||
* @param matrix the transform matrix
|
||||
*/
|
||||
void lv_nanovg_transform(NVGcontext * ctx, const lv_matrix_t * matrix);
|
||||
|
||||
/**
|
||||
* Set the clipping area
|
||||
* @param ctx the NanoVG context
|
||||
* @param area the clipping area
|
||||
*/
|
||||
void lv_nanovg_set_clip_area(NVGcontext * ctx, const lv_area_t * area);
|
||||
|
||||
/**
|
||||
* Append a rectangle to the path
|
||||
* @param ctx the NanoVG context
|
||||
* @param x the x coordinate of the rectangle
|
||||
* @param y the y coordinate of the rectangle
|
||||
* @param w the width of the rectangle
|
||||
* @param h the height of the rectangle
|
||||
* @param r the radius of the rectangle (0 for no rounding)
|
||||
*/
|
||||
void lv_nanovg_path_append_rect(NVGcontext * ctx, float x, float y, float w, float h, float r);
|
||||
|
||||
/**
|
||||
* Append an area to the path
|
||||
* @param ctx the NanoVG context
|
||||
* @param area the area
|
||||
*/
|
||||
void lv_nanovg_path_append_area(NVGcontext * ctx, const lv_area_t * area);
|
||||
|
||||
/**
|
||||
* Append a right angle arc to the path
|
||||
* @param ctx the NanoVG context
|
||||
* @param start_x the starting x coordinate
|
||||
* @param start_y the starting y coordinate
|
||||
* @param center_x the center x coordinate
|
||||
* @param center_y the center y coordinate
|
||||
* @param end_x the ending x coordinate
|
||||
* @param end_y the ending y coordinate
|
||||
*/
|
||||
void lv_nanovg_path_append_arc_right_angle(NVGcontext * ctx,
|
||||
float start_x, float start_y,
|
||||
float center_x, float center_y,
|
||||
float end_x, float end_y);
|
||||
|
||||
/**
|
||||
* Append an arc to the path
|
||||
* @param ctx the NanoVG context
|
||||
* @param cx the center x coordinate
|
||||
* @param cy the center y coordinate
|
||||
* @param radius the radius
|
||||
* @param start_angle the starting angle in radians
|
||||
* @param sweep the sweep angle in radians
|
||||
* @param pie whether to draw a pie slice (connected to center)
|
||||
*/
|
||||
void lv_nanovg_path_append_arc(NVGcontext * ctx,
|
||||
float cx, float cy,
|
||||
float radius,
|
||||
float start_angle,
|
||||
float sweep,
|
||||
bool pie);
|
||||
|
||||
/**
|
||||
* Fill the current path
|
||||
* @param ctx the NanoVG context
|
||||
* @param winding the winding rule
|
||||
* @param composite_operation the blend mode
|
||||
* @param color the fill color
|
||||
*/
|
||||
void lv_nanovg_fill(NVGcontext * ctx, enum NVGwinding winding, enum NVGcompositeOperation composite_operation,
|
||||
NVGcolor color);
|
||||
|
||||
/**
|
||||
* End the current frame
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_end_frame(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Clean up the NanoVG unit (e.g. at the end of task)
|
||||
* @param u pointer to the nanovg unit
|
||||
*/
|
||||
void lv_nanovg_clean_up(struct _lv_draw_nanovg_unit_t * u);
|
||||
|
||||
/**
|
||||
* Reshape the global image buffer
|
||||
* @param u pointer to the nanovg unit
|
||||
* @param cf the color format
|
||||
* @param w the new width
|
||||
* @param h the new height
|
||||
* @return pointer to the resized draw buffer
|
||||
*/
|
||||
lv_draw_buf_t * lv_nanovg_reshape_global_image(struct _lv_draw_nanovg_unit_t * u,
|
||||
lv_color_format_t cf,
|
||||
uint32_t w,
|
||||
uint32_t h);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_DRAW_NANOVG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_NANOVG_UTILS_H*/
|
||||
@@ -125,6 +125,8 @@ lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * obj, lv_color_format_t cf, l
|
||||
layer._clip_area = snapshot_area;
|
||||
layer.phy_clip_area = snapshot_area;
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_CREATED, &layer);
|
||||
|
||||
lv_display_t * disp_old = lv_refr_get_disp_refreshing();
|
||||
lv_display_t * disp_new = lv_obj_get_display(obj);
|
||||
lv_layer_t * layer_old = disp_new->layer_head;
|
||||
@@ -178,6 +180,9 @@ lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * obj, lv_color_format_t cf, l
|
||||
disp_new->layer_head = layer_old;
|
||||
lv_refr_set_disp_refreshing(disp_old);
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_SCREEN_LOAD_START, &layer);
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, &layer);
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
18
src/libs/nanovg/LICENSE.txt
Normal file
18
src/libs/nanovg/LICENSE.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Copyright (c) 2013 Mikko Mononen memon@inside.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
2577
src/libs/nanovg/nanovg.c
Normal file
2577
src/libs/nanovg/nanovg.c
Normal file
File diff suppressed because it is too large
Load Diff
707
src/libs/nanovg/nanovg.h
Normal file
707
src/libs/nanovg/nanovg.h
Normal file
File diff suppressed because it is too large
Load Diff
1842
src/libs/nanovg/nanovg_gl.h
Normal file
1842
src/libs/nanovg/nanovg_gl.h
Normal file
File diff suppressed because it is too large
Load Diff
167
src/libs/nanovg/nanovg_gl_utils.h
Normal file
167
src/libs/nanovg/nanovg_gl_utils.h
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
#ifndef NANOVG_GL_UTILS_H
|
||||
#define NANOVG_GL_UTILS_H
|
||||
|
||||
#include "../../lv_conf_internal.h"
|
||||
#include "../../stdlib/lv_mem.h"
|
||||
#include "../../stdlib/lv_string.h"
|
||||
|
||||
#if LV_USE_NANOVG
|
||||
|
||||
#ifdef NANOVG_GL_IMPLEMENTATION
|
||||
struct NVGLUframebuffer {
|
||||
NVGcontext * ctx;
|
||||
GLuint fbo;
|
||||
GLuint rbo;
|
||||
GLuint texture;
|
||||
int image;
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct NVGLUframebuffer NVGLUframebuffer;
|
||||
|
||||
// Helper function to create GL frame buffer to render to.
|
||||
// format: see NVGtexture
|
||||
void nvgluBindFramebuffer(NVGLUframebuffer * fb);
|
||||
NVGLUframebuffer * nvgluCreateFramebuffer(NVGcontext * ctx, int w, int h, int imageFlags, int format);
|
||||
void nvgluDeleteFramebuffer(NVGLUframebuffer * fb);
|
||||
|
||||
#endif // NANOVG_GL_UTILS_H
|
||||
|
||||
#ifdef NANOVG_GL_IMPLEMENTATION
|
||||
|
||||
#if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
|
||||
// FBO is core in OpenGL 3>.
|
||||
#define NANOVG_FBO_VALID 1
|
||||
#elif defined(NANOVG_GL2)
|
||||
// On OS X including glext defines FBO on GL2 too.
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/glext.h>
|
||||
#define NANOVG_FBO_VALID 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static GLint defaultFBO = -1;
|
||||
|
||||
NVGLUframebuffer * nvgluCreateFramebuffer(NVGcontext * ctx, int w, int h, int imageFlags, int format)
|
||||
{
|
||||
#ifdef NANOVG_FBO_VALID
|
||||
GLint defFBO;
|
||||
GLint defaultRBO;
|
||||
NVGLUframebuffer * fb = NULL;
|
||||
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defFBO);
|
||||
glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO);
|
||||
|
||||
fb = (NVGLUframebuffer *)lv_malloc(sizeof(NVGLUframebuffer));
|
||||
if(fb == NULL) goto error;
|
||||
lv_memzero(fb, sizeof(NVGLUframebuffer));
|
||||
|
||||
fb->image = nvgCreateImage(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, format, NULL);
|
||||
|
||||
#if defined NANOVG_GL2
|
||||
fb->texture = nvglImageHandleGL2(ctx, fb->image);
|
||||
#elif defined NANOVG_GL3
|
||||
fb->texture = nvglImageHandleGL3(ctx, fb->image);
|
||||
#elif defined NANOVG_GLES2
|
||||
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
|
||||
#elif defined NANOVG_GLES3
|
||||
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
|
||||
#endif
|
||||
|
||||
fb->ctx = ctx;
|
||||
|
||||
// frame buffer object
|
||||
glGenFramebuffers(1, &fb->fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo);
|
||||
|
||||
// render buffer object
|
||||
glGenRenderbuffers(1, &fb->rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);
|
||||
|
||||
// combine all
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
|
||||
|
||||
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
#ifdef GL_DEPTH24_STENCIL8
|
||||
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
|
||||
// Some graphics cards require a depth buffer along with a stencil.
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
|
||||
|
||||
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
#endif // GL_DEPTH24_STENCIL8
|
||||
goto error;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defFBO);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
|
||||
return fb;
|
||||
error:
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defFBO);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
|
||||
nvgluDeleteFramebuffer(fb);
|
||||
return NULL;
|
||||
#else
|
||||
NVG_NOTUSED(ctx);
|
||||
NVG_NOTUSED(w);
|
||||
NVG_NOTUSED(h);
|
||||
NVG_NOTUSED(imageFlags);
|
||||
NVG_NOTUSED(format);
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void nvgluBindFramebuffer(NVGLUframebuffer * fb)
|
||||
{
|
||||
#ifdef NANOVG_FBO_VALID
|
||||
if(defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : (GLuint)defaultFBO);
|
||||
#else
|
||||
NVG_NOTUSED(fb);
|
||||
#endif
|
||||
}
|
||||
|
||||
void nvgluDeleteFramebuffer(NVGLUframebuffer * fb)
|
||||
{
|
||||
#ifdef NANOVG_FBO_VALID
|
||||
if(fb == NULL) return;
|
||||
if(fb->fbo != 0)
|
||||
glDeleteFramebuffers(1, &fb->fbo);
|
||||
if(fb->rbo != 0)
|
||||
glDeleteRenderbuffers(1, &fb->rbo);
|
||||
if(fb->image >= 0)
|
||||
nvgDeleteImage(fb->ctx, fb->image);
|
||||
fb->ctx = NULL;
|
||||
fb->fbo = 0;
|
||||
fb->rbo = 0;
|
||||
fb->texture = 0;
|
||||
fb->image = -1;
|
||||
lv_free(fb);
|
||||
#else
|
||||
NVG_NOTUSED(fb);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // LV_USE_NANOVG
|
||||
|
||||
#endif // NANOVG_GL_IMPLEMENTATION
|
||||
@@ -33,6 +33,11 @@
|
||||
#define LV_NEMA_HAL_CUSTOM 0
|
||||
#define LV_NEMA_HAL_STM32 1
|
||||
|
||||
#define LV_NANOVG_BACKEND_GL2 1
|
||||
#define LV_NANOVG_BACKEND_GL3 2
|
||||
#define LV_NANOVG_BACKEND_GLES2 3
|
||||
#define LV_NANOVG_BACKEND_GLES3 4
|
||||
|
||||
/** Handle special Kconfig options. */
|
||||
#ifndef LV_KCONFIG_IGNORE
|
||||
#include "lv_conf_kconfig.h"
|
||||
@@ -1161,6 +1166,50 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Use NanoVG Renderer
|
||||
* - Requires LV_USE_NANOVG, LV_USE_MATRIX.
|
||||
*/
|
||||
#ifndef LV_USE_DRAW_NANOVG
|
||||
#ifdef CONFIG_LV_USE_DRAW_NANOVG
|
||||
#define LV_USE_DRAW_NANOVG CONFIG_LV_USE_DRAW_NANOVG
|
||||
#else
|
||||
#define LV_USE_DRAW_NANOVG 0
|
||||
#endif
|
||||
#endif
|
||||
#if LV_USE_DRAW_NANOVG
|
||||
/** Select OpenGL backend for NanoVG:
|
||||
* - LV_NANOVG_BACKEND_GL2: OpenGL 2.0
|
||||
* - LV_NANOVG_BACKEND_GL3: OpenGL 3.0+
|
||||
* - LV_NANOVG_BACKEND_GLES2: OpenGL ES 2.0
|
||||
* - LV_NANOVG_BACKEND_GLES3: OpenGL ES 3.0+
|
||||
*/
|
||||
#ifndef LV_NANOVG_BACKEND
|
||||
#ifdef CONFIG_LV_NANOVG_BACKEND
|
||||
#define LV_NANOVG_BACKEND CONFIG_LV_NANOVG_BACKEND
|
||||
#else
|
||||
#define LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Draw image texture cache count. */
|
||||
#ifndef LV_NANOVG_IMAGE_CACHE_CNT
|
||||
#ifdef CONFIG_LV_NANOVG_IMAGE_CACHE_CNT
|
||||
#define LV_NANOVG_IMAGE_CACHE_CNT CONFIG_LV_NANOVG_IMAGE_CACHE_CNT
|
||||
#else
|
||||
#define LV_NANOVG_IMAGE_CACHE_CNT 128
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Draw letter texture cache count. */
|
||||
#ifndef LV_NANOVG_LETTER_CACHE_CNT
|
||||
#ifdef CONFIG_LV_NANOVG_LETTER_CACHE_CNT
|
||||
#define LV_NANOVG_LETTER_CACHE_CNT CONFIG_LV_NANOVG_LETTER_CACHE_CNT
|
||||
#else
|
||||
#define LV_NANOVG_LETTER_CACHE_CNT 512
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*=======================
|
||||
* FEATURE CONFIGURATION
|
||||
*=======================*/
|
||||
@@ -3247,6 +3296,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Enable NanoVG (vector graphics library) */
|
||||
#ifndef LV_USE_NANOVG
|
||||
#ifdef CONFIG_LV_USE_NANOVG
|
||||
#define LV_USE_NANOVG CONFIG_LV_USE_NANOVG
|
||||
#else
|
||||
#define LV_USE_NANOVG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Use lvgl built-in LZ4 lib */
|
||||
#ifndef LV_USE_LZ4_INTERNAL
|
||||
#ifdef CONFIG_LV_USE_LZ4_INTERNAL
|
||||
|
||||
@@ -107,6 +107,20 @@ extern "C" {
|
||||
# define CONFIG_LV_USE_OS LV_OS_CUSTOM
|
||||
#endif
|
||||
|
||||
/*******************
|
||||
* LV_NANOVG_BACKEND
|
||||
*******************/
|
||||
|
||||
#ifdef CONFIG_LV_NANOVG_BACKEND_GL2
|
||||
# define CONFIG_LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GL2
|
||||
#elif defined(CONFIG_LV_NANOVG_BACKEND_GL3)
|
||||
# define CONFIG_LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GL3
|
||||
#elif defined(CONFIG_LV_NANOVG_BACKEND_GLES2)
|
||||
# define CONFIG_LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES2
|
||||
#elif defined(CONFIG_LV_NANOVG_BACKEND_GLES3)
|
||||
# define CONFIG_LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES3
|
||||
#endif
|
||||
|
||||
/*******************
|
||||
* LV_MEM_SIZE
|
||||
*******************/
|
||||
|
||||
3
src/misc/cache/instance/lv_image_cache.c
vendored
3
src/misc/cache/instance/lv_image_cache.c
vendored
@@ -82,6 +82,9 @@ void lv_image_cache_drop(const void * src)
|
||||
/*If user invalidate image, the header cache should be invalidated too.*/
|
||||
lv_image_header_cache_drop(src);
|
||||
|
||||
/*Notify draw units to invalidate any cached resources (e.g., GPU textures) for this image source.*/
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_INVALIDATE_AREA, (void *)src);
|
||||
|
||||
if(src == NULL) {
|
||||
lv_cache_drop_all(img_cache_p, NULL);
|
||||
return;
|
||||
|
||||
117
src/misc/lv_pending.c
Normal file
117
src/misc/lv_pending.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @file lv_pending.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_pending.h"
|
||||
#include "lv_array.h"
|
||||
#include "lv_assert.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_pending_t {
|
||||
lv_array_t * arr_act;
|
||||
lv_array_t arr_1;
|
||||
lv_array_t arr_2;
|
||||
lv_pending_free_cb_t free_cb;
|
||||
void * user_data;
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static inline void lv_pending_array_clear(lv_pending_t * pending, lv_array_t * arr);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_pending_t * lv_pending_create(size_t obj_size, uint32_t capacity_default)
|
||||
{
|
||||
lv_pending_t * pending = lv_malloc_zeroed(sizeof(lv_pending_t));
|
||||
LV_ASSERT_MALLOC(pending);
|
||||
lv_array_init(&pending->arr_1, capacity_default, obj_size);
|
||||
lv_array_init(&pending->arr_2, capacity_default, obj_size);
|
||||
pending->arr_act = &pending->arr_1;
|
||||
return pending;
|
||||
}
|
||||
|
||||
void lv_pending_destroy(lv_pending_t * pending)
|
||||
{
|
||||
LV_ASSERT_NULL(pending);
|
||||
lv_pending_remove_all(pending);
|
||||
lv_array_deinit(&pending->arr_1);
|
||||
lv_array_deinit(&pending->arr_2);
|
||||
lv_memzero(pending, sizeof(lv_pending_t));
|
||||
lv_free(pending);
|
||||
}
|
||||
|
||||
void lv_pending_set_free_cb(lv_pending_t * pending, lv_pending_free_cb_t free_cb,
|
||||
void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(pending);
|
||||
LV_ASSERT_NULL(free_cb);
|
||||
pending->free_cb = free_cb;
|
||||
pending->user_data = user_data;
|
||||
}
|
||||
|
||||
void lv_pending_add(lv_pending_t * pending, void * obj)
|
||||
{
|
||||
LV_ASSERT_NULL(pending);
|
||||
LV_ASSERT_NULL(obj);
|
||||
lv_array_push_back(pending->arr_act, obj);
|
||||
}
|
||||
|
||||
void lv_pending_remove_all(lv_pending_t * pending)
|
||||
{
|
||||
LV_ASSERT_NULL(pending);
|
||||
|
||||
lv_pending_array_clear(pending, &pending->arr_1);
|
||||
lv_pending_array_clear(pending, &pending->arr_2);
|
||||
}
|
||||
|
||||
void lv_pending_swap(lv_pending_t * pending)
|
||||
{
|
||||
pending->arr_act = (pending->arr_act == &pending->arr_1) ? &pending->arr_2 : &pending->arr_1;
|
||||
lv_pending_array_clear(pending, pending->arr_act);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static inline void lv_pending_array_clear(lv_pending_t * pending, lv_array_t * arr)
|
||||
{
|
||||
LV_ASSERT_NULL(pending->free_cb);
|
||||
|
||||
uint32_t size = lv_array_size(arr);
|
||||
if(size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove all the pending objects */
|
||||
for(uint32_t i = 0; i < size; i++) {
|
||||
pending->free_cb(lv_array_at(arr, i), pending->user_data);
|
||||
}
|
||||
|
||||
lv_array_clear(arr);
|
||||
}
|
||||
85
src/misc/lv_pending.h
Normal file
85
src/misc/lv_pending.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file lv_pending.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_PENDING_H
|
||||
#define LV_PENDING_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct _lv_pending_t lv_pending_t;
|
||||
|
||||
typedef void (*lv_pending_free_cb_t)(void * obj, void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Create a pending list
|
||||
* @param obj_size the size of the objects in the list
|
||||
* @param capacity_default the default capacity of the list
|
||||
* @return a pointer to the pending list
|
||||
*/
|
||||
lv_pending_t * lv_pending_create(size_t obj_size, uint32_t capacity_default);
|
||||
|
||||
/**
|
||||
* Destroy a pending list
|
||||
* @param pending pointer to the pending list
|
||||
*/
|
||||
void lv_pending_destroy(lv_pending_t * pending);
|
||||
|
||||
/**
|
||||
* Set a free callback for the pending list
|
||||
* @param pending pointer to the pending list
|
||||
* @param free_cb the free callback
|
||||
* @param user_data user data to pass to the free callback
|
||||
*/
|
||||
void lv_pending_set_free_cb(lv_pending_t * pending, lv_pending_free_cb_t free_cb,
|
||||
void * user_data);
|
||||
|
||||
/**
|
||||
* Add an object to the pending list
|
||||
* @param pending pointer to the pending list
|
||||
* @param obj pointer to the object to add
|
||||
*/
|
||||
void lv_pending_add(lv_pending_t * pending, void * obj);
|
||||
|
||||
/**
|
||||
* Remove all objects from both pending lists
|
||||
* @param pending pointer to the pending list
|
||||
*/
|
||||
void lv_pending_remove_all(lv_pending_t * pending);
|
||||
|
||||
/**
|
||||
* Remove all old object references and swap new object references
|
||||
* @param pending pointer to the pending list
|
||||
*/
|
||||
void lv_pending_swap(lv_pending_t * pending);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_PENDING_H*/
|
||||
@@ -395,11 +395,16 @@ void lv_canvas_init_layer(lv_obj_t * obj, lv_layer_t * layer)
|
||||
layer->buf_area = canvas_area;
|
||||
layer->_clip_area = canvas_area;
|
||||
layer->phy_clip_area = canvas_area;
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_CREATED, layer);
|
||||
}
|
||||
|
||||
void lv_canvas_finish_layer(lv_obj_t * canvas, lv_layer_t * layer)
|
||||
{
|
||||
if(layer->draw_task_head == NULL) return;
|
||||
if(layer->draw_task_head == NULL) {
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, layer);
|
||||
return;
|
||||
}
|
||||
|
||||
bool task_dispatched;
|
||||
|
||||
@@ -412,6 +417,9 @@ void lv_canvas_finish_layer(lv_obj_t * canvas, lv_layer_t * layer)
|
||||
lv_draw_dispatch_request();
|
||||
}
|
||||
}
|
||||
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_SCREEN_LOAD_START, layer);
|
||||
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, layer);
|
||||
lv_obj_invalidate(canvas);
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,9 @@
|
||||
#ifndef LV_USE_OPENGLES
|
||||
#if !defined(NON_AMD64_BUILD) && !defined(_MSC_VER) && !defined(_WIN32)
|
||||
#define LV_USE_OPENGLES 1
|
||||
|
||||
#define LV_USE_NANOVG 1
|
||||
#define LV_USE_DRAW_NANOVG 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user