Init
This commit is contained in:
124
inc/lvgl/src/libs/svg/lv_svg.c
Normal file
124
inc/lvgl/src/libs/svg/lv_svg.c
Normal 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*/
|
||||
336
inc/lvgl/src/libs/svg/lv_svg.h
Normal file
336
inc/lvgl/src/libs/svg/lv_svg.h
Normal 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*/
|
||||
384
inc/lvgl/src/libs/svg/lv_svg_decoder.c
Normal file
384
inc/lvgl/src/libs/svg/lv_svg_decoder.c
Normal 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*/
|
||||
51
inc/lvgl/src/libs/svg/lv_svg_decoder.h
Normal file
51
inc/lvgl/src/libs/svg/lv_svg_decoder.h
Normal 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*/
|
||||
2479
inc/lvgl/src/libs/svg/lv_svg_parser.c
Normal file
2479
inc/lvgl/src/libs/svg/lv_svg_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
84
inc/lvgl/src/libs/svg/lv_svg_parser.h
Normal file
84
inc/lvgl/src/libs/svg/lv_svg_parser.h
Normal 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*/
|
||||
2598
inc/lvgl/src/libs/svg/lv_svg_render.c
Normal file
2598
inc/lvgl/src/libs/svg/lv_svg_render.c
Normal file
File diff suppressed because it is too large
Load Diff
125
inc/lvgl/src/libs/svg/lv_svg_render.h
Normal file
125
inc/lvgl/src/libs/svg/lv_svg_render.h
Normal 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*/
|
||||
483
inc/lvgl/src/libs/svg/lv_svg_token.c
Normal file
483
inc/lvgl/src/libs/svg/lv_svg_token.c
Normal 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*/
|
||||
70
inc/lvgl/src/libs/svg/lv_svg_token.h
Normal file
70
inc/lvgl/src/libs/svg/lv_svg_token.h
Normal 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*/
|
||||
Reference in New Issue
Block a user