feat(draw): add nanovg rendering backend (#8865)

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
VIFEX
2026-01-08 22:23:34 +08:00
committed by GitHub
parent c6ea45d969
commit 918e480c66
44 changed files with 10137 additions and 3 deletions

36
Kconfig
View File

@@ -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

View File

@@ -9,5 +9,6 @@ Running under Embedded Linux
overview
opengl
nanovg
os/index
drivers/index

View 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

View File

@@ -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>`.

View File

@@ -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

View File

@@ -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"

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"

View 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 */

View 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*/

View 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 */

View 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 */

View 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 */

View 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*/

View 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 */

View 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 */

View 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 */

View 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*/

View 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 */

View 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 */

View 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*/

View 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 */

View 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 */

View 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 */

View 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*/

View 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*/

View 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*/

View 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*/

View 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 */

View 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*/

View File

@@ -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;
}

View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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

View File

@@ -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
*******************/

View File

@@ -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
View 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
View 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*/

View File

@@ -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);
}

View File

@@ -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