/** * @file lv_svg_render.c * */ /********************* * INCLUDES *********************/ #include "../../../lvgl.h" #include "lv_svg_render.h" #if LV_USE_SVG #include "../../misc/lv_text_private.h" #include #include #if LV_USE_FREETYPE #include "../../libs/freetype/lv_freetype_private.h" #endif /********************* * DEFINES *********************/ #ifndef M_PI #define M_PI 3.1415926f #endif #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #define ABS(a) fabsf(a) #define CALC_BOUNDS(p, bounds) \ do { \ if((p).x < (bounds).x1) (bounds).x1 = (int32_t)(p).x; \ if((p).y < (bounds).y1) (bounds).y1 = (int32_t)(p).y; \ if((p).x > (bounds).x2) (bounds).x2 = (int32_t)(p).x; \ if((p).y > (bounds).y2) (bounds).y2 = (int32_t)(p).y; \ } while(0) #define PCT_TO_PX(v, base) ((v) > 1 ? (v) : ((v) * (base))) enum { _RENDER_NORMAL = 0, _RENDER_IN_DEFS = 1, _RENDER_IN_GROUP = 2, }; enum { _RENDER_ATTR_FILL = (4 << 1), _RENDER_ATTR_FILL_RULE = (4 << 2), _RENDER_ATTR_FILL_OPACITY = (4 << 3), _RENDER_ATTR_STROKE = (4 << 4), _RENDER_ATTR_STROKE_OPACITY = (4 << 5), _RENDER_ATTR_STROKE_WIDTH = (4 << 6), _RENDER_ATTR_STROKE_LINECAP = (4 << 7), _RENDER_ATTR_STROKE_LINEJOIN = (4 << 8), _RENDER_ATTR_STROKE_MITER_LIMIT = (4 << 9), _RENDER_ATTR_STROKE_DASH_ARRAY = (4 << 10), }; /********************** * TYPEDEFS **********************/ static void _set_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr); static void _init_draw_dsc(lv_vector_draw_dsc_t * dsc); static void _deinit_draw_dsc(lv_vector_draw_dsc_t * dsc); static void _copy_draw_dsc(lv_vector_draw_dsc_t * dst, const lv_vector_draw_dsc_t * src); static void _prepare_render(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc); static void _special_render(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc); #if LV_USE_FREETYPE static void _freetype_outline_cb(lv_event_t * e); #endif typedef struct { lv_svg_render_obj_t base; float width; float height; bool viewport_fill; } lv_svg_render_viewport_t; typedef struct { lv_svg_render_obj_t base; lv_array_t items; } lv_svg_render_group_t; typedef struct { lv_svg_render_obj_t base; float x; float y; char * xlink; } lv_svg_render_use_t; typedef struct { lv_svg_render_obj_t base; lv_color_t color; float opacity; } lv_svg_render_solid_t; typedef struct { lv_svg_render_obj_t base; lv_vector_gradient_t dsc; lv_svg_gradient_units_t units; } lv_svg_render_gradient_t; typedef struct { lv_svg_render_obj_t base; float x; float y; float width; float height; float rx; float ry; } lv_svg_render_rect_t; typedef struct { lv_svg_render_obj_t base; float cx; float cy; float r; } lv_svg_render_circle_t; typedef struct { lv_svg_render_obj_t base; float cx; float cy; float rx; float ry; } lv_svg_render_ellipse_t; typedef struct { lv_svg_render_obj_t base; float x1; float y1; float x2; float y2; } lv_svg_render_line_t; typedef struct { lv_svg_render_obj_t base; lv_vector_path_t * path; lv_area_t bounds; } lv_svg_render_poly_t; typedef struct { lv_svg_render_obj_t base; float x; float y; float width; float height; lv_draw_image_dsc_t img_dsc; lv_svg_aspect_ratio_t ratio; } lv_svg_render_image_t; #if LV_USE_FREETYPE typedef struct { lv_svg_render_obj_t base; lv_array_t contents; char * family; float size; lv_freetype_font_style_t style; lv_font_t * font; float x; float y; lv_vector_path_t * path; lv_area_t bounds; } lv_svg_render_text_t; typedef struct _lv_svg_render_content { lv_svg_render_obj_t base; void (*render_content)(const struct _lv_svg_render_content * content, lv_vector_dsc_t * dsc, lv_matrix_t * matrix); uint32_t * letters; uint32_t count; } lv_svg_render_content_t; typedef struct { lv_svg_render_content_t base; char * family; float size; lv_freetype_font_style_t style; lv_font_t * font; lv_vector_path_t * path; lv_area_t bounds; } lv_svg_render_tspan_t; #endif struct _lv_svg_draw_dsc { struct _lv_svg_draw_dsc * next; lv_vector_draw_dsc_t dsc; const char * fill_ref; const char * stroke_ref; }; struct _lv_svg_drawing_builder_state { const lv_svg_node_t * doc; struct _lv_svg_draw_dsc * draw_dsc; int in_group_deps; bool in_defs; #if LV_USE_FREETYPE bool in_text; lv_svg_node_t * cur_text; #endif lv_svg_render_obj_t * list; lv_svg_render_obj_t * tail; }; /********************** * STATIC VARIABLES **********************/ static lv_svg_render_hal_t hal_funcs = {NULL}; /********************** * STATIC PROTOTYPES **********************/ void lv_svg_render_init(const lv_svg_render_hal_t * hal) { if(hal) { hal_funcs = *hal; #if LV_USE_FREETYPE lv_freetype_outline_add_event(_freetype_outline_cb, LV_EVENT_ALL, NULL); #endif } } static struct _lv_svg_draw_dsc * _lv_svg_draw_dsc_create(void) { struct _lv_svg_draw_dsc * dsc = lv_malloc_zeroed(sizeof(struct _lv_svg_draw_dsc)); LV_ASSERT_MALLOC(dsc); _init_draw_dsc(&(dsc->dsc)); return dsc; } static void _lv_svg_draw_dsc_delete(struct _lv_svg_draw_dsc * dsc) { while(dsc) { struct _lv_svg_draw_dsc * cur = dsc; dsc = dsc->next; _deinit_draw_dsc(&(cur->dsc)); lv_free(cur); } } static struct _lv_svg_draw_dsc * _lv_svg_draw_dsc_push(struct _lv_svg_draw_dsc * dsc) { if(!dsc) return NULL; struct _lv_svg_draw_dsc * cur = lv_malloc_zeroed(sizeof(struct _lv_svg_draw_dsc)); LV_ASSERT_MALLOC(cur); _copy_draw_dsc(&(cur->dsc), &(dsc->dsc)); cur->fill_ref = dsc->fill_ref; cur->stroke_ref = dsc->stroke_ref; cur->next = dsc; return cur; } static struct _lv_svg_draw_dsc * _lv_svg_draw_dsc_pop(struct _lv_svg_draw_dsc * dsc) { if(!dsc) return NULL; struct _lv_svg_draw_dsc * cur = dsc->next; lv_free(dsc); return cur; } static void _set_viewport_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { lv_svg_render_viewport_t * view = (lv_svg_render_viewport_t *)obj; switch(attr->id) { case LV_SVG_ATTR_WIDTH: view->width = attr->value.fval; break; case LV_SVG_ATTR_HEIGHT: view->height = attr->value.fval; break; case LV_SVG_ATTR_VIEWBOX: { if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { float * vals = attr->value.val; float scale_x = 1.0f; float scale_y = 1.0f; float trans_x = vals[0]; float trans_y = vals[1]; if(view->width > 0 && vals[2] > 0) { scale_x = view->width / vals[2]; } if(view->height > 0 && vals[3] > 0) { scale_y = view->height / vals[3]; } lv_matrix_scale(&obj->matrix, scale_x, scale_y); lv_matrix_translate(&obj->matrix, -trans_x, -trans_y); } } break; case LV_SVG_ATTR_VIEWPORT_FILL: { if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL && attr->val_type == LV_SVG_ATTR_VALUE_DATA) { dsc->fill_dsc.color = lv_color_to_32(lv_color_hex(attr->value.uval), 0xFF); view->viewport_fill = true; } else if(attr->class_type == LV_SVG_ATTR_VALUE_NONE) { view->viewport_fill = false; } } break; case LV_SVG_ATTR_VIEWPORT_FILL_OPACITY: { if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { dsc->fill_dsc.opa = (lv_opa_t)(attr->value.fval * 255.0f); } } break; } } static void _set_use_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_use_t * use = (lv_svg_render_use_t *)obj; switch(attr->id) { case LV_SVG_ATTR_X: use->x = attr->value.fval; break; case LV_SVG_ATTR_Y: use->y = attr->value.fval; break; case LV_SVG_ATTR_XLINK_HREF: { if(use->xlink) lv_free(use->xlink); use->xlink = lv_strdup(attr->value.sval); } break; } } static void _set_solid_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { LV_UNUSED(dsc); lv_svg_render_solid_t * solid = (lv_svg_render_solid_t *)obj; switch(attr->id) { case LV_SVG_ATTR_SOLID_COLOR: solid->color = lv_color_hex(attr->value.uval); break; case LV_SVG_ATTR_SOLID_OPACITY: solid->opacity = attr->value.fval; break; } } static void _set_gradient_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { LV_UNUSED(dsc); lv_svg_render_gradient_t * grad = (lv_svg_render_gradient_t *)obj; switch(attr->id) { case LV_SVG_ATTR_CX: grad->dsc.cx = attr->value.fval; break; case LV_SVG_ATTR_CY: grad->dsc.cy = attr->value.fval; break; case LV_SVG_ATTR_R: grad->dsc.cr = attr->value.fval; break; case LV_SVG_ATTR_X1: grad->dsc.x1 = attr->value.fval; break; case LV_SVG_ATTR_Y1: grad->dsc.y1 = attr->value.fval; break; case LV_SVG_ATTR_X2: grad->dsc.x2 = attr->value.fval; break; case LV_SVG_ATTR_Y2: grad->dsc.y2 = attr->value.fval; break; case LV_SVG_ATTR_GRADIENT_UNITS: grad->units = attr->value.ival; break; } } static void _set_rect_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_rect_t * rect = (lv_svg_render_rect_t *)obj; switch(attr->id) { case LV_SVG_ATTR_X: rect->x = attr->value.fval; break; case LV_SVG_ATTR_Y: rect->y = attr->value.fval; break; case LV_SVG_ATTR_WIDTH: rect->width = attr->value.fval; break; case LV_SVG_ATTR_HEIGHT: rect->height = attr->value.fval; break; case LV_SVG_ATTR_RX: rect->rx = attr->value.fval; break; case LV_SVG_ATTR_RY: rect->ry = attr->value.fval; break; } } static void _set_circle_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_circle_t * circle = (lv_svg_render_circle_t *)obj; switch(attr->id) { case LV_SVG_ATTR_CX: circle->cx = attr->value.fval; break; case LV_SVG_ATTR_CY: circle->cy = attr->value.fval; break; case LV_SVG_ATTR_R: circle->r = attr->value.fval; break; } } static void _set_ellipse_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_ellipse_t * ellipse = (lv_svg_render_ellipse_t *)obj; switch(attr->id) { case LV_SVG_ATTR_CX: ellipse->cx = attr->value.fval; break; case LV_SVG_ATTR_CY: ellipse->cy = attr->value.fval; break; case LV_SVG_ATTR_RX: ellipse->rx = attr->value.fval; break; case LV_SVG_ATTR_RY: ellipse->ry = attr->value.fval; break; } } static void _set_line_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_line_t * line = (lv_svg_render_line_t *)obj; switch(attr->id) { case LV_SVG_ATTR_X1: line->x1 = attr->value.fval; break; case LV_SVG_ATTR_Y1: line->y1 = attr->value.fval; break; case LV_SVG_ATTR_X2: line->x2 = attr->value.fval; break; case LV_SVG_ATTR_Y2: line->y2 = attr->value.fval; break; } } static void _set_polyline_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; if(attr->id == LV_SVG_ATTR_POINTS) { lv_vector_path_clear(poly->path); lv_svg_attr_values_list_t * vals = (lv_svg_attr_values_list_t *)(attr->value.val); uint32_t len = vals->length; lv_svg_point_t * points = (lv_svg_point_t *)(&vals->data); CALC_BOUNDS(points[0], poly->bounds); lv_fpoint_t pt = {points[0].x, points[0].y}; lv_vector_path_move_to(poly->path, &pt); for(uint32_t i = 1; i < len; i++) { pt.x = points[i].x; pt.y = points[i].y; lv_vector_path_line_to(poly->path, &pt); CALC_BOUNDS(pt, poly->bounds); } } } static void _set_polygen_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_polyline_attr(obj, dsc, attr); lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; if(attr->id == LV_SVG_ATTR_POINTS) { lv_vector_path_close(poly->path); } } static size_t _get_path_seg_size(uint32_t cmd) { switch(cmd) { case LV_SVG_PATH_CMD_MOVE_TO: case LV_SVG_PATH_CMD_LINE_TO: case LV_SVG_PATH_CMD_CLOSE: return sizeof(lv_svg_point_t) + sizeof(uint32_t); case LV_SVG_PATH_CMD_QUAD_TO: return sizeof(lv_svg_point_t) * 2 + sizeof(uint32_t); case LV_SVG_PATH_CMD_CURVE_TO: return sizeof(lv_svg_point_t) * 3 + sizeof(uint32_t); default: return 0; } } static void _set_path_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; if(attr->id == LV_SVG_ATTR_D) { lv_vector_path_clear(poly->path); lv_svg_attr_values_list_t * vals = (lv_svg_attr_values_list_t *)(attr->value.val); uint32_t len = vals->length; uint8_t * data_ptr = (uint8_t *)(&vals->data); for(uint32_t i = 0; i < len; i++) { lv_svg_attr_path_value_t * path_seg = (lv_svg_attr_path_value_t *)data_ptr; uint32_t cmd = path_seg->cmd; lv_svg_point_t * points = (lv_svg_point_t *)(&path_seg->data); switch(cmd) { case LV_SVG_PATH_CMD_MOVE_TO: { lv_fpoint_t pt = {points[0].x, points[0].y}; lv_vector_path_move_to(poly->path, &pt); CALC_BOUNDS(pt, poly->bounds); } break; case LV_SVG_PATH_CMD_LINE_TO: { lv_fpoint_t pt = {points[0].x, points[0].y}; lv_vector_path_line_to(poly->path, &pt); CALC_BOUNDS(pt, poly->bounds); } break; case LV_SVG_PATH_CMD_QUAD_TO: { lv_fpoint_t pt[2] = { {points[0].x, points[0].y}, {points[1].x, points[1].y} }; lv_vector_path_quad_to(poly->path, &pt[0], &pt[1]); CALC_BOUNDS(pt[0], poly->bounds); CALC_BOUNDS(pt[1], poly->bounds); } break; case LV_SVG_PATH_CMD_CURVE_TO: { lv_fpoint_t pt[3] = { {points[0].x, points[0].y}, {points[1].x, points[1].y}, {points[2].x, points[2].y} }; lv_vector_path_cubic_to(poly->path, &pt[0], &pt[1], &pt[2]); CALC_BOUNDS(pt[0], poly->bounds); CALC_BOUNDS(pt[1], poly->bounds); CALC_BOUNDS(pt[2], poly->bounds); } break; case LV_SVG_PATH_CMD_CLOSE: { lv_vector_path_close(poly->path); } break; } size_t mem_inc = _get_path_seg_size(cmd); data_ptr += mem_inc; } } } #if LV_USE_FREETYPE #define SET_FONT_ATTRS(obj, attr) \ do { \ switch(attr->id) { \ case LV_SVG_ATTR_FONT_FAMILY: { \ if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { \ if((obj)->font) { \ lv_freetype_font_delete((obj)->font); \ (obj)->font = NULL; \ } \ lv_vector_path_clear((obj)->path); \ if((obj)->family) lv_free((obj)->family); \ (obj)->family = lv_strdup(attr->value.sval); \ } \ } \ break; \ case LV_SVG_ATTR_FONT_SIZE: \ if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { \ if(attr->val_type == LV_SVG_ATTR_VALUE_DATA) { \ if((obj)->font) { \ lv_freetype_font_delete((obj)->font); \ (obj)->font = NULL; \ } \ (obj)->size = attr->value.fval; \ lv_vector_path_clear((obj)->path); \ } \ } \ break; \ case LV_SVG_ATTR_FONT_STYLE: \ if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { \ if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { \ if((obj)->font) { \ lv_freetype_font_delete((obj)->font); \ (obj)->font = NULL; \ } \ lv_vector_path_clear((obj)->path); \ if(strncmp(attr->value.sval, "italic", 6) == 0) { \ (obj)->style = LV_FREETYPE_FONT_STYLE_ITALIC; \ } \ } \ } \ break; \ case LV_SVG_ATTR_FONT_WEIGHT: \ if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { \ if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { \ if((obj)->font) { \ lv_freetype_font_delete((obj)->font); \ (obj)->font = NULL; \ } \ lv_vector_path_clear((obj)->path); \ if(strncmp(attr->value.sval, "bold", 4) == 0) { \ (obj)->style = LV_FREETYPE_FONT_STYLE_BOLD; \ } \ } \ } \ break; \ case LV_SVG_ATTR_FONT_VARIANT: \ if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { \ if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { \ if((obj)->font) { \ lv_freetype_font_delete((obj)->font); \ (obj)->font = NULL; \ } \ lv_vector_path_clear((obj)->path); \ if(strncmp(attr->value.sval, "small-caps", 10) == 0) { \ (obj)->size /= 2; \ } \ } \ } \ break; \ } \ } while(0) static void _set_tspan_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_tspan_t * tspan = (lv_svg_render_tspan_t *)obj; SET_FONT_ATTRS(tspan, attr); } static void _set_text_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_text_t * text = (lv_svg_render_text_t *)obj; SET_FONT_ATTRS(text, attr); switch(attr->id) { case LV_SVG_ATTR_X: text->x = attr->value.fval; break; case LV_SVG_ATTR_Y: text->y = attr->value.fval; break; } } #endif static void _set_image_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { _set_attr(obj, dsc, attr); lv_svg_render_image_t * image = (lv_svg_render_image_t *)obj; switch(attr->id) { case LV_SVG_ATTR_X: image->x = attr->value.fval; break; case LV_SVG_ATTR_Y: image->y = attr->value.fval; break; case LV_SVG_ATTR_HEIGHT: image->height = attr->value.fval; break; case LV_SVG_ATTR_WIDTH: image->width = attr->value.fval; break; case LV_SVG_ATTR_OPACITY: image->img_dsc.opa = (lv_opa_t)(attr->value.fval * 255.0f); break; case LV_SVG_ATTR_XLINK_HREF: { const char * xlink = attr->value.sval; if(hal_funcs.load_image) { hal_funcs.load_image(xlink, &image->img_dsc); } } break; case LV_SVG_ATTR_PRESERVE_ASPECT_RATIO: { if(attr->class_type == LV_SVG_ATTR_VALUE_INITIAL) { image->ratio = attr->value.uval; } } break; } } static void _set_attr(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr) { LV_UNUSED(obj); switch(attr->id) { case LV_SVG_ATTR_FILL: { if(attr->class_type == LV_SVG_ATTR_VALUE_NONE) { dsc->fill_dsc.opa = LV_OPA_0; obj->flags |= _RENDER_ATTR_FILL_OPACITY; obj->flags |= _RENDER_ATTR_FILL; return; } else if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_FILL; return; } else { if(obj->fill_ref) { lv_free(obj->fill_ref); obj->fill_ref = NULL; } if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { obj->fill_ref = lv_strdup(attr->value.sval); } else { // color dsc->fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; dsc->fill_dsc.color = lv_color_to_32(lv_color_hex(attr->value.uval), 0xFF); } obj->flags |= _RENDER_ATTR_FILL; if(obj->dsc.fill_dsc.opa == LV_OPA_0) { dsc->fill_dsc.opa = LV_OPA_COVER; obj->flags |= _RENDER_ATTR_FILL_OPACITY; } } } break; case LV_SVG_ATTR_STROKE: { if(attr->class_type == LV_SVG_ATTR_VALUE_NONE) { dsc->stroke_dsc.opa = LV_OPA_0; obj->flags |= _RENDER_ATTR_STROKE_OPACITY; obj->flags |= _RENDER_ATTR_STROKE; return; } else if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE; return; } else { if(obj->stroke_ref) { lv_free(obj->stroke_ref); obj->stroke_ref = NULL; } if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) { obj->stroke_ref = lv_strdup(attr->value.sval); } else { // color dsc->stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; dsc->stroke_dsc.color = lv_color_to_32(lv_color_hex(attr->value.uval), 0xFF); } } obj->flags |= _RENDER_ATTR_STROKE; if(obj->dsc.stroke_dsc.opa == LV_OPA_0) { dsc->stroke_dsc.opa = LV_OPA_COVER; obj->flags |= _RENDER_ATTR_STROKE_OPACITY; } } break; case LV_SVG_ATTR_FILL_OPACITY: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_FILL_OPACITY; return; } dsc->fill_dsc.opa = (lv_opa_t)(attr->value.fval * 255.0f); obj->flags |= _RENDER_ATTR_FILL_OPACITY; } break; case LV_SVG_ATTR_STROKE_OPACITY: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_OPACITY; return; } dsc->stroke_dsc.opa = (lv_opa_t)(attr->value.fval * 255.0f); obj->flags |= _RENDER_ATTR_STROKE_OPACITY; } break; case LV_SVG_ATTR_FILL_RULE: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_FILL_RULE; return; } dsc->fill_dsc.fill_rule = attr->value.ival; obj->flags |= _RENDER_ATTR_FILL_RULE; } break; case LV_SVG_ATTR_STROKE_WIDTH: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_WIDTH; return; } dsc->stroke_dsc.width = attr->value.fval; obj->flags |= _RENDER_ATTR_STROKE_WIDTH; } break; case LV_SVG_ATTR_STROKE_LINECAP: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_LINECAP; return; } dsc->stroke_dsc.cap = attr->value.ival; obj->flags |= _RENDER_ATTR_STROKE_LINECAP; } break; case LV_SVG_ATTR_STROKE_LINEJOIN: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_LINEJOIN; return; } dsc->stroke_dsc.join = attr->value.ival; obj->flags |= _RENDER_ATTR_STROKE_LINEJOIN; } break; case LV_SVG_ATTR_STROKE_MITER_LIMIT: { if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_MITER_LIMIT; return; } dsc->stroke_dsc.miter_limit = attr->value.ival; obj->flags |= _RENDER_ATTR_STROKE_MITER_LIMIT; } break; case LV_SVG_ATTR_STROKE_DASH_ARRAY: { if(attr->class_type == LV_SVG_ATTR_VALUE_NONE) { lv_array_clear(&(dsc->stroke_dsc.dash_pattern)); obj->flags |= _RENDER_ATTR_STROKE_DASH_ARRAY; return; } else if(attr->class_type == LV_SVG_ATTR_VALUE_INHERIT) { obj->flags &= ~_RENDER_ATTR_STROKE_DASH_ARRAY; return; } else { lv_array_t * dash_array = &(dsc->stroke_dsc.dash_pattern); lv_svg_attr_values_list_t * vals = (lv_svg_attr_values_list_t *)(attr->value.val); uint32_t len = vals->length; float * dashs = (float *)(&vals->data); lv_array_clear(dash_array); obj->flags |= _RENDER_ATTR_STROKE_DASH_ARRAY; if(len) { if(lv_array_capacity(dash_array) == 0) { lv_array_init(dash_array, len, sizeof(float)); } else { lv_array_resize(dash_array, len); } for(uint32_t i = 0; i < len; i++) { lv_array_push_back(dash_array, (uint8_t *)(&dashs[i])); } } } } break; case LV_SVG_ATTR_TRANSFORM: { if(attr->class_type == LV_SVG_ATTR_VALUE_NONE) { return; } lv_memcpy(&(obj->matrix), attr->value.val, sizeof(lv_matrix_t)); } break; case LV_SVG_ATTR_STROKE_DASH_OFFSET: // not support yet break; } } static void _set_solid_ref(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_render_obj_t * target_obj, bool fill) { LV_UNUSED(target_obj); lv_svg_render_solid_t * solid = (lv_svg_render_solid_t *)obj; if(fill) { dsc->fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; dsc->fill_dsc.color = lv_color_to_32(solid->color, 0xFF); dsc->fill_dsc.opa = (lv_opa_t)(solid->opacity * 255.0f); } else { dsc->stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; dsc->stroke_dsc.color = lv_color_to_32(solid->color, 0xFF); dsc->stroke_dsc.opa = (lv_opa_t)(solid->opacity * 255.0f); } } static void _set_gradient_ref(lv_svg_render_obj_t * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_render_obj_t * target_obj, bool fill) { if(!target_obj->get_bounds) { return; } lv_svg_render_gradient_t * grad = (lv_svg_render_gradient_t *)obj; lv_vector_gradient_t * grad_dsc = NULL; lv_matrix_t * mtx = NULL; if(fill) { dsc->fill_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; grad_dsc = &dsc->fill_dsc.gradient; mtx = &dsc->fill_dsc.matrix; } else { dsc->stroke_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; grad_dsc = &dsc->stroke_dsc.gradient; mtx = &dsc->stroke_dsc.matrix; } lv_memcpy(grad_dsc, &grad->dsc, sizeof(lv_vector_gradient_t)); lv_area_t bounds = {0, 0, 0, 0}; target_obj->get_bounds(target_obj, &bounds); int32_t w = bounds.x2 - bounds.x1; int32_t h = bounds.y2 - bounds.y1; if(grad->dsc.style == LV_VECTOR_GRADIENT_STYLE_RADIAL) { if(grad->units == LV_SVG_GRADIENT_UNITS_OBJECT) { grad_dsc->cx = PCT_TO_PX(grad_dsc->cx, w); grad_dsc->cy = PCT_TO_PX(grad_dsc->cy, h); grad_dsc->cr = PCT_TO_PX(grad_dsc->cr, MAX(w, h)); lv_matrix_translate(mtx, bounds.x1, bounds.y1); } } else { // LV_VECTOR_GRADIENT_STYLE_LINEAR if(grad->units == LV_SVG_GRADIENT_UNITS_OBJECT) { grad_dsc->x1 = PCT_TO_PX(grad_dsc->x1, w); grad_dsc->y1 = PCT_TO_PX(grad_dsc->y1, h); grad_dsc->x2 = PCT_TO_PX(grad_dsc->x2, w); grad_dsc->y2 = PCT_TO_PX(grad_dsc->y2, h); lv_matrix_translate(mtx, bounds.x1, bounds.y1); } } } static void _init_draw_dsc(lv_vector_draw_dsc_t * dsc) { lv_vector_fill_dsc_t * fill_dsc = &(dsc->fill_dsc); fill_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID; fill_dsc->color = lv_color_to_32(lv_color_black(), 0xFF); fill_dsc->opa = LV_OPA_COVER; fill_dsc->fill_rule = LV_VECTOR_FILL_NONZERO; lv_matrix_identity(&(fill_dsc->matrix)); // identity matrix lv_vector_stroke_dsc_t * stroke_dsc = &(dsc->stroke_dsc); stroke_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID; stroke_dsc->color = lv_color_to_32(lv_color_black(), 0xFF); stroke_dsc->opa = LV_OPA_0; // default no stroke stroke_dsc->width = 1.0f; stroke_dsc->cap = LV_VECTOR_STROKE_CAP_BUTT; stroke_dsc->join = LV_VECTOR_STROKE_JOIN_MITER; stroke_dsc->miter_limit = 4.0f; lv_matrix_identity(&(stroke_dsc->matrix)); // identity matrix dsc->blend_mode = LV_VECTOR_BLEND_SRC_OVER; lv_matrix_identity(&(dsc->matrix)); // identity matrix } static void _deinit_draw_dsc(lv_vector_draw_dsc_t * dsc) { lv_array_deinit(&(dsc->stroke_dsc.dash_pattern)); } static void _copy_draw_dsc(lv_vector_draw_dsc_t * dst, const lv_vector_draw_dsc_t * src) { lv_memcpy(&dst->fill_dsc, &src->fill_dsc, sizeof(lv_vector_fill_dsc_t)); dst->stroke_dsc.style = src->stroke_dsc.style; dst->stroke_dsc.color = src->stroke_dsc.color; dst->stroke_dsc.opa = src->stroke_dsc.opa; dst->stroke_dsc.width = src->stroke_dsc.width; dst->stroke_dsc.cap = src->stroke_dsc.cap; dst->stroke_dsc.join = src->stroke_dsc.join; dst->stroke_dsc.miter_limit = src->stroke_dsc.miter_limit; lv_array_copy(&(dst->stroke_dsc.dash_pattern), &(src->stroke_dsc.dash_pattern)); lv_memcpy(&(dst->stroke_dsc.gradient), &(src->stroke_dsc.gradient), sizeof(lv_vector_gradient_t)); lv_memcpy(&(dst->stroke_dsc.matrix), &(src->stroke_dsc.matrix), sizeof(lv_matrix_t)); dst->blend_mode = src->blend_mode; } static void _copy_draw_dsc_from_ref(lv_vector_dsc_t * dsc, const lv_svg_render_obj_t * obj) { lv_vector_draw_dsc_t * dst = &(dsc->current_dsc); if(obj->fill_ref) { lv_svg_render_obj_t * list = obj->head; while(list) { if(list->id) { if(strcmp(obj->fill_ref, list->id) == 0) { list->set_paint_ref(list, dst, obj, true); break; } } list = list->next; } } if(obj->stroke_ref) { lv_svg_render_obj_t * list = obj->head; while(list) { if(list->id) { if(strcmp(obj->stroke_ref, list->id) == 0) { list->set_paint_ref(list, dst, obj, false); break; } } list = list->next; } } } static void _set_render_attrs(lv_svg_render_obj_t * obj, const lv_svg_node_t * node, struct _lv_svg_drawing_builder_state * state) { if((node->type != LV_SVG_TAG_CONTENT) && node->xml_id) { obj->id = lv_strdup(node->xml_id); } if(obj->init) { obj->init(obj, node); } if(state->draw_dsc->fill_ref) { obj->fill_ref = lv_strdup(state->draw_dsc->fill_ref); } if(state->draw_dsc->stroke_ref) { obj->stroke_ref = lv_strdup(state->draw_dsc->stroke_ref); } uint32_t len = lv_array_size(&node->attrs); for(uint32_t i = 0; i < len; i++) { lv_svg_attr_t * attr = lv_array_at(&node->attrs, i); obj->set_attr(obj, &(state->draw_dsc->dsc), attr); } if(node->type == LV_SVG_TAG_G) { // only need store it state->draw_dsc->fill_ref = obj->fill_ref; state->draw_dsc->stroke_ref = obj->stroke_ref; } obj->head = state->list; } // init functions static void _init_obj(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { LV_UNUSED(node); lv_matrix_identity(&obj->matrix); } static void _init_viewport(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_viewport_t * view = (lv_svg_render_viewport_t *)obj; view->viewport_fill = false; } static void _init_group(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_group_t * group = (lv_svg_render_group_t *)obj; lv_array_init(&group->items, LV_TREE_NODE(node)->child_cnt, sizeof(lv_svg_render_obj_t *)); } static void _init_image(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_image_t * image = (lv_svg_render_image_t *)obj; lv_draw_image_dsc_init(&image->img_dsc); image->ratio = LV_SVG_ASPECT_RATIO_XMID_YMID | LV_SVG_ASPECT_RATIO_OPT_MEET; } static void _init_poly(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; poly->path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_area_set(&poly->bounds, 0, 0, 0, 0); } #if LV_USE_FREETYPE static void _init_text(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_text_t * text = (lv_svg_render_text_t *)obj; text->family = lv_strdup("sans-serif"); text->size = 16.0f; text->style = LV_FREETYPE_FONT_STYLE_NORMAL; text->font = NULL; text->x = text->y = 0.0f; lv_array_init(&text->contents, LV_TREE_NODE(node)->child_cnt, sizeof(lv_svg_render_obj_t *)); text->path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); } static void _init_content(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_content_t * content = (lv_svg_render_content_t *)obj; const char * str = node->xml_id; content->count = lv_text_get_encoded_length(str); content->letters = lv_malloc(sizeof(uint32_t) * content->count); LV_ASSERT_MALLOC(content->letters); uint32_t offset = 0; for(uint32_t i = 0; i < content->count; i++) { content->letters[i] = lv_text_encoded_next(str, &offset); } } static void _init_tspan(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_tspan_t * span = (lv_svg_render_tspan_t *)obj; lv_svg_node_t * parent = LV_SVG_NODE(LV_TREE_NODE(node)->parent); if(parent->type != LV_SVG_TAG_TEXT) { return; } lv_svg_render_text_t * text = (lv_svg_render_text_t *)parent->render_obj; span->family = lv_strdup(text->family); span->size = text->size; span->style = text->style; span->path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_svg_node_t * content_node = LV_SVG_NODE(LV_TREE_NODE(node)->children[0]); _init_content(obj, content_node); } #endif static void _init_gradient(lv_svg_render_obj_t * obj, const lv_svg_node_t * node) { _init_obj(obj, node); lv_svg_render_gradient_t * grad = (lv_svg_render_gradient_t *)obj; grad->units = LV_SVG_GRADIENT_UNITS_OBJECT; grad->dsc.cx = 0.5f; grad->dsc.cy = 0.5f; grad->dsc.cr = 0.5f; grad->dsc.x1 = 0.0f; grad->dsc.y1 = 0.0f; grad->dsc.x2 = 1.0f; grad->dsc.y2 = 0.0f; grad->dsc.spread = LV_VECTOR_GRADIENT_SPREAD_PAD; uint32_t count = LV_TREE_NODE(node)->child_cnt; uint32_t stop_count = 0; for(uint32_t i = 0; i < count; i++) { lv_svg_node_t * child_node = LV_SVG_NODE_CHILD(node, i); uint32_t len = lv_array_size(&child_node->attrs); bool is_stop = false; lv_color_t stop_color = lv_color_black(); lv_opa_t stop_opa = LV_OPA_COVER; uint8_t stop_frac = 0; for(uint32_t j = 0; j < len; j++) { lv_svg_attr_t * attr = lv_array_at(&child_node->attrs, j); switch(attr->id) { case LV_SVG_ATTR_GRADIENT_STOP_COLOR: { stop_color = lv_color_hex(attr->value.uval); is_stop = true; } break; case LV_SVG_ATTR_GRADIENT_STOP_OPACITY: { stop_opa = (lv_opa_t)(attr->value.fval * 255.0f); is_stop = true; } break; case LV_SVG_ATTR_GRADIENT_STOP_OFFSET: { stop_frac = (uint8_t)(attr->value.fval * 255.0f); is_stop = true; } break; } } if(is_stop) { grad->dsc.stops[stop_count].opa = stop_opa; grad->dsc.stops[stop_count].frac = stop_frac; grad->dsc.stops[stop_count].color = stop_color; stop_count++; } if(stop_count == LV_GRADIENT_MAX_STOPS) { break; } } grad->dsc.stops_count = stop_count; } static void _setup_matrix(lv_matrix_t * matrix, lv_vector_dsc_t * dsc, const lv_svg_render_obj_t * obj) { lv_memcpy(matrix, &dsc->current_dsc.matrix, sizeof(lv_matrix_t)); lv_matrix_multiply(&dsc->current_dsc.matrix, &obj->matrix); } static void _restore_matrix(lv_matrix_t * matrix, lv_vector_dsc_t * dsc) { lv_memcpy(&dsc->current_dsc.matrix, matrix, sizeof(lv_matrix_t)); } static void _prepare_render(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc) { _copy_draw_dsc(&(dsc->current_dsc), &(obj->dsc)); } static void _special_render(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc) { const lv_vector_draw_dsc_t * src = &(obj->dsc); lv_vector_draw_dsc_t * dst = &(dsc->current_dsc); if(obj->flags & _RENDER_ATTR_FILL) { lv_memcpy(&(dst->fill_dsc), &(src->fill_dsc), sizeof(lv_vector_fill_dsc_t)); dst->blend_mode = src->blend_mode; } if(obj->flags & _RENDER_ATTR_FILL_OPACITY) { dst->fill_dsc.opa = src->fill_dsc.opa; } if(obj->flags & _RENDER_ATTR_FILL_RULE) { dst->fill_dsc.fill_rule = src->fill_dsc.fill_rule; } if(obj->flags & _RENDER_ATTR_STROKE) { dst->stroke_dsc.style = src->stroke_dsc.style; dst->stroke_dsc.color = src->stroke_dsc.color; lv_memcpy(&(dst->stroke_dsc.gradient), &(src->stroke_dsc.gradient), sizeof(lv_vector_gradient_t)); lv_memcpy(&(dst->stroke_dsc.matrix), &(src->stroke_dsc.matrix), sizeof(lv_matrix_t)); dst->blend_mode = src->blend_mode; } if(obj->flags & _RENDER_ATTR_STROKE_OPACITY) { dst->stroke_dsc.opa = src->stroke_dsc.opa; } if(obj->flags & _RENDER_ATTR_STROKE_WIDTH) { dst->stroke_dsc.width = src->stroke_dsc.width; } if(obj->flags & _RENDER_ATTR_STROKE_LINECAP) { dst->stroke_dsc.cap = src->stroke_dsc.cap; } if(obj->flags & _RENDER_ATTR_STROKE_LINEJOIN) { dst->stroke_dsc.join = src->stroke_dsc.join; } if(obj->flags & _RENDER_ATTR_STROKE_MITER_LIMIT) { dst->stroke_dsc.miter_limit = src->stroke_dsc.miter_limit; } if(obj->flags & _RENDER_ATTR_STROKE_DASH_ARRAY) { lv_array_copy(&(dst->stroke_dsc.dash_pattern), &(src->stroke_dsc.dash_pattern)); } } // render functions static void _render_viewport(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { LV_UNUSED(matrix); lv_svg_render_viewport_t * view = (lv_svg_render_viewport_t *)obj; lv_matrix_multiply(&dsc->current_dsc.matrix, &obj->matrix); if(view->viewport_fill) { lv_area_t rc = {0, 0, (int32_t)view->width, (int32_t)view->height}; lv_vector_clear_area(dsc, &rc); } } static void _render_rect(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_rect_t * rect = (lv_svg_render_rect_t *)obj; if(rect->rx > 0 && rect->ry == 0) rect->ry = rect->rx; else if(rect->ry > 0 && rect->rx == 0) rect->rx = rect->ry; lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_area_t rc = {(int32_t)rect->x, (int32_t)rect->y, (int32_t)(rect->x + rect->width), (int32_t)(rect->y + rect->height)}; lv_vector_path_append_rect(path, &rc, rect->rx, rect->ry); _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, path); lv_vector_path_delete(path); _restore_matrix(&mtx, dsc); } static void _render_circle(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_circle_t * circle = (lv_svg_render_circle_t *)obj; lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_fpoint_t cp = {circle->cx, circle->cy}; lv_vector_path_append_circle(path, &cp, circle->r, circle->r); _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, path); lv_vector_path_delete(path); _restore_matrix(&mtx, dsc); } static void _render_ellipse(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_ellipse_t * ellipse = (lv_svg_render_ellipse_t *)obj; lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_fpoint_t cp = {ellipse->cx, ellipse->cy}; lv_vector_path_append_circle(path, &cp, ellipse->rx, ellipse->ry); _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, path); lv_vector_path_delete(path); _restore_matrix(&mtx, dsc); } static void _render_line(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_line_t * line = (lv_svg_render_line_t *)obj; lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_fpoint_t sp = {line->x1, line->y1}; lv_vector_path_move_to(path, &sp); lv_fpoint_t ep = {line->x2, line->y2}; lv_vector_path_line_to(path, &ep); _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, path); lv_vector_path_delete(path); _restore_matrix(&mtx, dsc); } static void _render_poly(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, poly->path); _restore_matrix(&mtx, dsc); } static void _render_group(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_svg_render_group_t * group = (lv_svg_render_group_t *)obj; lv_matrix_t mtx; _setup_matrix(&mtx, dsc, obj); struct _lv_svg_draw_dsc save_dsc; lv_memzero(&save_dsc, sizeof(struct _lv_svg_draw_dsc)); for(uint32_t i = 0; i < group->items.size; i++) { lv_svg_render_obj_t * list = *((lv_svg_render_obj_t **)lv_array_at(&group->items, i)); if(list->render && (list->flags & _RENDER_IN_GROUP)) { _copy_draw_dsc(&(save_dsc.dsc), &(dsc->current_dsc)); _special_render(list, dsc); list->render(list, dsc, matrix); _copy_draw_dsc(&(dsc->current_dsc), &(save_dsc.dsc)); } } _restore_matrix(&mtx, dsc); } static void _render_image(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_matrix_t imtx; _setup_matrix(&imtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } lv_svg_render_image_t * image = (lv_svg_render_image_t *)obj; if(!image->img_dsc.header.w || !image->img_dsc.header.h || !image->img_dsc.src) { return; } lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); lv_area_t rc = {(int32_t)image->x, (int32_t)image->y, (int32_t)(image->x + image->width), (int32_t)(image->y + image->height)}; lv_vector_path_append_rect(path, &rc, 0, 0); lv_matrix_t mtx; lv_matrix_identity(&mtx); float img_w = (float)image->img_dsc.header.w; float img_h = (float)image->img_dsc.header.h; float scale_x = image->width / img_w; float scale_y = image->height / img_h; float scale = 1.0f; if((image->ratio & 0x1) == LV_SVG_ASPECT_RATIO_OPT_SLICE) { scale = MAX(scale_x, scale_y); } else if((image->ratio & 0x1) == LV_SVG_ASPECT_RATIO_OPT_MEET) { scale = MIN(scale_x, scale_y); } uint32_t align = image->ratio & ~0x1; switch(align) { case LV_SVG_ASPECT_RATIO_NONE: lv_matrix_scale(&mtx, scale_x, scale_y); break; case LV_SVG_ASPECT_RATIO_XMIN_YMIN: lv_matrix_scale(&mtx, scale, scale); break; case LV_SVG_ASPECT_RATIO_XMID_YMIN: { float tx = (image->width - img_w * scale) / 2; lv_matrix_translate(&mtx, tx, 0); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMAX_YMIN: { float tx = image->width - img_w * scale; lv_matrix_translate(&mtx, tx, 0); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMIN_YMID: { float ty = (image->height - img_h * scale) / 2; lv_matrix_translate(&mtx, 0, ty); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMID_YMID: { float tx = (image->width - img_w * scale) / 2; float ty = (image->height - img_h * scale) / 2; lv_matrix_translate(&mtx, tx, ty); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMAX_YMID: { float tx = image->width - img_w * scale; float ty = (image->height - img_h * scale) / 2; lv_matrix_translate(&mtx, tx, ty); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMIN_YMAX: { float ty = image->height - img_h * scale; lv_matrix_translate(&mtx, 0, ty); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMID_YMAX: { float tx = (image->width - img_w * scale) / 2; float ty = image->height - img_h * scale; lv_matrix_translate(&mtx, tx, ty); lv_matrix_scale(&mtx, scale, scale); } break; case LV_SVG_ASPECT_RATIO_XMAX_YMAX: { float tx = image->width - img_w * scale; float ty = image->height - img_h * scale; lv_matrix_translate(&mtx, tx, ty); lv_matrix_scale(&mtx, scale, scale); } break; } lv_vector_dsc_set_fill_transform(dsc, &mtx); lv_vector_dsc_set_fill_image(dsc, &image->img_dsc); _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, path); lv_vector_path_delete(path); _restore_matrix(&imtx, dsc); } static void _render_use(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { LV_UNUSED(matrix); lv_matrix_t imtx; _setup_matrix(&imtx, dsc, obj); lv_svg_render_use_t * use = (lv_svg_render_use_t *)obj; lv_matrix_t mtx; lv_matrix_identity(&mtx); lv_matrix_translate(&mtx, use->x, use->y); lv_svg_render_obj_t * list = obj->head; while(list) { if(list->id) { if(strcmp(use->xlink, list->id) == 0) { if(list->render) { _prepare_render(list, dsc); _special_render(obj, dsc); list->render(list, dsc, &mtx); } break; } } list = list->next; } _restore_matrix(&imtx, dsc); } #if LV_USE_FREETYPE static void _render_text(const lv_svg_render_obj_t * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) { lv_svg_render_text_t * text = (lv_svg_render_text_t *)obj; if(!text->font) { if(!hal_funcs.get_font_path) { return; } const char * font_path = hal_funcs.get_font_path(text->family); if(!font_path) { return; } text->font = lv_freetype_font_create(font_path, LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, (uint32_t)text->size, text->style); } if(!text->font || !lv_freetype_is_outline_font(text->font)) { LV_LOG_ERROR("svg current font is not outline font!"); return; } lv_matrix_t tmtx; _setup_matrix(&tmtx, dsc, obj); if(matrix) { lv_matrix_multiply(&dsc->current_dsc.matrix, matrix); } if(lv_array_size(&text->path->ops) == 0) { /* empty path */ lv_vector_path_t * glyph_path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); // draw text contents and spans lv_matrix_t mtx; lv_matrix_identity(&mtx); lv_matrix_translate(&mtx, text->x, text->y); for(uint32_t i = 0; i < lv_array_size(&text->contents); i++) { lv_svg_render_obj_t * ptext = *((lv_svg_render_obj_t **)lv_array_at(&text->contents, i)); lv_svg_render_content_t * content = (lv_svg_render_content_t *)ptext; if(content->render_content) { content->render_content(content, dsc, &mtx); } else { float scale = text->size / 128.0f; for(uint32_t j = 0; j < content->count; j++) { uint32_t letter = content->letters[j]; lv_font_glyph_dsc_t g; lv_font_get_glyph_dsc(text->font, &g, letter, '\0'); lv_vector_path_t * p = (lv_vector_path_t *)lv_font_get_glyph_bitmap(&g, NULL); lv_vector_path_clear(glyph_path); lv_vector_path_copy(glyph_path, p); uint32_t letter_w = g.box_w > 0 ? g.box_w : g.adv_w; lv_matrix_t scale_matrix = mtx; lv_matrix_translate(&mtx, g.ofs_x, 0); lv_matrix_scale(&scale_matrix, scale, scale); lv_matrix_transform_path(&scale_matrix, glyph_path); lv_vector_path_append_path(text->path, glyph_path); text->font->release_glyph(text->font, &g); lv_matrix_translate(&mtx, letter_w, 0); } } } lv_vector_path_delete(glyph_path); lv_vector_path_get_bounding(text->path, &text->bounds); } _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, text->path); _restore_matrix(&tmtx, dsc); } static void _render_span(const lv_svg_render_content_t * content, lv_vector_dsc_t * dsc, lv_matrix_t * matrix) { lv_svg_render_obj_t * obj = (lv_svg_render_obj_t *)content; lv_svg_render_tspan_t * span = (lv_svg_render_tspan_t *)content; if(!span->font) { if(!hal_funcs.get_font_path) { return; } const char * font_path = hal_funcs.get_font_path(span->family); if(!font_path) { return; } span->font = lv_freetype_font_create(font_path, LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, (uint32_t)span->size, span->style); } if(!span->font || !lv_freetype_is_outline_font(span->font)) { LV_LOG_ERROR("svg current font is not outline font!"); return; } struct _lv_svg_draw_dsc save_dsc; lv_memzero(&save_dsc, sizeof(struct _lv_svg_draw_dsc)); _copy_draw_dsc(&(save_dsc.dsc), &(dsc->current_dsc)); _copy_draw_dsc(&(dsc->current_dsc), &(obj->dsc)); if(lv_array_size(&span->path->ops) == 0) { /* empty path */ lv_vector_path_t * glyph_path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); // draw text contents and spans lv_matrix_t * mtx = matrix; float scale = span->size / 128.0f; for(uint32_t j = 0; j < content->count; j++) { uint32_t letter = content->letters[j]; lv_font_glyph_dsc_t g; lv_font_get_glyph_dsc(span->font, &g, letter, '\0'); lv_vector_path_t * p = (lv_vector_path_t *)lv_font_get_glyph_bitmap(&g, NULL); lv_vector_path_clear(glyph_path); lv_vector_path_copy(glyph_path, p); uint32_t letter_w = g.box_w > 0 ? g.box_w : g.adv_w; lv_matrix_t scale_matrix = *mtx; lv_matrix_translate(mtx, g.ofs_x, 0); lv_matrix_scale(&scale_matrix, scale, scale); lv_matrix_transform_path(&scale_matrix, glyph_path); lv_vector_path_append_path(span->path, glyph_path); span->font->release_glyph(span->font, &g); lv_matrix_translate(mtx, letter_w, 0); } lv_vector_path_delete(glyph_path); lv_vector_path_get_bounding(span->path, &span->bounds); } _copy_draw_dsc_from_ref(dsc, obj); lv_vector_dsc_add_path(dsc, span->path); _copy_draw_dsc(&(dsc->current_dsc), &(save_dsc.dsc)); } #endif // get bounds functions static void _get_rect_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_rect_t * rect = (lv_svg_render_rect_t *)obj; area->x1 = (int32_t)rect->x; area->y1 = (int32_t)rect->y; area->x2 = (int32_t)(rect->x + rect->width); area->y2 = (int32_t)(rect->y + rect->height); } static void _get_circle_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_circle_t * circle = (lv_svg_render_circle_t *)obj; area->x1 = (int32_t)(circle->cx - circle->r); area->y1 = (int32_t)(circle->cy - circle->r); area->x2 = (int32_t)(circle->cx + circle->r); area->y2 = (int32_t)(circle->cy + circle->r); } static void _get_ellipse_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_ellipse_t * ellipse = (lv_svg_render_ellipse_t *)obj; area->x1 = (int32_t)(ellipse->cx - ellipse->rx); area->y1 = (int32_t)(ellipse->cy - ellipse->ry); area->x2 = (int32_t)(ellipse->cx + ellipse->rx); area->y2 = (int32_t)(ellipse->cy + ellipse->ry); } static void _get_line_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_line_t * line = (lv_svg_render_line_t *)obj; area->x1 = (int32_t)(line->x1); area->y1 = (int32_t)(line->y1); area->x2 = (int32_t)(line->x2); area->y2 = (int32_t)(line->y2); } static void _get_poly_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; lv_area_copy(area, &poly->bounds); } #if LV_USE_FREETYPE static void _get_text_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_text_t * text = (lv_svg_render_text_t *)obj; lv_area_copy(area, &text->bounds); } static void _get_tspan_bounds(const lv_svg_render_obj_t * obj, lv_area_t * area) { lv_svg_render_tspan_t * tspan = (lv_svg_render_tspan_t *)obj; lv_area_copy(area, &tspan->bounds); } #endif // destroy functions static void _destroy_poly(lv_svg_render_obj_t * obj) { lv_svg_render_poly_t * poly = (lv_svg_render_poly_t *)obj; lv_vector_path_delete(poly->path); } static void _destroy_use(lv_svg_render_obj_t * obj) { lv_svg_render_use_t * use = (lv_svg_render_use_t *)obj; if(use->xlink) { lv_free(use->xlink); } } static void _destroy_group(lv_svg_render_obj_t * obj) { lv_svg_render_group_t * group = (lv_svg_render_group_t *)obj; lv_array_deinit(&group->items); } #if LV_USE_FREETYPE static void _destroy_text(lv_svg_render_obj_t * obj) { lv_svg_render_text_t * text = (lv_svg_render_text_t *)obj; if(text->font) { lv_freetype_font_delete(text->font); } if(text->family) { lv_free(text->family); } lv_array_deinit(&text->contents); lv_vector_path_delete(text->path); } static void _destroy_content(lv_svg_render_obj_t * obj) { lv_svg_render_content_t * content = (lv_svg_render_content_t *)obj; if(content->letters) { lv_free(content->letters); } } static void _destroy_tspan(lv_svg_render_obj_t * obj) { lv_svg_render_tspan_t * span = (lv_svg_render_tspan_t *)obj; if(span->font) { lv_freetype_font_delete(span->font); } if(span->family) { lv_free(span->family); } _destroy_content(obj); lv_vector_path_delete(span->path); } #endif static lv_svg_render_obj_t * _lv_svg_render_create(const lv_svg_node_t * node, struct _lv_svg_drawing_builder_state * state) { switch(node->type) { case LV_SVG_TAG_SVG: { lv_svg_render_viewport_t * view = lv_malloc_zeroed(sizeof(lv_svg_render_viewport_t)); LV_ASSERT_MALLOC(view); view->base.init = _init_viewport; view->base.render = _render_viewport; view->base.set_attr = _set_viewport_attr; _set_render_attrs(LV_SVG_RENDER_OBJ(view), node, state); return LV_SVG_RENDER_OBJ(view); } case LV_SVG_TAG_RECT: { lv_svg_render_rect_t * rect = lv_malloc_zeroed(sizeof(lv_svg_render_rect_t)); LV_ASSERT_MALLOC(rect); rect->base.init = _init_obj; rect->base.render = _render_rect; rect->base.set_attr = _set_rect_attr; rect->base.get_bounds = _get_rect_bounds; _set_render_attrs(LV_SVG_RENDER_OBJ(rect), node, state); return LV_SVG_RENDER_OBJ(rect); } case LV_SVG_TAG_CIRCLE: { lv_svg_render_circle_t * circle = lv_malloc_zeroed(sizeof(lv_svg_render_circle_t)); LV_ASSERT_MALLOC(circle); circle->base.init = _init_obj; circle->base.render = _render_circle; circle->base.set_attr = _set_circle_attr; circle->base.get_bounds = _get_circle_bounds; _set_render_attrs(LV_SVG_RENDER_OBJ(circle), node, state); return LV_SVG_RENDER_OBJ(circle); } case LV_SVG_TAG_ELLIPSE: { lv_svg_render_ellipse_t * ellipse = lv_malloc_zeroed(sizeof(lv_svg_render_ellipse_t)); LV_ASSERT_MALLOC(ellipse); ellipse->base.init = _init_obj; ellipse->base.render = _render_ellipse; ellipse->base.set_attr = _set_ellipse_attr; ellipse->base.get_bounds = _get_ellipse_bounds; _set_render_attrs(LV_SVG_RENDER_OBJ(ellipse), node, state); return LV_SVG_RENDER_OBJ(ellipse); } case LV_SVG_TAG_LINE: { lv_svg_render_line_t * line = lv_malloc_zeroed(sizeof(lv_svg_render_line_t)); LV_ASSERT_MALLOC(line); line->base.init = _init_obj; line->base.render = _render_line; line->base.set_attr = _set_line_attr; line->base.get_bounds = _get_line_bounds; _set_render_attrs(LV_SVG_RENDER_OBJ(line), node, state); return LV_SVG_RENDER_OBJ(line); } case LV_SVG_TAG_POLYLINE: { lv_svg_render_poly_t * poly = lv_malloc_zeroed(sizeof(lv_svg_render_poly_t)); LV_ASSERT_MALLOC(poly); poly->base.init = _init_poly; poly->base.render = _render_poly; poly->base.set_attr = _set_polyline_attr; poly->base.get_bounds = _get_poly_bounds; poly->base.destroy = _destroy_poly; _set_render_attrs(LV_SVG_RENDER_OBJ(poly), node, state); return LV_SVG_RENDER_OBJ(poly); } case LV_SVG_TAG_POLYGON: { lv_svg_render_poly_t * poly = lv_malloc_zeroed(sizeof(lv_svg_render_poly_t)); LV_ASSERT_MALLOC(poly); poly->base.init = _init_poly; poly->base.render = _render_poly; poly->base.set_attr = _set_polygen_attr; poly->base.get_bounds = _get_poly_bounds; poly->base.destroy = _destroy_poly; _set_render_attrs(LV_SVG_RENDER_OBJ(poly), node, state); return LV_SVG_RENDER_OBJ(poly); } case LV_SVG_TAG_PATH: { lv_svg_render_poly_t * poly = lv_malloc_zeroed(sizeof(lv_svg_render_poly_t)); LV_ASSERT_MALLOC(poly); poly->base.init = _init_poly; poly->base.render = _render_poly; poly->base.set_attr = _set_path_attr; poly->base.get_bounds = _get_poly_bounds; poly->base.destroy = _destroy_poly; _set_render_attrs(LV_SVG_RENDER_OBJ(poly), node, state); return LV_SVG_RENDER_OBJ(poly); } #if LV_USE_FREETYPE case LV_SVG_TAG_TEXT: { lv_svg_render_text_t * txt = lv_malloc_zeroed(sizeof(lv_svg_render_text_t)); LV_ASSERT_MALLOC(txt); txt->base.init = _init_text; txt->base.set_attr = _set_text_attr; txt->base.render = _render_text; txt->base.get_bounds = _get_text_bounds; txt->base.destroy = _destroy_text; _set_render_attrs(LV_SVG_RENDER_OBJ(txt), node, state); return LV_SVG_RENDER_OBJ(txt); } case LV_SVG_TAG_TSPAN: { lv_svg_render_tspan_t * span = lv_malloc_zeroed(sizeof(lv_svg_render_tspan_t)); LV_ASSERT_MALLOC(span); lv_svg_render_content_t * content = (lv_svg_render_content_t *)span; content->render_content = _render_span; content->base.init = _init_tspan; content->base.set_attr = _set_tspan_attr; content->base.get_bounds = _get_tspan_bounds; content->base.destroy = _destroy_tspan; _set_render_attrs(LV_SVG_RENDER_OBJ(span), node, state); return LV_SVG_RENDER_OBJ(span); } case LV_SVG_TAG_CONTENT: { lv_svg_render_content_t * content = lv_malloc_zeroed(sizeof(lv_svg_render_content_t)); LV_ASSERT_MALLOC(content); content->base.init = _init_content; content->base.destroy = _destroy_content; _set_render_attrs(LV_SVG_RENDER_OBJ(content), node, state); return LV_SVG_RENDER_OBJ(content); } #endif case LV_SVG_TAG_IMAGE: { lv_svg_render_image_t * image = lv_malloc_zeroed(sizeof(lv_svg_render_image_t)); LV_ASSERT_MALLOC(image); image->base.init = _init_image; image->base.render = _render_image; image->base.set_attr = _set_image_attr; _set_render_attrs(LV_SVG_RENDER_OBJ(image), node, state); return LV_SVG_RENDER_OBJ(image); } case LV_SVG_TAG_USE: { lv_svg_render_use_t * use = lv_malloc_zeroed(sizeof(lv_svg_render_use_t)); LV_ASSERT_MALLOC(use); use->base.init = _init_obj; use->base.set_attr = _set_use_attr; use->base.render = _render_use; use->base.destroy = _destroy_use; _set_render_attrs(LV_SVG_RENDER_OBJ(use), node, state); return LV_SVG_RENDER_OBJ(use); } case LV_SVG_TAG_SOLID_COLOR: { lv_svg_render_solid_t * solid = lv_malloc_zeroed(sizeof(lv_svg_render_solid_t)); LV_ASSERT_MALLOC(solid); solid->base.init = _init_obj; solid->base.set_attr = _set_solid_attr; solid->base.set_paint_ref = _set_solid_ref; _set_render_attrs(LV_SVG_RENDER_OBJ(solid), node, state); return LV_SVG_RENDER_OBJ(solid); } case LV_SVG_TAG_RADIAL_GRADIENT: case LV_SVG_TAG_LINEAR_GRADIENT: { lv_svg_render_gradient_t * grad = lv_malloc_zeroed(sizeof(lv_svg_render_gradient_t)); LV_ASSERT_MALLOC(grad); grad->base.init = _init_gradient; grad->base.set_attr = _set_gradient_attr; grad->base.set_paint_ref = _set_gradient_ref; if(node->type == LV_SVG_TAG_LINEAR_GRADIENT) { grad->dsc.style = LV_VECTOR_GRADIENT_STYLE_LINEAR; } else { // radial gradient grad->dsc.style = LV_VECTOR_GRADIENT_STYLE_RADIAL; } _set_render_attrs(LV_SVG_RENDER_OBJ(grad), node, state); return LV_SVG_RENDER_OBJ(grad); } case LV_SVG_TAG_G: { lv_svg_render_group_t * group = lv_malloc_zeroed(sizeof(lv_svg_render_group_t)); LV_ASSERT_MALLOC(group); group->base.init = _init_group; group->base.set_attr = _set_attr; group->base.render = _render_group; group->base.destroy = _destroy_group; _set_render_attrs(LV_SVG_RENDER_OBJ(group), node, state); return LV_SVG_RENDER_OBJ(group); } default: return NULL; } } static bool _lv_svg_doc_walk_cb(const lv_tree_node_t * node, void * data) { struct _lv_svg_drawing_builder_state * state = (struct _lv_svg_drawing_builder_state *)data; lv_svg_render_obj_t * obj = _lv_svg_render_create(LV_SVG_NODE(node), state); if(!obj) { return true; } if(state->in_defs) { obj->flags |= _RENDER_IN_DEFS; } if(state->in_group_deps > 0) { obj->flags |= _RENDER_IN_GROUP; } if(state->list == NULL) { state->list = obj; state->tail = obj; } else { state->tail->next = obj; state->tail = obj; } LV_SVG_NODE(node)->render_obj = obj; return true; } static bool _lv_svg_doc_walk_before_cb(const lv_tree_node_t * node, void * data) { struct _lv_svg_drawing_builder_state * state = (struct _lv_svg_drawing_builder_state *)data; lv_svg_node_t * svg_node = LV_SVG_NODE(node); #if LV_USE_FREETYPE if(svg_node->type == LV_SVG_TAG_TEXT) { state->in_text = true; state->cur_text = svg_node; } #endif if(svg_node->type == LV_SVG_TAG_DEFS) { state->in_defs = true; } if(svg_node->type == LV_SVG_TAG_G) { state->in_group_deps++; } state->draw_dsc = _lv_svg_draw_dsc_push(state->draw_dsc); return true; } static void _lv_svg_doc_walk_after_cb(const lv_tree_node_t * node, void * data) { struct _lv_svg_drawing_builder_state * state = (struct _lv_svg_drawing_builder_state *)data; lv_svg_node_t * svg_node = LV_SVG_NODE(node); if(svg_node->render_obj) { _copy_draw_dsc(&(LV_SVG_NODE(node)->render_obj->dsc), &(state->draw_dsc->dsc)); } #if LV_USE_FREETYPE if(state->in_text) { if(svg_node->type == LV_SVG_TAG_TSPAN || svg_node->type == LV_SVG_TAG_CONTENT) { if(LV_TREE_NODE(svg_node)->parent == LV_TREE_NODE(state->cur_text)) { lv_svg_render_text_t * text = (lv_svg_render_text_t *)state->cur_text->render_obj; if((lv_array_size(&text->contents) + 1) > lv_array_capacity(&text->contents)) { lv_array_resize(&text->contents, text->contents.capacity << 1); } lv_array_push_back(&text->contents, (uint8_t *)(&svg_node->render_obj)); } } } if(svg_node->type == LV_SVG_TAG_TEXT) { state->in_text = false; state->cur_text = NULL; } #endif if(svg_node->type == LV_SVG_TAG_G) { lv_svg_render_group_t * group = (lv_svg_render_group_t *)svg_node->render_obj; uint32_t count = LV_TREE_NODE(node)->child_cnt; for(uint32_t i = 0; i < count; i++) { lv_svg_node_t * child = LV_SVG_NODE_CHILD(node, i); if(child->render_obj) { // not defs lv_array_push_back(&group->items, (uint8_t *)(&child->render_obj)); } } state->in_group_deps--; if(state->in_group_deps == 0) { group->base.flags &= ~_RENDER_IN_GROUP; } } if(svg_node->type == LV_SVG_TAG_DEFS) { state->in_defs = false; } state->draw_dsc = _lv_svg_draw_dsc_pop(state->draw_dsc); } #if LV_USE_FREETYPE static void _freetype_outline_cb(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); lv_freetype_outline_event_param_t * param = lv_event_get_param(e); switch(code) { case LV_EVENT_CREATE: param->outline = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); break; case LV_EVENT_DELETE: lv_vector_path_delete(param->outline); break; case LV_EVENT_INSERT: { if(param->type == LV_FREETYPE_OUTLINE_MOVE_TO) { lv_fpoint_t pt = {0}; pt.x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.x); pt.y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.y); lv_vector_path_move_to(param->outline, &pt); } else if(param->type == LV_FREETYPE_OUTLINE_LINE_TO) { lv_fpoint_t pt = {0}; pt.x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.x); pt.y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.y); lv_vector_path_line_to(param->outline, &pt); } else if(param->type == LV_FREETYPE_OUTLINE_CUBIC_TO) { lv_fpoint_t pt[3] = {0}; pt[0].x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->control1.x); pt[0].y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->control1.y); pt[1].x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->control2.x); pt[1].y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->control2.y); pt[2].x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.x); pt[2].y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.y); lv_vector_path_cubic_to(param->outline, &pt[0], &pt[1], &pt[2]); } else if(param->type == LV_FREETYPE_OUTLINE_CONIC_TO) { lv_fpoint_t pt[2] = {0}; pt[0].x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->control1.x); pt[0].y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->control1.y); pt[1].x = LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.x); pt[1].y = -LV_FREETYPE_F26DOT6_TO_FLOAT(param->to.y); lv_vector_path_quad_to(param->outline, &pt[0], &pt[1]); } else if(param->type == LV_FREETYPE_OUTLINE_END) { lv_vector_path_close(param->outline); } } break; default: LV_LOG_WARN("unknown event code: %d", code); break; } } #endif /********************** * GLOBAL FUNCTIONS **********************/ lv_svg_render_obj_t * lv_svg_render_create(const lv_svg_node_t * svg_doc) { if(!svg_doc) { return NULL; } struct _lv_svg_draw_dsc * dsc = _lv_svg_draw_dsc_create(); struct _lv_svg_drawing_builder_state state = { .doc = svg_doc, .draw_dsc = dsc, .in_group_deps = 0, .in_defs = false, #if LV_USE_FREETYPE .in_text = false, .cur_text = NULL, #endif .list = NULL, .tail = NULL }; lv_tree_walk(LV_TREE_NODE(svg_doc), LV_TREE_WALK_PRE_ORDER, _lv_svg_doc_walk_cb, _lv_svg_doc_walk_before_cb, _lv_svg_doc_walk_after_cb, &state); _lv_svg_draw_dsc_delete(dsc); return state.list; } void lv_svg_render_delete(lv_svg_render_obj_t * list) { while(list) { lv_svg_render_obj_t * obj = list; list = list->next; _deinit_draw_dsc(&(obj->dsc)); if(obj->destroy) { obj->destroy(obj); } if(obj->id) { lv_free(obj->id); } if(obj->fill_ref) { lv_free(obj->fill_ref); } if(obj->stroke_ref) { lv_free(obj->stroke_ref); } lv_free(obj); } } void lv_draw_svg_render(lv_vector_dsc_t * dsc, const lv_svg_render_obj_t * render) { if(!render || !dsc) { return; } const lv_svg_render_obj_t * cur = render; while(cur) { if(cur->render && ((cur->flags & 3) == _RENDER_NORMAL)) { _prepare_render(cur, dsc); cur->render(cur, dsc, NULL); } cur = cur->next; } } void lv_draw_svg(lv_layer_t * layer, const lv_svg_node_t * svg_doc) { if(!svg_doc) { return; } lv_vector_dsc_t * dsc = lv_vector_dsc_create(layer); lv_svg_render_obj_t * list = lv_svg_render_create(svg_doc); lv_draw_svg_render(dsc, list); lv_draw_vector(dsc); lv_svg_render_delete(list); lv_vector_dsc_delete(dsc); } /********************** * STATIC FUNCTIONS **********************/ #endif /*LV_USE_SVG*/