diff --git a/docs/src/details/integration/chip_vendors/stm32/neochrom.rst b/docs/src/details/integration/chip_vendors/stm32/neochrom.rst
index 9d63d1c77a..77c7c1fa52 100644
--- a/docs/src/details/integration/chip_vendors/stm32/neochrom.rst
+++ b/docs/src/details/integration/chip_vendors/stm32/neochrom.rst
@@ -75,6 +75,36 @@ provided implementations by setting :c:macro:`LV_USE_NEMA_HAL` to a value other
:c:macro:`LV_NEMA_HAL_CUSTOM`.
+Vector Graphics
+***************
+
+The NeoChrom VG driver in LVGL can render vector graphics Widgets using NeoChrom VG's
+hardware support for vector graphics drawing. You can display SVG files in your application.
+See the SVG examples for usage.
+
+To use vector graphics with NeoChrom, you should enable the following configs in ``lv_conf.h``.
+
+.. code-block::
+
+ LV_USE_NEMA_GFX 1
+ LV_USE_NEMA_VG 1
+ LV_USE_VECTOR_GRAPHIC 1
+ LV_USE_MATRIX 1
+ LV_USE_FLOAT 1
+
+To use the SVG widget, additionally enable ``LV_USE_SVG``.
+
+If there is RAM available, SVG performance can be increased by enabling the image cache,
+``LV_CACHE_DEF_SIZE``.
+``LV_CACHE_DEF_SIZE`` is a cache size in bytes. If it is large enough for your SVGs,
+it will cache decoded SVG data so it does not need to be parsed every refresh, significantly
+reducing SVG redraw time.
+
+``LV_USE_DEMO_VECTOR_GRAPHIC`` is a demo you can enable which draws some vector graphics shapes.
+Gradient and image fills are not supported yet, as well as dashed strokes. These are
+missing from the demo when it is run with the NeoChrom driver.
+
+
TSC Images
**********
diff --git a/examples/assets/circle.svg b/examples/assets/circle.svg
new file mode 100644
index 0000000000..abc6e157ef
--- /dev/null
+++ b/examples/assets/circle.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/examples/libs/svg/index.rst b/examples/libs/svg/index.rst
index 8b361cb92a..2f0fecb824 100644
--- a/examples/libs/svg/index.rst
+++ b/examples/libs/svg/index.rst
@@ -1,6 +1,17 @@
Load and render SVG data
----------------------------------------
+------------------------
.. lv_example:: libs/svg/lv_example_svg_1
:language: c
+Load and render SVG data from a file
+------------------------------------
+
+.. lv_example:: libs/svg/lv_example_svg_2
+ :language: c
+
+Load and render SVG data in a draw event
+----------------------------------------
+
+.. lv_example:: libs/svg/lv_example_svg_3
+ :language: c
diff --git a/examples/libs/svg/lv_example_svg.h b/examples/libs/svg/lv_example_svg.h
index 1af7d0552a..a37b44bc89 100644
--- a/examples/libs/svg/lv_example_svg.h
+++ b/examples/libs/svg/lv_example_svg.h
@@ -26,6 +26,8 @@ extern "C" {
* GLOBAL PROTOTYPES
**********************/
void lv_example_svg_1(void);
+void lv_example_svg_2(void);
+void lv_example_svg_3(void);
/**********************
* MACROS
diff --git a/examples/libs/svg/lv_example_svg_1.c b/examples/libs/svg/lv_example_svg_1.c
index c9dcf2e9a7..7af06b64cb 100644
--- a/examples/libs/svg/lv_example_svg_1.c
+++ b/examples/libs/svg/lv_example_svg_1.c
@@ -3,22 +3,22 @@
#if LV_USE_SVG && LV_USE_VECTOR_GRAPHIC
/**
- * Load an SVG data
+ * Load an SVG from data
*/
-static void event_cb(lv_event_t * e)
-{
- static char svg_data[] = "";
-
- lv_layer_t * layer = lv_event_get_layer(e);
- lv_svg_node_t * svg = lv_svg_load_data(svg_data, sizeof(svg_data) / sizeof(char));
- lv_draw_svg(layer, svg);
- lv_svg_node_delete(svg);
-}
-
void lv_example_svg_1(void)
{
- lv_obj_add_event_cb(lv_screen_active(), event_cb, LV_EVENT_DRAW_MAIN, NULL);
+ static const char svg_data[] = "";
+
+ static lv_image_dsc_t svg_dsc;
+ svg_dsc.header.magic = LV_IMAGE_HEADER_MAGIC;
+ svg_dsc.header.w = 450;
+ svg_dsc.header.h = 150;
+ svg_dsc.data_size = sizeof(svg_data) - 1;
+ svg_dsc.data = (const uint8_t *) svg_data;
+
+ lv_obj_t * svg = lv_image_create(lv_screen_active());
+ lv_image_set_src(svg, &svg_dsc);
}
#else
diff --git a/examples/libs/svg/lv_example_svg_2.c b/examples/libs/svg/lv_example_svg_2.c
new file mode 100644
index 0000000000..b4e839512f
--- /dev/null
+++ b/examples/libs/svg/lv_example_svg_2.c
@@ -0,0 +1,26 @@
+#include "../../lv_examples.h"
+#if LV_BUILD_EXAMPLES
+#if LV_USE_SVG && LV_USE_VECTOR_GRAPHIC
+
+/**
+ * Load an SVG from a file
+ */
+void lv_example_svg_2(void)
+{
+ lv_obj_t * svg = lv_image_create(lv_screen_active());
+ lv_image_set_src(svg, "A:lvgl/examples/assets/circle.svg");
+}
+#else
+
+void lv_example_svg_2(void)
+{
+ /*TODO
+ *fallback for online examples*/
+
+ lv_obj_t * label = lv_label_create(lv_screen_active());
+ lv_label_set_text(label, "SVG is not enabled");
+ lv_obj_center(label);
+}
+
+#endif
+#endif
diff --git a/examples/libs/svg/lv_example_svg_3.c b/examples/libs/svg/lv_example_svg_3.c
new file mode 100644
index 0000000000..04ab15c659
--- /dev/null
+++ b/examples/libs/svg/lv_example_svg_3.c
@@ -0,0 +1,36 @@
+#include "../../lv_examples.h"
+#if LV_BUILD_EXAMPLES
+#if LV_USE_SVG && LV_USE_VECTOR_GRAPHIC
+
+/**
+ * Draw SVG data in a draw event
+ */
+static void event_cb(lv_event_t * e)
+{
+ static const char svg_data[] = "";
+
+ lv_layer_t * layer = lv_event_get_layer(e);
+ lv_svg_node_t * svg = lv_svg_load_data(svg_data, sizeof(svg_data) / sizeof(char));
+ lv_draw_svg(layer, svg);
+ lv_svg_node_delete(svg);
+}
+
+void lv_example_svg_3(void)
+{
+ lv_obj_add_event_cb(lv_screen_active(), event_cb, LV_EVENT_DRAW_MAIN, NULL);
+}
+#else
+
+void lv_example_svg_3(void)
+{
+ /*TODO
+ *fallback for online examples*/
+
+ lv_obj_t * label = lv_label_create(lv_screen_active());
+ lv_label_set_text(label, "SVG is not enabled");
+ lv_obj_center(label);
+}
+
+#endif
+#endif
diff --git a/src/draw/lv_draw_vector.c b/src/draw/lv_draw_vector.c
index 1d5a7e0486..089554dedb 100644
--- a/src/draw/lv_draw_vector.c
+++ b/src/draw/lv_draw_vector.c
@@ -12,8 +12,8 @@
#if LV_USE_VECTOR_GRAPHIC
-#if !((LV_USE_DRAW_SW && LV_USE_THORVG) || LV_USE_DRAW_VG_LITE)
- #error "LV_USE_VECTOR_GRAPHIC requires either (LV_USE_DRAW_SW and LV_USE_THORVG) or LV_USE_DRAW_VG_LITE"
+#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)"
#endif
#include "../misc/lv_ll.h"
diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx.c b/src/draw/nema_gfx/lv_draw_nema_gfx.c
index 44c914c35c..a56420f223 100644
--- a/src/draw/nema_gfx/lv_draw_nema_gfx.c
+++ b/src/draw/nema_gfx/lv_draw_nema_gfx.c
@@ -243,12 +243,18 @@ static int32_t nema_gfx_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * ta
}
break;
}
+#if LV_USE_VECTOR_GRAPHIC && LV_USE_NEMA_VG
+ case LV_DRAW_TASK_TYPE_VECTOR: {
+ if(task->preference_score > 80) {
+ task->preference_score = 80;
+ task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
+ }
+ return 1;
+ }
+#endif
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
case LV_DRAW_TASK_TYPE_MASK_BITMAP:
-#if LV_USE_VECTOR_GRAPHIC
- case LV_DRAW_TASK_TYPE_VECTOR:
-#endif
default:
break;
}
@@ -328,6 +334,11 @@ static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u)
case LV_DRAW_TASK_TYPE_BORDER:
lv_draw_nema_gfx_border(t, t->draw_dsc, &t->area);
break;
+#if LV_USE_VECTOR_GRAPHIC && LV_USE_NEMA_VG
+ case LV_DRAW_TASK_TYPE_VECTOR:
+ lv_draw_nema_gfx_vector(t, t->draw_dsc, &t->area);
+ break;
+#endif
default:
break;
}
diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx.h b/src/draw/nema_gfx/lv_draw_nema_gfx.h
index 8197fab28e..5717e5be45 100644
--- a/src/draw/nema_gfx/lv_draw_nema_gfx.h
+++ b/src/draw/nema_gfx/lv_draw_nema_gfx.h
@@ -110,6 +110,11 @@ void lv_draw_nema_gfx_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * ds
void lv_draw_nema_gfx_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc,
const lv_area_t * coords);
+#if LV_USE_VECTOR_GRAPHIC && LV_USE_NEMA_VG
+void lv_draw_nema_gfx_vector(lv_draw_task_t * t, const lv_draw_vector_dsc_t * dsc,
+ const lv_area_t * coords);
+#endif
+
/**********************
* MACROS
diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx_vector.c b/src/draw/nema_gfx/lv_draw_nema_gfx_vector.c
new file mode 100644
index 0000000000..923f514c30
--- /dev/null
+++ b/src/draw/nema_gfx/lv_draw_nema_gfx_vector.c
@@ -0,0 +1,161 @@
+/**
+ * @file lv_draw_nema_gfx_vector.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_draw_nema_gfx.h"
+#if LV_USE_NEMA_GFX && LV_USE_VECTOR_GRAPHIC && LV_USE_NEMA_VG
+
+#include "lv_nema_gfx_path.h"
+#include "../lv_draw_vector_private.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_draw_nema_gfx_unit_t * u;
+ float rel_translate_x;
+ float rel_translate_y;
+} ctx_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_path_ctx_t * dsc);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_nema_gfx_vector(lv_draw_task_t * t, const lv_draw_vector_dsc_t * dsc,
+ const lv_area_t * coords)
+{
+ ctx_t c;
+ c.u = (lv_draw_nema_gfx_unit_t *) t->draw_unit;
+
+ lv_layer_t * layer = t->target_layer;
+ lv_area_t rel_clip_area;
+ lv_area_copy(&rel_clip_area, &t->clip_area);
+ lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
+
+ c.rel_translate_x = -layer->buf_area.x1;
+ c.rel_translate_y = -layer->buf_area.y1;
+
+ nema_set_clip(rel_clip_area.x1, rel_clip_area.y1, lv_area_get_width(&rel_clip_area),
+ lv_area_get_height(&rel_clip_area));
+
+ lv_color_format_t dst_cf = layer->draw_buf->header.cf;
+ uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
+
+ /* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
+ int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
+ -1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
+
+ nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
+ lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
+
+ nema_vg_set_blend(NEMA_BL_SRC_OVER | NEMA_BLOP_SRC_PREMULT);
+
+ lv_vector_for_each_destroy_tasks(dsc->task_list, task_draw_cb, &c);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_path_ctx_t * dsc)
+{
+ ctx_t * c = ctx;
+ lv_draw_nema_gfx_unit_t * u = c->u;
+
+ if(!path) return;
+
+ nema_vg_path_clear(u->path);
+ nema_vg_paint_clear(u->paint);
+
+ nema_vg_paint_set_type(u->paint, NEMA_VG_PAINT_COLOR);
+
+ lv_matrix_t matrix = dsc->matrix;
+ matrix.m[0][2] += c->rel_translate_x;
+ matrix.m[1][2] += c->rel_translate_y;
+ nema_vg_path_set_matrix(c->u->path, (void *) &matrix);
+
+ /* the path ops array needs to be translated to nema's opcodes */
+ lv_vector_path_op_t * ops = lv_array_front(&path->ops);
+ uint32_t op_count = lv_array_size(&path->ops);
+ uint8_t * nema_ops = lv_malloc(op_count * sizeof(*nema_ops));
+ LV_ASSERT_MALLOC(nema_ops);
+ for(uint32_t i = 0; i < op_count; i++) {
+ nema_ops[i] = ops[i] == LV_VECTOR_PATH_OP_MOVE_TO ? NEMA_VG_PRIM_MOVE :
+ ops[i] == LV_VECTOR_PATH_OP_LINE_TO ? NEMA_VG_PRIM_LINE :
+ ops[i] == LV_VECTOR_PATH_OP_QUAD_TO ? NEMA_VG_PRIM_BEZIER_QUAD :
+ ops[i] == LV_VECTOR_PATH_OP_CUBIC_TO ? NEMA_VG_PRIM_BEZIER_CUBIC :
+ /*LV_VECTOR_PATH_OP_CLOSE*/ NEMA_VG_PRIM_CLOSE;
+ }
+
+ /* the path points array is in the right format for nema to use as-is */
+ uint32_t point_count = lv_array_size(&path->points);
+ float * points = lv_array_front(&path->points);
+
+ nema_vg_path_set_shape(u->path, op_count, nema_ops, point_count, points);
+
+ nema_vg_set_quality(path->quality == LV_VECTOR_PATH_QUALITY_LOW ? NEMA_VG_QUALITY_FASTER :
+ path->quality == LV_VECTOR_PATH_QUALITY_MEDIUM ? NEMA_VG_QUALITY_BETTER :
+ /*LV_VECTOR_PATH_QUALITY_HIGH*/ NEMA_VG_QUALITY_MAXIMUM);
+
+ if(dsc->fill_dsc.opa) {
+ nema_vg_set_fill_rule(dsc->fill_dsc.fill_rule == LV_VECTOR_FILL_NONZERO ? NEMA_VG_FILL_NON_ZERO :
+ /*LV_VECTOR_FILL_EVENODD*/ NEMA_VG_FILL_EVEN_ODD);
+
+ uint32_t nema_dsc_color = nema_rgba(dsc->fill_dsc.color.red, dsc->fill_dsc.color.green,
+ dsc->fill_dsc.color.blue, dsc->fill_dsc.color.alpha);
+ nema_vg_paint_set_paint_color(u->paint, nema_dsc_color);
+
+ nema_vg_draw_path(u->path, u->paint);
+ }
+
+ if(dsc->stroke_dsc.opa) {
+ nema_vg_set_fill_rule(NEMA_VG_STROKE);
+ nema_vg_stroke_set_width(dsc->stroke_dsc.width);
+ uint8_t cap = dsc->stroke_dsc.cap == LV_VECTOR_STROKE_CAP_BUTT ? NEMA_VG_CAP_BUTT :
+ dsc->stroke_dsc.cap == LV_VECTOR_STROKE_CAP_SQUARE ? NEMA_VG_CAP_SQUARE :
+ /*LV_VECTOR_STROKE_CAP_ROUND*/ NEMA_VG_CAP_ROUND;
+ nema_vg_stroke_set_cap_style(cap, cap);
+ uint8_t join = dsc->stroke_dsc.join == LV_VECTOR_STROKE_JOIN_MITER ? NEMA_VG_JOIN_MITER :
+ dsc->stroke_dsc.join == LV_VECTOR_STROKE_JOIN_BEVEL ? NEMA_VG_JOIN_BEVEL :
+ /*LV_VECTOR_STROKE_JOIN_ROUND*/ NEMA_VG_JOIN_ROUND;
+ nema_vg_stroke_set_join_style(join);
+ nema_vg_stroke_set_miter_limit(dsc->stroke_dsc.miter_limit);
+
+ uint32_t nema_dsc_color = nema_rgba(dsc->stroke_dsc.color.red, dsc->stroke_dsc.color.green,
+ dsc->stroke_dsc.color.blue, dsc->stroke_dsc.color.alpha);
+ nema_vg_paint_set_paint_color(u->paint, nema_dsc_color);
+
+ nema_vg_draw_path(u->path, u->paint);
+ }
+
+ nema_cl_submit(&c->u->cl);
+ nema_cl_wait(&u->cl);
+
+ lv_free(nema_ops);
+}
+
+#endif /*LV_USE_NEMA_GFX && LV_USE_VECTOR_GRAPHIC && LV_USE_NEMA_VG*/