This commit is contained in:
2026-02-26 19:56:43 -08:00
commit f000ee7c19
1137 changed files with 603879 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
/**
* @file lv_svg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_svg.h"
#if LV_USE_SVG
#include "../../misc/lv_assert.h"
#include "../../misc/lv_log.h"
#include "../../stdlib/lv_mem.h"
#include "lv_svg_token.h"
#include "lv_svg_parser.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_svg_node_constructor(const lv_tree_class_t * class_p, lv_tree_node_t * node)
{
LV_UNUSED(class_p);
lv_svg_node_t * t = (lv_svg_node_t *)node;
t->xml_id = NULL;
t->type = LV_SVG_TAG_INVALID;
lv_array_init(&t->attrs, 4, sizeof(lv_svg_attr_t));
t->render_obj = NULL;
}
static void lv_svg_node_destructor(const lv_tree_class_t * class_p, lv_tree_node_t * node)
{
LV_UNUSED(class_p);
lv_svg_node_t * t = (lv_svg_node_t *)node;
if(t->xml_id) {
lv_free(t->xml_id);
}
for(uint32_t i = 0; i < lv_array_size(&t->attrs); i++) {
lv_svg_attr_t * attr = lv_array_at(&t->attrs, i);
if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) {
lv_free(attr->value.val);
}
}
lv_array_deinit(&t->attrs);
}
static bool svg_token_process_cb(_lv_svg_token_t * token, void * data)
{
_lv_svg_parser_t * parser = (_lv_svg_parser_t *)data;
return _lv_svg_parser_token(parser, token);
}
/**********************
* STATIC VARIABLES
**********************/
const lv_tree_class_t lv_svg_node_class = {
.base_class = &lv_tree_node_class,
.instance_size = sizeof(lv_svg_node_t),
.constructor_cb = lv_svg_node_constructor,
.destructor_cb = lv_svg_node_destructor,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_svg_node_t * lv_svg_load_data(const char * svg_data, uint32_t data_len)
{
LV_ASSERT_NULL(svg_data);
LV_ASSERT(data_len > 0);
_lv_svg_parser_t parser;
_lv_svg_parser_init(&parser);
if(_lv_svg_tokenizer(svg_data, data_len, svg_token_process_cb, &parser)) {
if(_lv_svg_parser_is_finish(&parser)) {
lv_svg_node_t * doc = parser.doc_root;
parser.doc_root = NULL;
_lv_svg_parser_deinit(&parser);
#if LV_USE_SVG_DEBUG
_lv_svg_dump_tree(doc, 0);
#endif
return doc;
}
else {
_lv_svg_parser_deinit(&parser);
LV_LOG_ERROR("svg document parser raise errors!");
return NULL;
}
}
else {
_lv_svg_parser_deinit(&parser);
LV_LOG_ERROR("svg document tokenizer raise errors!");
return NULL;
}
}
lv_svg_node_t * lv_svg_node_create(lv_svg_node_t * parent)
{
lv_tree_node_t * node = lv_tree_node_create(&lv_svg_node_class, (lv_tree_node_t *)parent);
return (lv_svg_node_t *)node;
}
void lv_svg_node_delete(lv_svg_node_t * node)
{
lv_tree_node_delete((lv_tree_node_t *)node);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SVG*/

View File

@@ -0,0 +1,336 @@
/**
* @file lv_svg.h
*
*/
#ifndef LV_SVG_H
#define LV_SVG_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../misc/lv_array.h"
#include "../../misc/lv_tree.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum _lv_svg_tag_t {
LV_SVG_TAG_INVALID = -1,
LV_SVG_TAG_CONTENT,
LV_SVG_TAG_SVG,
LV_SVG_TAG_USE,
LV_SVG_TAG_G,
LV_SVG_TAG_PATH,
LV_SVG_TAG_RECT,
LV_SVG_TAG_CIRCLE,
LV_SVG_TAG_ELLIPSE,
LV_SVG_TAG_LINE,
LV_SVG_TAG_POLYLINE,
LV_SVG_TAG_POLYGON,
LV_SVG_TAG_SOLID_COLOR,
LV_SVG_TAG_LINEAR_GRADIENT,
LV_SVG_TAG_RADIAL_GRADIENT,
LV_SVG_TAG_STOP,
LV_SVG_TAG_DEFS,
LV_SVG_TAG_IMAGE,
#if LV_USE_SVG_ANIMATION
LV_SVG_TAG_MPATH,
LV_SVG_TAG_SET,
LV_SVG_TAG_ANIMATE,
LV_SVG_TAG_ANIMATE_COLOR,
LV_SVG_TAG_ANIMATE_TRANSFORM,
LV_SVG_TAG_ANIMATE_MOTION,
#endif
LV_SVG_TAG_TEXT,
LV_SVG_TAG_TSPAN,
LV_SVG_TAG_TEXT_AREA,
};
typedef int8_t lv_svg_tag_t;
enum _lv_svg_attr_type_t {
LV_SVG_ATTR_INVALID = 0,
LV_SVG_ATTR_ID,
LV_SVG_ATTR_XML_ID,
LV_SVG_ATTR_VERSION,
LV_SVG_ATTR_BASE_PROFILE,
LV_SVG_ATTR_VIEWBOX,
LV_SVG_ATTR_PRESERVE_ASPECT_RATIO,
LV_SVG_ATTR_VIEWPORT_FILL,
LV_SVG_ATTR_VIEWPORT_FILL_OPACITY,
LV_SVG_ATTR_DISPLAY,
LV_SVG_ATTR_VISIBILITY,
LV_SVG_ATTR_X,
LV_SVG_ATTR_Y,
LV_SVG_ATTR_WIDTH,
LV_SVG_ATTR_HEIGHT,
LV_SVG_ATTR_RX,
LV_SVG_ATTR_RY,
LV_SVG_ATTR_CX,
LV_SVG_ATTR_CY,
LV_SVG_ATTR_R,
LV_SVG_ATTR_X1,
LV_SVG_ATTR_Y1,
LV_SVG_ATTR_X2,
LV_SVG_ATTR_Y2,
LV_SVG_ATTR_POINTS,
LV_SVG_ATTR_D,
LV_SVG_ATTR_PATH_LENGTH,
LV_SVG_ATTR_XLINK_HREF,
LV_SVG_ATTR_STYLE,
LV_SVG_ATTR_FILL,
LV_SVG_ATTR_FILL_RULE,
LV_SVG_ATTR_FILL_OPACITY,
LV_SVG_ATTR_STROKE,
LV_SVG_ATTR_STROKE_WIDTH,
LV_SVG_ATTR_STROKE_LINECAP,
LV_SVG_ATTR_STROKE_LINEJOIN,
LV_SVG_ATTR_STROKE_MITER_LIMIT,
LV_SVG_ATTR_STROKE_DASH_ARRAY,
LV_SVG_ATTR_STROKE_DASH_OFFSET,
LV_SVG_ATTR_STROKE_OPACITY,
LV_SVG_ATTR_OPACITY,
LV_SVG_ATTR_SOLID_COLOR,
LV_SVG_ATTR_SOLID_OPACITY,
LV_SVG_ATTR_GRADIENT_UNITS,
LV_SVG_ATTR_GRADIENT_STOP_OFFSET,
LV_SVG_ATTR_GRADIENT_STOP_COLOR,
LV_SVG_ATTR_GRADIENT_STOP_OPACITY,
LV_SVG_ATTR_FONT_FAMILY,
LV_SVG_ATTR_FONT_STYLE,
LV_SVG_ATTR_FONT_VARIANT,
LV_SVG_ATTR_FONT_WEIGHT,
LV_SVG_ATTR_FONT_SIZE,
LV_SVG_ATTR_TRANSFORM,
LV_SVG_ATTR_TEXT_ANCHOR,
#if LV_USE_SVG_ANIMATION
LV_SVG_ATTR_ATTRIBUTE_NAME,
LV_SVG_ATTR_ATTRIBUTE_TYPE,
LV_SVG_ATTR_BEGIN,
LV_SVG_ATTR_END,
LV_SVG_ATTR_DUR,
LV_SVG_ATTR_MIN,
LV_SVG_ATTR_MAX,
LV_SVG_ATTR_RESTART,
LV_SVG_ATTR_REPEAT_COUNT,
LV_SVG_ATTR_REPEAT_DUR,
LV_SVG_ATTR_CALC_MODE,
LV_SVG_ATTR_VALUES,
LV_SVG_ATTR_KEY_TIMES,
LV_SVG_ATTR_KEY_SPLINES,
LV_SVG_ATTR_KEY_POINTS,
LV_SVG_ATTR_FROM,
LV_SVG_ATTR_TO,
LV_SVG_ATTR_BY,
LV_SVG_ATTR_ADDITIVE,
LV_SVG_ATTR_ACCUMULATE,
LV_SVG_ATTR_PATH,
LV_SVG_ATTR_ROTATE,
LV_SVG_ATTR_TRANSFORM_TYPE,
#endif
};
typedef uint8_t lv_svg_attr_type_t;
enum _lv_svg_transform_type_t {
LV_SVG_TRANSFORM_TYPE_MATRIX = 1,
LV_SVG_TRANSFORM_TYPE_TRANSLATE,
LV_SVG_TRANSFORM_TYPE_ROTATE,
LV_SVG_TRANSFORM_TYPE_SCALE,
LV_SVG_TRANSFORM_TYPE_SKEW_X,
LV_SVG_TRANSFORM_TYPE_SKEW_Y,
};
typedef uint8_t lv_svg_transform_type_t;
#if LV_USE_SVG_ANIMATION
enum lv_svg_anim_action_t {
LV_SVG_ANIM_REMOVE = 0,
LV_SVG_ANIM_FREEZE,
};
enum _lv_svg_anim_restart_type_t {
LV_SVG_ANIM_RESTART_ALWAYS = 0,
LV_SVG_ANIM_RESTART_WHEN_NOT_ACTIVE,
LV_SVG_ANIM_RESTART_NEVER,
};
enum _lv_svg_anim_calc_mode_t {
LV_SVG_ANIM_CALC_MODE_LINEAR = 0,
LV_SVG_ANIM_CALC_MODE_PACED,
LV_SVG_ANIM_CALC_MODE_SPLINE,
LV_SVG_ANIM_CALC_MODE_DISCRETE,
};
enum _lv_svg_anim_additive_type_t {
LV_SVG_ANIM_ADDITIVE_REPLACE = 0,
LV_SVG_ANIM_ADDITIVE_SUM,
};
enum _lv_svg_anim_accumulate_type_t {
LV_SVG_ANIM_ACCUMULATE_NONE = 0,
LV_SVG_ANIM_ACCUMULATE_SUM,
};
#endif
enum _lv_svg_aspect_ratio_t {
LV_SVG_ASPECT_RATIO_NONE = 0,
LV_SVG_ASPECT_RATIO_XMIN_YMIN = (1 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMIN = (2 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMIN = (3 << 1),
LV_SVG_ASPECT_RATIO_XMIN_YMID = (4 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMID = (5 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMID = (6 << 1),
LV_SVG_ASPECT_RATIO_XMIN_YMAX = (7 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMAX = (8 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMAX = (9 << 1),
};
typedef uint32_t lv_svg_aspect_ratio_t;
enum _lv_svg_aspect_ratio_opt_t {
LV_SVG_ASPECT_RATIO_OPT_MEET = 0,
LV_SVG_ASPECT_RATIO_OPT_SLICE,
};
typedef struct {
float x;
float y;
} lv_svg_point_t;
typedef struct {
float m[3][3];
} lv_svg_matrix_t;
typedef uint32_t lv_svg_color_t;
enum _lv_svg_fill_rule_t {
LV_SVG_FILL_NONZERO = 0,
LV_SVG_FILL_EVENODD,
};
typedef uint8_t lv_svg_fill_rule_t;
enum _lv_svg_line_cap_t {
LV_SVG_LINE_CAP_BUTT = 0,
LV_SVG_LINE_CAP_SQUARE,
LV_SVG_LINE_CAP_ROUND,
};
typedef uint8_t lv_svg_line_cap_t;
enum _lv_svg_line_join_t {
LV_SVG_LINE_JOIN_MITER = 0,
LV_SVG_LINE_JOIN_BEVEL,
LV_SVG_LINE_JOIN_ROUND,
};
typedef uint8_t lv_svg_line_join_t;
enum _lv_svg_gradient_units_t {
LV_SVG_GRADIENT_UNITS_OBJECT = 0,
LV_SVG_GRADIENT_UNITS_USER_SPACE,
};
typedef uint8_t lv_svg_gradient_units_t;
typedef union {
int32_t ival;
uint32_t uval;
float fval;
char * sval;
void * val;
} lv_svg_attr_value_t;
/*
* to simplify list buffer management, allocate enough memory for all data and length.
* | size | data[0] | data[1] | data[2] | ... |
*/
typedef struct {
uint32_t length;
uint8_t data[1];
} lv_svg_attr_values_list_t;
/* https://www.w3.org/TR/SVGTiny12/svgudomidl.html */
enum _lv_svg_path_cmd_t {
LV_SVG_PATH_CMD_MOVE_TO = 77,
LV_SVG_PATH_CMD_LINE_TO = 76,
LV_SVG_PATH_CMD_CURVE_TO = 67,
LV_SVG_PATH_CMD_QUAD_TO = 81,
LV_SVG_PATH_CMD_ARC_TO = 65, /*svg2 extension*/
LV_SVG_PATH_CMD_CLOSE = 90,
};
/*
* to simplify list buffer management, allocate enough memory for all path data and cmd.
* | cmd | data[0] | data[1] | data[2] | ... |
*/
typedef struct {
uint32_t cmd;
uint8_t data[1];
} lv_svg_attr_path_value_t;
enum _lv_svg_attr_value_type_t {
LV_SVG_ATTR_VALUE_DATA = 0,
LV_SVG_ATTR_VALUE_PTR,
};
typedef uint8_t lv_svg_attr_value_type_t;
enum _lv_svg_attr_value_class_t {
LV_SVG_ATTR_VALUE_NONE = 0,
LV_SVG_ATTR_VALUE_INITIAL,
LV_SVG_ATTR_VALUE_INHERIT,
};
typedef uint8_t lv_svg_attr_value_class_t;
typedef struct {
lv_svg_attr_type_t id;
lv_svg_attr_value_type_t val_type;
lv_svg_attr_value_class_t class_type;
lv_svg_attr_value_t value;
} lv_svg_attr_t;
struct _lv_svg_render_obj;
typedef struct {
lv_tree_node_t base;
char * xml_id; /* xml_id or content */
lv_svg_tag_t type;
lv_array_t attrs;
struct _lv_svg_render_obj * render_obj;
} lv_svg_node_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Loading SVG data and creating the DOM tree
* @param svg_data pointer to the SVG data
* @param data_len the SVG data length
*/
lv_svg_node_t * lv_svg_load_data(const char * svg_data, uint32_t data_len);
/**
* @brief Create an SVG DOM node
* @param parent pointer to the parent node
* @return true: an new SVG DOM node, false: NULL
*/
lv_svg_node_t * lv_svg_node_create(lv_svg_node_t * parent);
/**
* @brief Delete an SVG DOM subtree
* @param node pointer to an SVG DOM subtree
*/
void lv_svg_node_delete(lv_svg_node_t * node);
/**********************
* MACROS
**********************/
#define LV_SVG_NODE_CHILD(n, i) \
((lv_svg_node_t *)(LV_TREE_NODE((n))->children[i]))
#define LV_SVG_NODE(n) ((lv_svg_node_t*)(n))
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_H*/

View File

@@ -0,0 +1,384 @@
/**
* @file lv_svg_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#if LV_USE_SVG
#include "lv_svg_decoder.h"
#include "lv_svg.h"
#include "../../draw/lv_draw_buf_private.h"
#include "../../display/lv_display_private.h"
/*********************
* DEFINES
*********************/
#define DECODER_NAME "SVG"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t svg_decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src,
lv_image_header_t * header);
static lv_result_t svg_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void svg_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static uint8_t * alloc_file(const char * filename, uint32_t * size);
static void svg_draw_buf_free(void * svg_buf);
static void svg_draw(lv_layer_t * layer, const lv_image_decoder_dsc_t * dsc, const lv_area_t * coords,
const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * clip_area);
/**********************
* STATIC VARIABLES
**********************/
static struct _lv_draw_buf_handlers_t _svg_draw_buf_handler = {
.buf_free_cb = svg_draw_buf_free,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the SVG decoder functions in LVGL
*/
void lv_svg_decoder_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, svg_decoder_info);
lv_image_decoder_set_open_cb(dec, svg_decoder_open);
lv_image_decoder_set_close_cb(dec, svg_decoder_close);
dec->name = DECODER_NAME;
}
void lv_svg_decoder_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == svg_decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static bool valid_svg_data(const uint8_t * data, uint32_t data_size)
{
return (data_size >= 4 && lv_memcmp(data, "<svg", 4) == 0)
|| (data_size >= 5 && lv_memcmp(data, "<?xml", 5) == 0);
}
static lv_result_t svg_decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src,
lv_image_header_t * header)
{
lv_image_src_t src_type = src->src_type;
int width = 0;
int height = 0;
if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_VARIABLE) {
const void * src_data = src->src;
uint8_t * buf = NULL;
if(src_type == LV_IMAGE_SRC_FILE) {
/*Support only "*.svg" files*/
if(lv_strcmp(lv_fs_get_ext(src_data), "svg")) {
return LV_RESULT_INVALID;
}
uint32_t rn;
lv_fs_res_t res;
uint32_t file_size = 0;
res = lv_fs_seek(&src->file, 0, LV_FS_SEEK_END);
if(res == LV_FS_RES_OK) {
lv_fs_tell(&src->file, &file_size);
lv_fs_seek(&src->file, 0, LV_FS_SEEK_SET); /* Reset position to start */
}
#ifdef LV_USE_SVG_DEBUG
LV_LOG_INFO("LVGL file_size = %d.", file_size);
#endif
if(file_size > 512)
file_size = 512;
buf = (uint8_t *)lv_zalloc(file_size);
LV_ASSERT_NULL(buf);
/* read some bytes for searching svg header */
res = lv_fs_read(&src->file, buf, file_size, &rn);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", (char *)src_data);
lv_free(buf);
return LV_RESULT_INVALID;
}
if(!valid_svg_data(buf, rn)) {
lv_free(buf);
return LV_RESULT_INVALID;
}
width = LV_DPI_DEF;
height = LV_DPI_DEF;
uint8_t * svg_start = NULL;
uint8_t * svg_end = NULL;
uint8_t * ptr = buf;
uint8_t * ptr_end = buf + file_size - 1;
while(ptr < ptr_end) {
if(*ptr == '<') {
if(lv_strncmp((char *)(ptr + 1), "svg", 3) == 0) {
svg_start = ptr;
}
}
if(svg_start && (*ptr == '>')) {
svg_end = ptr;
break;
}
ptr++;
}
if(svg_start && svg_end) {
lv_svg_node_t * svg_doc = lv_svg_load_data((char *)svg_start, svg_end - svg_start);
lv_svg_render_obj_t * svg_header = lv_svg_render_create(svg_doc);
if(svg_header->tag == LV_SVG_TAG_SVG) {
lv_area_t bounds;
svg_header->clz->get_bounds(svg_header, &bounds);
width = lv_area_get_width(&bounds) - 1;
height = lv_area_get_height(&bounds) - 1;
}
lv_svg_render_delete(svg_header);
lv_svg_node_delete(svg_doc);
}
else
LV_LOG_WARN("can't find svg viewport tag end");
lv_free(buf);
}
else {
const lv_image_dsc_t * img_dsc = src_data;
uint32_t data_size = img_dsc->data_size;
width = img_dsc->header.w;
height = img_dsc->header.h;
if(!valid_svg_data(img_dsc->data, data_size)) {
return LV_RESULT_INVALID;
}
}
header->cf = LV_COLOR_FORMAT_ARGB8888;
header->w = width;
header->h = height;
header->stride = width * 4;
header->flags |= LV_IMAGE_FLAGS_CUSTOM_DRAW;
decoder->custom_draw_cb = svg_draw;
return LV_RESULT_OK;
}
return LV_RESULT_INVALID;
}
static lv_result_t svg_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
LV_PROFILER_DECODER_BEGIN_TAG("lv_svg_decoder_open");
uint8_t * svg_data = NULL;
uint32_t svg_data_size = 0;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
if(lv_strcmp(lv_fs_get_ext(fn), "svg") == 0) { /*Check the extension*/
svg_data = alloc_file(fn, &svg_data_size);
if(svg_data == NULL) {
LV_LOG_WARN("can't load file: %s", (const char *)dsc->src);
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
}
else {
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
svg_data = (uint8_t *)img_dsc->data;
svg_data_size = (uint32_t)img_dsc->data_size;
}
else {
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
lv_svg_node_t * svg_doc = lv_svg_load_data((char *)svg_data, svg_data_size);
lv_svg_render_obj_t * draw_list = lv_svg_render_create(svg_doc);
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
lv_free(svg_data);
}
lv_svg_node_delete(svg_doc);
/* create a fake draw_buf object */
lv_draw_buf_t * draw_buf = lv_zalloc(sizeof(lv_draw_buf_t));
draw_buf->header.w = 1;
draw_buf->header.h = 1;
draw_buf->header.cf = LV_COLOR_FORMAT_ARGB8888;
draw_buf->header.flags = LV_IMAGE_FLAGS_ALLOCATED | LV_IMAGE_FLAGS_CUSTOM_DRAW;
draw_buf->header.stride = 4;
draw_buf->header.magic = LV_IMAGE_HEADER_MAGIC;
draw_buf->data = NULL;
draw_buf->unaligned_data = (void *)draw_list;
draw_buf->data_size = lv_svg_render_get_size(draw_list);
draw_buf->handlers = &_svg_draw_buf_handler;
dsc->decoded = draw_buf;
if(!dsc->args.no_cache && lv_image_cache_is_enabled()) {
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = dsc->decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, draw_buf, NULL);
if(entry == NULL) {
lv_draw_buf_destroy(draw_buf);
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
}
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_OK;
}
static void svg_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
if(dsc->args.no_cache ||
!lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
}
static uint8_t * alloc_file(const char * filename, uint32_t * size)
{
uint8_t * data = NULL;
lv_fs_file_t f;
uint32_t data_size;
uint32_t rn;
lv_fs_res_t res;
*size = 0;
res = lv_fs_open(&f, filename, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", filename);
return NULL;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_tell(&f, &data_size);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
goto failed;
}
/*Read file to buffer*/
data = lv_malloc(data_size);
if(data == NULL) {
LV_LOG_WARN("malloc failed for data size %u", data_size);
goto failed;
}
res = lv_fs_read(&f, data, data_size, &rn);
if(res == LV_FS_RES_OK && rn == data_size) {
*size = rn;
}
else {
LV_LOG_WARN("read file failed");
lv_free(data);
data = NULL;
}
failed:
lv_fs_close(&f);
return data;
}
static void svg_draw_buf_free(void * svg_buf)
{
lv_svg_render_obj_t * draw_list = (lv_svg_render_obj_t *)svg_buf;
lv_svg_render_delete(draw_list);
}
static void svg_draw(lv_layer_t * layer, const lv_image_decoder_dsc_t * decoder_dsc, const lv_area_t * coords,
const lv_draw_image_dsc_t * image_dsc, const lv_area_t * clip_area)
{
const lv_draw_buf_t * draw_buf = decoder_dsc->decoded;
const lv_svg_render_obj_t * list = draw_buf->unaligned_data;
LV_PROFILER_DRAW_BEGIN;
lv_draw_vector_dsc_t * dsc = lv_draw_vector_dsc_create(layer);
/*Save the widget so that `LV_EVENT_DRAW_TASK_ADDED` can be sent to it in `lv_draw_vector`*/
dsc->base.obj = image_dsc->base.obj;
lv_matrix_t matrix;
lv_matrix_identity(&matrix);
lv_matrix_translate(&matrix, coords->x1, coords->y1);
dsc->ctx->scissor_area = *clip_area;
if(image_dsc) {
int32_t off_x = (lv_area_get_width(coords) - image_dsc->header.w - 1) / 2;
int32_t off_y = (lv_area_get_height(coords) - image_dsc->header.h - 1) / 2;
if(image_dsc->pivot.x != 0 || image_dsc->pivot.y != 0) {
lv_matrix_translate(&matrix, off_x, off_y);
}
lv_matrix_translate(&matrix, image_dsc->pivot.x, image_dsc->pivot.y);
lv_matrix_rotate(&matrix, image_dsc->rotation / 10.0f);
lv_matrix_scale(&matrix, image_dsc->scale_x / 256.0f, image_dsc->scale_y / 256.0f);
lv_matrix_translate(&matrix, -image_dsc->pivot.x, -image_dsc->pivot.y);
}
lv_draw_vector_dsc_set_transform(dsc, &matrix);
lv_draw_svg_render(dsc, list);
lv_draw_vector(dsc);
lv_draw_vector_dsc_delete(dsc);
LV_PROFILER_DRAW_END;
}
#endif /*LV_USE_SVG*/

View File

@@ -0,0 +1,51 @@
/**
* @file lv_svg_decoder.h
*
*/
#ifndef LV_SVG_DECODER_H
#define LV_SVG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../draw/lv_image_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the SVG decoder functions in LVGL
*/
void lv_svg_decoder_init(void);
void lv_svg_decoder_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SVG_DECODER_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
/**
* @file lv_svg_parser.h
*
*/
#ifndef LV_SVG_PARSER_H
#define LV_SVG_PARSER_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "lv_svg.h"
#include "lv_svg_token.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SVG_PARSER_PROCESS = 0,
LV_SVG_PARSER_IGNORE,
} _lv_svg_parser_state_t;
typedef struct {
uint16_t state;
char * ignore_name;
uint32_t ignore_len;
int32_t dpi;
lv_svg_node_t * doc_root;
lv_svg_node_t * cur_node;
} _lv_svg_parser_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Initialize the SVG parser
* @param parser pointer to a parser object
*/
void _lv_svg_parser_init(_lv_svg_parser_t * parser);
/**
* @brief Deinitialize the SVG parser
* @param parser pointer to a parser object
*/
void _lv_svg_parser_deinit(_lv_svg_parser_t * parser);
/**
* @brief Parse an SVG document
* @param parser pointer to a parser object
* @param token pointer to a token object
* @return true: the parsing is finished, false: the parsing is not finished yet.
*/
bool _lv_svg_parser_token(_lv_svg_parser_t * parser, const _lv_svg_token_t * token);
/**
* @brief Check if the parsing is finished
* @param parser pointer to a parser object
* @return true: the parsing is finished, false: the parsing is not finished yet.
*/
bool _lv_svg_parser_is_finish(_lv_svg_parser_t * parser);
/**
* @brief Dump the SVG tree
* @param root pointer to the root of the SVG tree
* @param depth the depth of the current node in the tree
*/
void _lv_svg_dump_tree(lv_svg_node_t * root, int depth);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_PARSER_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
/**
* @file lv_svg_render.h
*
*/
#ifndef LV_SVG_RENDER_H
#define LV_SVG_RENDER_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#if !LV_USE_VECTOR_GRAPHIC
#error "LV_USE_SVG requires LV_USE_VECTOR_GRAPHIC = 1"
#endif
#include "lv_svg.h"
#include "../../misc/lv_types.h"
#include "../../draw/lv_draw_vector_private.h"
/*********************
* DEFINES
*********************/
#define LV_SVG_RENDER_OBJ(n) ((lv_svg_render_obj_t*)(n))
/**********************
* TYPEDEFS
**********************/
struct _lv_svg_render_class;
typedef struct _lv_svg_render_obj {
struct _lv_svg_render_obj * next;
lv_svg_tag_t tag;
uint32_t flags;
char * id;
lv_vector_path_ctx_t dsc;
lv_matrix_t matrix;
/* for url(XXX) reference */
struct _lv_svg_render_obj * head;
char * fill_ref;
char * stroke_ref;
struct _lv_svg_render_class * clz;
} lv_svg_render_obj_t;
typedef struct _lv_svg_render_class {
void (*set_paint_ref)(struct _lv_svg_render_obj * obj, lv_vector_path_ctx_t * dsc,
const struct _lv_svg_render_obj * target_obj, bool fill);
void (*init)(struct _lv_svg_render_obj * obj, const lv_svg_node_t * node);
void (*render)(const struct _lv_svg_render_obj * obj, lv_draw_vector_dsc_t * dsc, const lv_matrix_t * matrix);
void (*set_attr)(struct _lv_svg_render_obj * obj, lv_vector_path_ctx_t * dsc, const lv_svg_attr_t * attr);
void (*get_bounds)(const struct _lv_svg_render_obj * obj, lv_area_t * area);
void (*get_size)(const struct _lv_svg_render_obj * obj, uint32_t * size);
void (*destroy)(struct _lv_svg_render_obj * obj);
} lv_svg_render_class;
typedef struct _lv_svg_render_hal {
void (*load_image)(const char * image_url, lv_draw_image_dsc_t * img_dsc);
const char * (*get_font_path)(const char * font_family);
} lv_svg_render_hal_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Initialize the SVG render
* @param hal pointer to a structure with rendering functions
*/
void lv_svg_render_init(const lv_svg_render_hal_t * hal);
/**
* @brief Create a new SVG render from an SVG document
* @param svg_doc pointer to the SVG document
* @return pointer to the new SVG render object
*/
lv_svg_render_obj_t * lv_svg_render_create(const lv_svg_node_t * svg_doc);
/**
* @brief Delete an SVG render object
* @param render pointer to the SVG render object to delete
*/
void lv_svg_render_delete(lv_svg_render_obj_t * render);
/**
* @brief Get size of render objects
* @param render pointer to the SVG render object
* @return the bytes of SVG render objects
*/
uint32_t lv_svg_render_get_size(const lv_svg_render_obj_t * render);
/**
* @brief Get viewport's width and height of the render object
* @param render pointer to the SVG render object
* @param width pointer to save the width of the viewport of the SVG render object
* @param height pointer to save the height of the viewport of the SVG render object
* @return lv_result_t, LV_RESULT_OK if success, LV_RESULT_INVALID if fail
*/
lv_result_t lv_svg_render_get_viewport_size(const lv_svg_render_obj_t * render, float * width, float * height);
/**
* @brief Render an SVG object to a vector graphics
* @param dsc pointer to the vector graphics descriptor
* @param render pointer to the SVG render object to render
*/
void lv_draw_svg_render(lv_draw_vector_dsc_t * dsc, const lv_svg_render_obj_t * render);
/**
* @brief Draw an SVG document to a layer
* @param layer pointer to the target layer
* @param svg_doc pointer to the SVG document to draw
*/
void lv_draw_svg(lv_layer_t * layer, const lv_svg_node_t * svg_doc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_RENDER_H*/

View File

@@ -0,0 +1,483 @@
/**
* @file lv_svg_token.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_svg_token.h"
#if LV_USE_SVG
#include "../../../lvgl.h"
#include <ctype.h>
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*
* tag mask quote mask tag search comment doc type xml inst
* | 0 0 0 | 0 0 | 0 | 0 | 0 | 0 | 0 |
*/
enum {
SVG_TAG_MASK = (1 << 3) - 1,
SVG_QUOTE_MASK = (1 << 5) - (1 << 3),
SVG_TAG = 1 << 5,
SVG_SEARCH = 1 << 6,
SVG_COMMENT = 1 << 7,
SVG_DOCTYPE = 1 << 8,
SVG_XMLINST = 1 << 9,
};
typedef uint32_t _lv_svg_parser_bits_t;
enum {
SVG_NO_QUOTE = 0,
SVG_SINGLE_QUOTE = 1,
SVG_DOUBLE_QUOTE = 2,
};
typedef uint32_t _lv_svg_parser_quote_t;
enum {
SVG_NO_TAG = 0,
SVG_TAG_NAME = 1,
SVG_ATTR_START = 2,
SVG_ATTR_NAME = 3,
SVG_SEARCH_EQUAL = 4,
SVG_SEARCH_VALUE = 5,
SVG_QUOTE_VALUE = 6,
SVG_VALUE = 7,
};
typedef uint32_t _lv_svg_parser_tag_state_t;
typedef struct {
uint32_t flags;
const char * cur;
const char * end;
} _lv_svg_parser_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void _set_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags |= bit;
}
static void _clear_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags &= ~bit;
}
static bool _is_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
return state->flags & bit;
}
static void _set_tag_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags = (state->flags & ~SVG_TAG_MASK) | bit;
}
static void _set_quote_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags = (state->flags & ~SVG_QUOTE_MASK) | (bit << 3);
}
static bool _special_handle(_lv_svg_parser_state_t * state)
{
return state->flags & (SVG_TAG | SVG_SEARCH | SVG_TAG_MASK | SVG_COMMENT | SVG_DOCTYPE | SVG_XMLINST);
}
static void _lv_svg_token_init(_lv_svg_token_t * token)
{
token->start = NULL;
token->end = NULL;
token->type = LV_SVG_TOKEN_CONTENT;
token->flat = false;
token->cur_attr = NULL;
lv_array_init(&token->attrs, LV_ARRAY_DEFAULT_CAPACITY, sizeof(_lv_svg_token_attr_t));
}
static void _lv_svg_token_reset(_lv_svg_token_t * token)
{
token->start = NULL;
token->end = NULL;
token->type = LV_SVG_TOKEN_CONTENT;
token->flat = false;
token->cur_attr = NULL;
lv_array_clear(&token->attrs);
}
static bool _lv_svg_token_process(_lv_svg_token_t * token, svg_token_process cb, void * data)
{
if(!token->start || SVG_TOKEN_LEN(token) == 0)
return true;
bool ret = cb(token, data);
_lv_svg_token_reset(token);
return ret;
}
static _lv_svg_token_attr_t * _new_svg_attr(_lv_svg_token_t * token)
{
if((lv_array_size(&token->attrs) + 1) > lv_array_capacity(&token->attrs)) {
lv_array_resize(&token->attrs, token->attrs.capacity << 1);
}
token->attrs.size++;
_lv_svg_token_attr_t * attr = lv_array_at(&token->attrs, token->attrs.size - 1);
lv_memset(attr, 0, sizeof(_lv_svg_token_attr_t));
return attr;
}
static void _svg_parser_xml_inst(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>' && (*(state->cur - 1)) == '?') {
_clear_state(state, SVG_XMLINST);
state->cur++;
break;
}
state->cur++;
}
}
static void _svg_parser_comment(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>' && (*(state->cur - 1)) == '-' && (*(state->cur - 2)) == '-') {
_clear_state(state, SVG_COMMENT);
state->cur++;
break;
}
state->cur++;
}
}
static void _svg_parser_doctype(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
//TODO: processing DTD type
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>') {
_clear_state(state, SVG_DOCTYPE);
state->cur++;
break;
}
state->cur++;
}
}
static bool _svg_parser_tag(_lv_svg_parser_state_t * state, _lv_svg_token_t * token, svg_token_process cb, void * data)
{
while(state->cur <= state->end) {
switch(state->flags & SVG_TAG_MASK) {
case SVG_NO_TAG: {
if(!_lv_svg_token_process(token, cb, data)) {
return false;
}
state->cur++;
}
return true;
case SVG_TAG_NAME: {
char ch = *(state->cur);
if(ch == '/') {
token->type = LV_SVG_TOKEN_END;
state->cur++;
if(!token->start) {
token->start = state->cur;
}
continue;
}
else if(ch == '>' || isspace(ch)) {
token->end = state->cur;
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->start) {
token->type = LV_SVG_TOKEN_BEGIN;
token->start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_ATTR_START: {
char ch = *(state->cur);
if(!isspace(ch) && ch != '\'' && ch != '\"') {
if(ch == '/') {
token->flat = true;
state->cur++;
continue;
}
if(ch == '>') {
_set_tag_state(state, SVG_NO_TAG);
}
else {
token->cur_attr = NULL;
_set_tag_state(state, SVG_ATTR_NAME);
}
continue;
}
}
break;
case SVG_ATTR_NAME: {
if(!token->cur_attr) {
token->cur_attr = _new_svg_attr(token);
}
char ch = *(state->cur);
if(isspace(ch) || ch == '=' || ch == '/' || ch == '>') {
token->cur_attr->name_end = state->cur;
_set_tag_state(state, SVG_SEARCH_EQUAL);
continue;
}
else {
if(!token->cur_attr->name_start) {
token->cur_attr->name_start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_SEARCH_EQUAL: {
char ch = *(state->cur);
if(!isspace(ch) && ch != '/' && ch != '\'' && ch != '\"') {
if(ch == '=') {
_set_tag_state(state, SVG_SEARCH_VALUE);
}
else {
// attr name has empty value
token->cur_attr = NULL;
_set_tag_state(state, SVG_ATTR_START);
continue;
}
}
}
break;
case SVG_SEARCH_VALUE: {
char ch = *(state->cur);
if(!isspace(ch)) {
if(ch == '\'' || ch == '\"') {
if(ch == '\'') {
_set_quote_state(state, SVG_SINGLE_QUOTE);
}
else {
_set_quote_state(state, SVG_DOUBLE_QUOTE);
}
_set_tag_state(state, SVG_QUOTE_VALUE);
}
else {
_set_tag_state(state, SVG_VALUE);
continue;
}
}
}
break;
case SVG_QUOTE_VALUE: {
char ch = *(state->cur);
if((ch == '\'' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_SINGLE_QUOTE)
|| (ch == '\"' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_DOUBLE_QUOTE)) {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
token->cur_attr->value_end = state->cur;
_set_quote_state(state, SVG_NO_QUOTE);
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_VALUE: {
char ch = *(state->cur);
if(isspace(ch) || ch == '>' || ch == '/') {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
token->cur_attr->value_end = state->cur;
_set_quote_state(state, SVG_NO_QUOTE);
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
state->cur++;
continue;
}
}
break;
}
state->cur++;
}
return true;
}
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool _lv_svg_tokenizer(const char * svg_data, uint32_t data_len, svg_token_process cb, void * data)
{
LV_ASSERT_NULL(svg_data);
LV_ASSERT(data_len > 0);
LV_ASSERT_NULL(cb);
LV_ASSERT_NULL(data);
_lv_svg_token_t token;
_lv_svg_token_init(&token);
_lv_svg_parser_state_t state = {
.flags = 0,
.cur = svg_data,
.end = svg_data + data_len,
};
while(state.cur < state.end) {
char ch = *(state.cur);
if(ch == '\r' || ch == '\n') { // skip LR character
state.cur++;
continue;
}
else if(_special_handle(&state)) {
if(_is_state(&state, SVG_TAG)) {
_clear_state(&state, SVG_TAG);
switch(ch) {
case '/': // end tag
_set_tag_state(&state, SVG_TAG_NAME);
break;
case '!': {
// <!-- comment or <!DOCTYPE>
_set_state(&state, SVG_SEARCH); // get more character
state.cur++;
}
break;
case '?': {
// xml instruction
_set_state(&state, SVG_XMLINST);
state.cur++;
}
break;
default: {
if(isalpha(ch)) {
_set_tag_state(&state, SVG_TAG_NAME);
}
else {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
}
// process token
if(!_lv_svg_token_process(&token, cb, data)) {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
else if(_is_state(&state, SVG_SEARCH)) {
if(ch == '-' || isalpha(ch)) {
if(!token.start) {
token.start = state.cur;
}
token.end = state.cur;
}
else {
// processing as a normal tag name.
_clear_state(&state, SVG_SEARCH);
_set_tag_state(&state, SVG_TAG_NAME);
continue;
}
if(((token.end - token.start) == 1) && (token.start[0] == '-') && (token.start[1] == '-')) {
// is <!-- comment start
_clear_state(&state, SVG_SEARCH);
token.start = token.end = NULL;
_set_state(&state, SVG_COMMENT);
}
else if(((token.end - token.start) == 6) && (strncmp(token.start, "DOCTYPE", 7) == 0)) {
_clear_state(&state, SVG_SEARCH);
token.start = token.end = NULL;
_set_state(&state, SVG_DOCTYPE);
}
state.cur++;
}
else if(_is_state(&state, SVG_COMMENT)) {
_svg_parser_comment(&state, &token);
}
else if(_is_state(&state, SVG_DOCTYPE)) {
_svg_parser_doctype(&state, &token);
}
else if(_is_state(&state, SVG_TAG_MASK)) {
if(!_svg_parser_tag(&state, &token, cb, data)) {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
else if(_is_state(&state, SVG_XMLINST)) {
_svg_parser_xml_inst(&state, &token);
}
}
else {
switch(ch) {
case '<': {
_set_state(&state, SVG_TAG); // start a new tag
state.cur++;
}
break;
default: {
if(!token.start) {
token.start = state.cur;
}
if(state.cur == state.end) {
goto finish;
}
token.end = ++state.cur;
}
}
}
}
finish:
lv_array_deinit(&token.attrs);
return true;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SVG*/

View File

@@ -0,0 +1,70 @@
/**
* @file lv_svg_token.h
*
*/
#ifndef LV_SVG_TOKEN_H
#define LV_SVG_TOKEN_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../misc/lv_array.h"
/*********************
* DEFINES
*********************/
#define SVG_TOKEN_LEN(t) ((t)->end - (t)->start)
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SVG_TOKEN_BEGIN = 0,
LV_SVG_TOKEN_END,
LV_SVG_TOKEN_CONTENT,
} _lv_svg_token_type_t;
typedef struct {
const char * name_start;
const char * name_end;
const char * value_start;
const char * value_end;
} _lv_svg_token_attr_t;
typedef struct {
const char * start;
const char * end;
_lv_svg_token_type_t type;
bool flat;
_lv_svg_token_attr_t * cur_attr;
lv_array_t attrs;
} _lv_svg_token_t;
typedef bool (*svg_token_process)(_lv_svg_token_t * token, void * user_data);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Parse SVG data and call a callback for each token
* @param svg_data pointer to SVG data
* @param len length of the SVG data
* @param cb callback function to be called for each token
* @param user_data custom data to be passed to the callback function
* @return true: SVG data successfully parsed, false: error occurred
*/
bool _lv_svg_tokenizer(const char * svg_data, uint32_t len, svg_token_process cb, void * user_data);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_TOKEN_H*/