📄 loader_svg_sa.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / Scene Management sub-project * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/scene_manager.h>#include <gpac/constants.h>#include <gpac/utf.h>#include <gpac/xml.h>#include <gpac/events.h>#include <gpac/internal/bifs_dev.h>#include <gpac/internal/scenegraph_dev.h>#include <gpac/nodes_svg_sa.h>#ifndef GPAC_DISABLE_SVG#ifdef GPAC_ENABLE_SVG_SAtypedef struct{ GF_SceneLoader *load; GF_Err last_error; GF_SAXParser *sax_parser; Bool has_root; /* stack of SVG nodes*/ GF_List *node_stack; GF_List *defered_hrefs; GF_List *defered_animations; GF_List *defered_listeners;} GF_SVG_SA_Parser;typedef struct{ /*top of parsed sub-tree*/ SVG_SA_Element *node; /*depth of unknown elements being skipped*/ u32 unknown_depth; /*last child added, used to speed-up parsing*/ GF_ChildNodeItem *last_child;} SVG_SA_NodeStack;typedef struct { /* Stage of the resolving: 0: resolving attributes which depends on the target: from, to, by, values, type 1: resolving begin times 2: resolving end times */ u32 resolve_stage; /* Animation element being defered */ SVG_SA_Element *animation_elt; /* anim parent*/ SVG_SA_Element *anim_parent; /* target animated element*/ SVG_SA_Element *target; /* id of the target element when unresolved*/ char *target_id; /* attributes which cannot be parsed until the type of the target attribute is known */ char *attributeName; char *type; /* only for animateTransform */ char *to; char *from; char *by; char *values;} DeferedAnimation;static GF_Err svg_sa_report(GF_SVG_SA_Parser *parser, GF_Err e, char *format, ...){#ifndef GPAC_DISABLE_LOG if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { char szMsg[2048]; va_list args; va_start(args, format); vsprintf(szMsg, format, args); va_end(args); GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[SVG Parsing] %s (line %d)\n", szMsg, gf_xml_sax_get_line(parser->sax_parser)) ); }#endif if (e) parser->last_error = e; return e;}static void svg_progress(void *cbk, u32 done, u32 total){ gf_set_progress("SVG Parsing", done, total);}static void svg_sa_init_root_element(GF_SVG_SA_Parser *parser, SVG_SA_svgElement *root_svg){ u32 svg_w, svg_h; svg_w = svg_h = 0; if (root_svg->width.type == SVG_NUMBER_VALUE) { svg_w = FIX2INT(root_svg->width.value); svg_h = FIX2INT(root_svg->height.value); } gf_sg_set_scene_size_info(parser->load->scene_graph, svg_w, svg_h, 1); if (parser->load->ctx) { parser->load->ctx->scene_width = svg_w; parser->load->ctx->scene_height = svg_h; } parser->has_root = 1;}static void svg_sa_delete_defered_anim(DeferedAnimation *anim, GF_List *defered_animations){ if (defered_animations) gf_list_del_item(defered_animations, anim); if (anim->target_id) free(anim->target_id); if (anim->attributeName) free(anim->attributeName); if (anim->to) free(anim->to); if (anim->from) free(anim->from); if (anim->by) free(anim->by); if (anim->values) free(anim->values); if (anim->type) free(anim->type); free(anim);}void svg_sa_reset_defered_animations(GF_List *l){ /*delete all unresolved anims*/ while (1) { DeferedAnimation *anim = (DeferedAnimation *)gf_list_last(l); if (!anim) break; svg_sa_delete_defered_anim(anim, l); }}static void svg_post_process_href(GF_SVG_SA_Parser *parser, XMLRI *iri){ /*keep data when encoding*/ if ( !(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; /*unresolved, queue it...*/ if ((iri->type==XMLRI_ELEMENTID) && !iri->target && iri->string) { gf_list_add(parser->defered_hrefs, iri); } if (iri->type != XMLRI_STRING) return; gf_svg_store_embedded_data(iri, parser->load->localPath, parser->load->fileName);}static Bool svg_sa_parse_animation(GF_SVG_SA_Parser *parser, GF_SceneGraph *sg, DeferedAnimation *anim, const char *nodeID){ GF_FieldInfo info; u32 tag; u8 anim_value_type = 0; if (anim->resolve_stage==0) { if (!anim->target) anim->target = (SVG_SA_Element *) gf_sg_find_node_by_name(sg, anim->target_id + 1); if (!anim->target) return 0; /*setup IRI ptr*/ anim->animation_elt->xlink->href.type = XMLRI_ELEMENTID; anim->animation_elt->xlink->href.target = anim->target; gf_svg_register_iri(sg, &anim->animation_elt->xlink->href); tag = gf_node_get_tag((GF_Node *)anim->animation_elt); if (anim->attributeName) { SMIL_AttributeName *attname_value; /* get the type of the target attribute to determine type of the from/to/by ... */ if (gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "attributeName", &info) != GF_OK) return 1; attname_value = (SMIL_AttributeName *)info.far_ptr; if (gf_node_get_field_by_name((GF_Node *)anim->target, anim->attributeName, &info) == GF_OK) { attname_value->type = info.fieldType; attname_value->field_ptr = info.far_ptr; attname_value->name = anim->attributeName; } else { return 1; } anim_value_type = attname_value->type; } else { if (tag == TAG_SVG_SA_animateMotion) { anim_value_type = SVG_Motion_datatype; } else if (tag == TAG_SVG_SA_discard) { anim->resolve_stage = 1; return svg_sa_parse_animation(parser, sg, anim, nodeID); } else { GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Parsing] missing attributeName attribute on %s\n", gf_node_get_name((GF_Node*)anim->animation_elt) )); } } if (anim->type && (tag== TAG_SVG_SA_animateTransform) ) { /* determine the transform_type in case of animateTransform attribute */ gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "type", &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->type, 0); switch(*(SVG_TransformType *) info.far_ptr) { case SVG_TRANSFORM_TRANSLATE: anim_value_type = SVG_Transform_Translate_datatype; break; case SVG_TRANSFORM_SCALE: anim_value_type = SVG_Transform_Scale_datatype; break; case SVG_TRANSFORM_ROTATE: anim_value_type = SVG_Transform_Rotate_datatype; break; case SVG_TRANSFORM_SKEWX: anim_value_type = SVG_Transform_SkewX_datatype; break; case SVG_TRANSFORM_SKEWY: anim_value_type = SVG_Transform_SkewY_datatype; break; case SVG_TRANSFORM_MATRIX: anim_value_type = SVG_Transform_datatype; break; default: GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] unknown datatype for animate transform.\n")); return 0; } } /* Parsing of to / from / by / values */ if (anim->to) { gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "to", &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->to, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)anim->animation_elt->anim->to.value); } if (anim->from) { gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "from", &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->from, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)anim->animation_elt->anim->from.value); } if (anim->by) { gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "by", &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->by, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)anim->animation_elt->anim->by.value); } if (anim->values) { gf_node_get_field_by_name((GF_Node *)anim->animation_elt, "values", &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->values, anim_value_type); if (anim_value_type==XMLRI_datatype) { u32 i, count = gf_list_count(anim->animation_elt->anim->values.values); for (i=0; i<count; i++) { XMLRI *iri = (XMLRI *)gf_list_get(anim->animation_elt->anim->values.values, i); svg_post_process_href(parser, iri); } } } anim->resolve_stage = 1; } if (anim->resolve_stage == 1) { if (gf_svg_resolve_smil_times(sg, anim->anim_parent, anim->animation_elt->timing->begin, 0, nodeID)) { anim->resolve_stage = 2; } else { return 0; } } if (!gf_svg_resolve_smil_times(sg, anim->anim_parent, anim->animation_elt->timing->end, 1, nodeID)) return 0; /*animateMotion needs its children to be parsed !! */ if (!nodeID && gf_node_get_tag((GF_Node *)anim->animation_elt) == TAG_SVG_SA_animateMotion) return 1; /*OK init the anim*/ gf_node_init((GF_Node *)anim->animation_elt); return 1;}static void svg_sa_resolved_refs(GF_SVG_SA_Parser *parser, GF_SceneGraph *sg, const char *nodeID){ u32 count, i; /*check unresolved HREF*/ count = gf_list_count(parser->defered_hrefs); for (i=0; i<count; i++) { GF_Node *targ; XMLRI *iri = (XMLRI *)gf_list_get(parser->defered_hrefs, i); if (nodeID && strcmp(iri->string + 1, nodeID)) continue; targ = gf_sg_find_node_by_name(sg, iri->string + 1); if (targ) { iri->type = XMLRI_ELEMENTID; iri->target = (SVG_SA_Element *) targ; gf_svg_register_iri(sg, iri); free(iri->string); iri->string = NULL; gf_list_rem(parser->defered_hrefs, i); i--; count--; } } /*check unresolved listeners*/ count = gf_list_count(parser->defered_listeners); for (i=0; i<count; i++) { GF_Node *par; SVG_SA_listenerElement *listener = (SVG_SA_listenerElement *)gf_list_get(parser->defered_listeners, i); par = NULL; if (listener->observer.type == XMLRI_ELEMENTID) { if (!listener->observer.target) continue; else par = (GF_Node*)listener->observer.target; } if (listener->target.type == XMLRI_ELEMENTID) { if (!listener->target.target) continue; else { if (!par) par = (GF_Node*)listener->target.target; } } assert(par); gf_dom_listener_add((GF_Node *)par, (GF_Node *) listener); gf_list_rem(parser->defered_listeners, i); i--; count--; } /*check unresolved anims*/ count = gf_list_count(parser->defered_animations); for (i=0; i<count; i++) { DeferedAnimation *anim = (DeferedAnimation *)gf_list_get(parser->defered_animations, i); /*resolve it - we don't check the name since it may be used in SMIL times, which we don't track at anim level*/ if (svg_sa_parse_animation(parser, sg, anim, nodeID)) { svg_sa_delete_defered_anim(anim, parser->defered_animations); i--; count--; } }}static SVG_SA_Element *svg_sa_parse_element(GF_SVG_SA_Parser *parser, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes, SVG_SA_NodeStack *parent){ u32 tag, i; SVG_SA_Element *elt; GF_FieldInfo info; const char *node_name = NULL; DeferedAnimation *anim = NULL; /* Translates the node type (called name) from a String into a unique numeric identifier in GPAC */ tag = gf_svg_sa_node_type_by_class_name(name); if (tag == TAG_UndefinedNode) { svg_sa_report(parser, GF_OK, "Unknown element %s - skipping", name); return NULL; } /* Creates a node in the current scene graph */ elt = (SVG_SA_Element*)gf_node_new(parser->load->scene_graph, tag); if (!elt) { parser->last_error = GF_SG_UNKNOWN_NODE; return NULL; } gf_node_register((GF_Node *)elt, (parent ? (GF_Node *)parent->node : NULL)); if (parent && elt) gf_node_list_add_child_last( & parent->node->children, (GF_Node*)elt, & parent->last_child); if (gf_svg_sa_is_animation_tag(tag)) { GF_SAFEALLOC(anim, DeferedAnimation); /*default anim target is parent node*/ anim->animation_elt = elt; anim->target = parent->node; anim->anim_parent = parent->node; } else if (tag == TAG_SVG_SA_video || tag == TAG_SVG_SA_audio || tag == TAG_SVG_SA_animation || tag == TAG_SVG_SA_conditional) { /* warning: we use the DeferedAnimation structure for some timing nodes which are not animations, but we put the parse stage at 1 (timing) see svg_sa_parse_animation. */ GF_SAFEALLOC(anim, DeferedAnimation); /*default anim target is parent node*/ anim->animation_elt = elt; anim->anim_parent = parent->node; anim->resolve_stage = 1; } /*parse all att*/ for (i=0; i<nb_attributes; i++) { GF_XMLAttribute *att = (GF_XMLAttribute *)&attributes[i]; if (!att->value || !strlen(att->value)) continue; if (!stricmp(att->name, "style")) { /* Special case: style if present will always be first in the list */ gf_svg_parse_style((GF_Node *)elt, att->value);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -