void compositor_init_svg_font_face_uri(GF_Compositor *compositor, GF_Node *node) { GF_Node *par; GF_Font *font; FontURIStack *stack; GF_Err e; SVGAllAttributes atts; /*check parent is a font-face-src*/ par = gf_node_get_parent(node, 0); if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face_src)) return; /*check parent's parent is a font-face*/ par = gf_node_get_parent(par, 0); if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face)) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (!atts.xlink_href) return; /*get font familly*/ gf_svg_flatten_attributes((SVG_Element*)par, &atts); if (!atts.font_family) return; /*if font with the same name exists, don't load*/ if (gf_compositor_svg_set_font(compositor->font_manager, atts.font_family->value, 0, 1) != NULL) return; /*register font to font manager*/ GF_SAFEALLOC(font, GF_Font); e = gf_font_manager_register_font(compositor->font_manager, font); if (e) { gf_free(font); return; } GF_SAFEALLOC(stack, FontURIStack); stack->font = font; stack->compositor = compositor; font->ft_mgr = compositor->font_manager; font->get_glyphs = svg_font_uri_get_glyphs; font->load_glyph = svg_font_uri_load_glyph; font->get_alias = svg_font_uri_get_alias; font->udta = node; font->name = gf_strdup(atts.font_family->value); gf_node_set_private(node, stack); gf_node_set_callback_function(node, svg_traverse_font_face_uri); font->not_loaded = 1; compositor->fonts_pending++; svg_font_uri_check(node, stack); }
static Bool svg_font_uri_check(GF_Node *node, FontURIStack *st) { GF_Font *font; GF_Node *font_elt; SVGAllAttributes atts; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (!atts.xlink_href) return 0; if (atts.xlink_href->type == XMLRI_ELEMENTID) { if (!atts.xlink_href->target) atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(node), atts.xlink_href->string+1); } else { GF_SceneGraph *ext_sg; char *font_name = strchr(atts.xlink_href->string, '#'); if (!font_name) return 0; if (!st->mo) { st->mo = gf_mo_load_xlink_resource(node, 0, 0, -1); if (!st->mo) return 0; } ext_sg = gf_mo_get_scenegraph(st->mo); if (!ext_sg) return 0; atts.xlink_href->target = gf_sg_find_node_by_name(ext_sg, font_name+1); if (!atts.xlink_href->target) return 0; } font_elt = atts.xlink_href->target; if (gf_node_get_tag(font_elt) != TAG_SVG_font) return 0; font = gf_node_get_private(font_elt); if (!font) return 0; st->alias = font; gf_mo_is_done(st->mo); font->not_loaded = 0; return 1; }
static void svg_animation_smil_update(GF_Node *node, SVGlinkStack *stack, Fixed normalized_scene_time) { if (stack->init_vis_state == 3) { stack->init_vis_state = 4; gf_mo_resume(stack->resource); } else if (stack->needs_play || (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY )) { SVGAllAttributes all_atts; Double clipBegin, clipEnd; GF_MediaObject *new_res; gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); clipBegin = all_atts.clipBegin ? *all_atts.clipBegin : 0; clipEnd = all_atts.clipEnd ? *all_atts.clipEnd : -1; if (stack->needs_play) { gf_mo_play(stack->resource, clipBegin, clipEnd, 0); stack->needs_play = 0; } else { Bool primary = all_atts.gpac_useAsPrimary ? *all_atts.gpac_useAsPrimary : 1; new_res = gf_mo_load_xlink_resource(node, primary, clipBegin, clipEnd); if (new_res != stack->resource) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); if (all_atts.xlink_href) all_atts.xlink_href->target = NULL; stack->resource = new_res; stack->fragment_id = NULL; stack->inline_sg = NULL; } gf_node_dirty_clear(node, 0); } } }
static void svg_compute_text_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state ) { GF_ChildNodeItem *child; Bool is_switch = GF_FALSE; /*compute length of all text blocks*/ switch (gf_node_get_tag(node)) { case TAG_DOMText: get_domtext_width(node, atts, tr_state); break; case TAG_SVG_tspan: get_tspan_width(node, tr_state); break; case TAG_SVG_switch: is_switch = GF_TRUE; case TAG_SVG_a: child = ((GF_ParentNode *)node)->children; while (child) { if (is_switch) { SVGAllAttributes a_atts; gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts); if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) { svg_compute_text_width(child->node, atts, tr_state); break; } } else { svg_compute_text_width(child->node, atts, tr_state); } child = child->next; } break; default: break; } }
static void svg_traverse_defs(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 prev_flags, backup_flags; u32 styling_size = sizeof(SVGPropertiesPointers); GF_TraverseState *tr_state = (GF_TraverseState *) rs; SVGAllAttributes all_atts; if (is_destroy) { gf_sc_check_focus_upon_destroy(node); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; prev_flags = tr_state->switched_off; tr_state->switched_off = 1; compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); tr_state->switched_off = prev_flags; memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; }
static void svg_traverse_text_block(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans) { GF_ChildNodeItem *child; Bool is_switch = GF_FALSE; switch (gf_node_get_tag(node)) { case TAG_DOMText: svg_traverse_domtext(node, atts, tr_state, spans, NULL); break; case TAG_SVG_tspan: /*mark tspan as dirty to force rebuild*/ gf_node_dirty_set(node, 0, GF_FALSE); gf_node_traverse(node, tr_state); break; case TAG_SVG_switch: is_switch = GF_TRUE; case TAG_SVG_a: child = ((GF_ParentNode *)node)->children; while (child) { if (is_switch) { SVGAllAttributes a_atts; gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts); if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) { svg_traverse_text_block(child->node, atts, tr_state, spans); break; } } else if (gf_node_get_tag(child->node)==TAG_DOMText) { svg_traverse_domtext(child->node, atts, tr_state, spans, node); } child = child->next; } break; default: break; } }
static void svg_traverse_tbreak(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes atts; if (is_destroy) return; if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; svg_text_area_reset_state(tr_state); /*beginning of a line, force a break of current fontSize*/ if (!tr_state->text_end_x) { if (tr_state->svg_props->line_increment->type != SVG_NUMBER_AUTO) { tr_state->text_end_y += tr_state->svg_props->line_increment->value; } else { tr_state->text_end_y += tr_state->svg_props->font_size->value; } } tr_state->line_spacing = 0; tr_state->text_end_x = 0; tr_state->last_char_type = 0; memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg_traverse_updates(GF_Node *node, void *rs, Bool is_destroy) { /*video stack is just an extension of image stack, type-casting is OK*/ SVG_updates_stack *stack = (SVG_updates_stack*)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; SVGPropertiesPointers backup_props; u32 backup_flags, dirty_flags; if (is_destroy) { if (stack->resource) { if (stack->is_open) { gf_mo_set_flag(stack->resource, GF_MO_DISPLAY_REMOVE, GF_TRUE); gf_mo_stop(stack->resource); } gf_mo_unregister(node, stack->resource); } gf_free(stack); return; } if (tr_state->traversing_mode!=TRAVERSE_SORT) return; /*flatten attributes and apply animations + inheritance*/ gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) return; dirty_flags = gf_node_dirty_get(node); if (dirty_flags) { stack->clipBegin = all_atts.clipBegin ? *all_atts.clipBegin : 0; stack->clipEnd = all_atts.clipEnd ? *all_atts.clipEnd : -1; if (dirty_flags & GF_SG_SVG_XLINK_HREF_DIRTY) { GF_MediaObject *new_res; MFURL url; Bool lock_timeline=GF_FALSE; url.vals = NULL; url.count = 0; if (all_atts.syncBehavior) lock_timeline = (*all_atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE; gf_term_get_mfurl_from_xlink(node, &url); new_res = gf_mo_register(node, &url, lock_timeline, GF_FALSE); gf_sg_mfurl_del(url); if (stack->resource!=new_res) { if (stack->resource) { gf_mo_stop(stack->resource); gf_mo_unregister(node, stack->resource); } stack->resource = new_res; if (stack->resource && stack->is_open) gf_mo_play(stack->resource, stack->clipBegin, stack->clipEnd, GF_FALSE); } } gf_node_dirty_clear(node, 0); } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void get_tspan_width(GF_Node *node, void *rs) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *tspan = (SVG_Element *)node; SVGAllAttributes atts; GF_ChildNodeItem *child; gf_svg_flatten_attributes(tspan, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: get_domtext_width(child->node, &atts, tr_state); break; case TAG_SVG_tspan: get_tspan_width(child->node, tr_state); break; default: break; } child=child->next; } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg_play_texture(SVG_video_stack *stack, SVGAllAttributes *atts) { SVGAllAttributes all_atts; Bool lock_scene = GF_FALSE; if (stack->txh.is_open) gf_sc_texture_stop(&stack->txh); if (!atts) { gf_svg_flatten_attributes((SVG_Element*)stack->txh.owner, &all_atts); atts = &all_atts; } if (atts->syncBehavior) lock_scene = (*atts->syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE; gf_sc_texture_play_from_to(&stack->txh, &stack->txurl, atts->clipBegin ? (*atts->clipBegin) : 0.0, atts->clipEnd ? (*atts->clipEnd) : -1.0, GF_FALSE, lock_scene); }
static void svg_traverse_a(GF_Node *node, void *rs, Bool is_destroy) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; SVGPropertiesPointers backup_props; u32 styling_size = sizeof(SVGPropertiesPointers); u32 backup_flags; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; if (is_destroy) { gf_sc_check_focus_upon_destroy(node); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; if (compositor_svg_is_display_off(tr_state->svg_props)) { /*u32 prev_flags = tr_state->switched_off; tr_state->switched_off = 1; compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); tr_state->switched_off = prev_flags;*/ memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; return; } compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); } else { compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); if (tr_state->traversing_mode==TRAVERSE_SORT) drawable_check_focus_highlight(node, tr_state, NULL); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; }
GF_MediaObject *gf_mo_load_xlink_resource(GF_Node *node, Bool primary_resource, Double clipBegin, Double clipEnd) { GF_Scene *new_resource; SVGAllAttributes all_atts; XLinkAttributesPointers xlinkp; SMILSyncAttributesPointers syncp; GF_Scene *scene = gf_sg_get_private(gf_node_get_graph(node)); if (!scene) return NULL; gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); xlinkp.actuate = all_atts.xlink_actuate; xlinkp.arcrole = all_atts.xlink_arcrole; xlinkp.href = all_atts.xlink_href; xlinkp.role = all_atts.xlink_role; xlinkp.show = all_atts.xlink_show; xlinkp.title = all_atts.xlink_title; xlinkp.type = all_atts.xlink_type; syncp.syncBehavior = all_atts.syncBehavior; syncp.syncBehaviorDefault = all_atts.syncBehaviorDefault; syncp.syncMaster = all_atts.syncMaster; syncp.syncReference = all_atts.syncReference; syncp.syncTolerance = all_atts.syncTolerance; syncp.syncToleranceDefault = all_atts.syncToleranceDefault; if (!xlinkp.href) return NULL; if (xlinkp.href->type == XMLRI_ELEMENTID) return NULL; // else if (xlinkp.href->string && (xlinkp.href->string[0]=='#')) return NULL; new_resource = gf_svg_get_subscene(node, &xlinkp, &syncp, primary_resource ? 1 : 0, primary_resource); if (!new_resource) return NULL; /*play*/ gf_mo_play(new_resource->root_od->mo, 0, -1, 0); return new_resource->root_od->mo; }
static void svg_traverse_textArea(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix mx3d; GF_Matrix2D backup_matrix; DrawableContext *ctx = NULL; GF_ChildNodeItem *child; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *text = (SVG_Element *)node; SVGAllAttributes atts; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { tr_state->text_parent = node; gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; } child = child->next; } tr_state->text_parent = NULL; return; } gf_svg_flatten_attributes(text, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; tr_state->text_parent = node; tr_state->in_svg_text_area++; if (tr_state->traversing_mode==TRAVERSE_PICK) { if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) { compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) node)->children; while (child) { gf_node_traverse(child->node, tr_state); child = child->next; } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; } tr_state->in_svg_text_area--; tr_state->text_parent = NULL; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) || tr_state->visual->compositor->reset_fonts ) { u32 mode; child = ((GF_ParentNode *) text)->children; svg_reset_text_stack(st); gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); drawable_reset_path(st->drawable); tr_state->max_length = (atts.width ? (atts.width->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.width->value) : FIX_MAX); tr_state->max_height = (atts.height ? (atts.height->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.height->value) : FIX_MAX); tr_state->base_x = (atts.x ? atts.x->value : 0); tr_state->base_y = (atts.y ? atts.y->value : 0); /*init the xml:space algo*/ tr_state->last_char_type = 0; /*let it initialize from first font*/ tr_state->line_spacing = 0; tr_state->text_end_x = 0; tr_state->text_end_y = (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? 0 : tr_state->svg_props->line_increment->value); tr_state->x_anchors = gf_list_new(); if (tr_state->svg_props->font_size && (tr_state->svg_props->font_size->value <= tr_state->max_height)) { Fixed remain; u32 c, refresh_to_idx, prev_refresh; /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; prev_refresh = tr_state->refresh_children_bounds; tr_state->refresh_children_bounds = 0; c = refresh_to_idx = 0; child = ((GF_ParentNode *) text)->children; while (child) { c++; switch (gf_node_get_tag(child->node)) { case TAG_DOMText: svg_traverse_dom_text_area(child->node, &atts, tr_state, st->spans); break; case TAG_SVG_tspan: /*mark tspan as dirty to force rebuild*/ gf_node_dirty_set(child->node, 0, GF_FALSE); gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: case TAG_SVG_a: case TAG_SVG_tbreak: gf_node_traverse(child->node, tr_state); break; default: break; } if (tr_state->refresh_children_bounds) { tr_state->refresh_children_bounds=0; refresh_to_idx=c; } child=child->next; } st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; svg_text_area_reset_state(tr_state); gf_list_del(tr_state->x_anchors); tr_state->x_anchors = NULL; remain = 0; if (tr_state->refresh_children_bounds) { refresh_to_idx = (u32) -1; tr_state->base_shift = 0; } if (tr_state->svg_props->display_align) { switch (*tr_state->svg_props->display_align) { case SVG_DISPLAYALIGN_CENTER: remain = (tr_state->max_height-tr_state->text_end_y) / 2; break; case SVG_DISPLAYALIGN_AFTER: remain = tr_state->max_height - tr_state->text_end_y; break; default: remain = 0; break; } if (remain<0) remain=0; if (remain) { refresh_to_idx = (u32) -1; tr_state->base_shift = remain; svg_text_area_shift_bounds(st, tr_state); } } if (refresh_to_idx) { tr_state->refresh_children_bounds=1; /*and retraverse in case of bounds adjustements*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: break; case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child=child->next; refresh_to_idx--; if (!refresh_to_idx) break; } tr_state->base_shift = 0; } tr_state->traversing_mode = mode; tr_state->refresh_children_bounds = prev_refresh; } svg_update_bounds(st); } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ( (tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: break; case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } tr_state->in_svg_text_area--; tr_state->text_parent = NULL; compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg_traverse_tspan(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix2D backup_matrix; GF_Matrix mx3d; DrawableContext *ctx; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *tspan = (SVG_Element *)node; SVGAllAttributes atts; GF_ChildNodeItem *child; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } return; } if (!tr_state->in_svg_text && !tr_state->in_svg_text_area) return; gf_svg_flatten_attributes(tspan, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; if (tr_state->traversing_mode==TRAVERSE_PICK) { if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) ) { u32 mode; /*tspan has been modified in the SORT stage, which means that an anim local to tspan has modified the node. The result of the parent (text, textArea) will thus be wrong if we try to update the tspan. We therefore keep the previous computed drawable, and invalidate the parent for next frame*/ if (tr_state->traversing_mode==TRAVERSE_SORT) { gf_node_dirty_set(node, 0, GF_TRUE); goto skip_changes; } /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; svg_reset_text_stack(st); child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: svg_traverse_domtext(child->node, &atts, tr_state, st->spans, NULL); break; case TAG_SVG_tspan: gf_node_dirty_set(child->node, 0, GF_FALSE); gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: case TAG_SVG_a: case TAG_SVG_tbreak: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } tr_state->traversing_mode = mode; gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; svg_update_bounds(st); } skip_changes: if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (tr_state->refresh_children_bounds) { if (tr_state->base_shift) svg_text_area_shift_bounds(st, tr_state); else svg_update_bounds(st); child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ( (tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { child = ((GF_ParentNode *) tspan)->children; ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
/* This is a crude draft implementation of filter. The main drawback is that we don't cache any data. We should be able to check for changes in the sub-group or in the filter */ void svg_draw_filter(GF_Node *filter, GF_Node *node, GF_TraverseState *tr_state) { GF_IRect rc1, rc2; #ifndef GPAC_DISABLE_3D u32 type_3d; #endif u32 prev_flags; GF_IRect txrc; Fixed scale_x, scale_y, temp_x, temp_y; DrawableContext *ctx, *child_ctx; GF_SURFACE offscreen_surface, old_surf; GF_Rect bounds, local_bounds, rc; GF_Matrix2D backup; SVGAllAttributes all_atts; GF_FilterStack *st = gf_node_get_private(filter); assert(tr_state->traversing_mode==TRAVERSE_SORT); /*store the current transform matrix, create a new one for group_cache*/ gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); gf_node_allow_cyclic_traverse(node); tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; tr_state->bounds.width = tr_state->bounds.height = 0; gf_node_traverse(node, tr_state); local_bounds = bounds = tr_state->bounds; /*compute bounds in final coordinate system - this ensures that the cache has the correct anti aliasing*/ gf_mx2d_apply_rect(&backup, &bounds); txrc = gf_rect_pixelize(&bounds); if (txrc.width%2) txrc.width++; if (txrc.height%2) txrc.height++; bounds = gf_rect_ft(&txrc); tr_state->traversing_mode = TRAVERSE_SORT; gf_mx2d_copy(tr_state->transform, backup); if (!bounds.width || !bounds.height) { return; } /*create a context */ ctx = drawable_init_context_svg(st->drawable, tr_state); if (!ctx) return; /*setup texture */ st->txh.height = txrc.height; st->txh.width = txrc.width; st->txh.stride = txrc.width * 4; st->txh.pixelformat = GF_PIXEL_ARGB; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) st->txh.pixelformat = GF_PIXEL_RGBA; #endif st->txh.transparent = 1; if (st->txh.stride * st->txh.height > st->alloc_size) { st->alloc_size = st->txh.stride * st->txh.height; st->data = (u8*)gf_realloc(st->data, sizeof(u8) * st->alloc_size); } memset(st->data, 0x0, sizeof(char) * st->txh.stride * st->txh.height); st->txh.data = (char *) st->data; /*setup geometry (rectangle matching the bounds of the object) Warning, we want to center the cached bitmap at the center of the screen (main visual)*/ gf_path_reset(st->drawable->path); gf_path_add_rect_center(st->drawable->path, bounds.x + bounds.width/2, bounds.y - bounds.height/2, bounds.width, bounds.height); old_surf = tr_state->visual->raster_surface; offscreen_surface = tr_state->visual->compositor->rasterizer->surface_new(tr_state->visual->compositor->rasterizer, tr_state->visual->center_coords); tr_state->visual->raster_surface = offscreen_surface; gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); /*attach the buffer to visual*/ tr_state->visual->compositor->rasterizer->surface_attach_to_buffer(offscreen_surface, st->txh.data, st->txh.width, st->txh.height, 0, st->txh.stride, st->txh.pixelformat); prev_flags = tr_state->immediate_draw; tr_state->immediate_draw = 1; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->in_svg_filter = 1; /*recompute the bounds with the final scaling used*/ scale_x = gf_divfix(bounds.width, local_bounds.width); scale_y = gf_divfix(bounds.height, local_bounds.height); gf_mx2d_init(tr_state->transform); gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); rc = local_bounds; gf_mx2d_apply_rect(&tr_state->transform, &rc); /*centered the bitmap on the visual*/ if (tr_state->visual->center_coords) { temp_x = -rc.x - rc.width/2; temp_y = rc.height/2 - rc.y; } else { temp_x = -rc.x; temp_y = rc.height - rc.y; } gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); rc1 = tr_state->visual->surf_rect; rc2 = tr_state->visual->top_clipper; tr_state->visual->surf_rect.width = st->txh.width; tr_state->visual->surf_rect.height = st->txh.height; if (tr_state->visual->center_coords) { tr_state->visual->surf_rect.y = st->txh.height/2; tr_state->visual->surf_rect.x = -1 * (s32) st->txh.width/2; } else { tr_state->visual->surf_rect.y = st->txh.height; tr_state->visual->surf_rect.x = 0; } tr_state->visual->top_clipper = tr_state->visual->surf_rect; #ifndef GPAC_DISABLE_3D type_3d = tr_state->visual->type_3d; tr_state->visual->type_3d=0; #endif if (prev_flags) gf_node_allow_cyclic_traverse(node); gf_node_traverse(node, tr_state); child_ctx = ctx->next; while (child_ctx && child_ctx->drawable) { drawable_reset_bounds(child_ctx->drawable, tr_state->visual); child_ctx->drawable = NULL; child_ctx = child_ctx->next; } tr_state->visual->cur_context = ctx; /*restore state and destroy whatever needs to be cleaned*/ tr_state->in_svg_filter = 0; tr_state->immediate_draw = prev_flags; tr_state->visual->compositor->rasterizer->surface_delete(offscreen_surface); tr_state->visual->raster_surface = old_surf; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->visual->surf_rect = rc1; tr_state->visual->top_clipper = rc2; #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d ; #endif /*update texture*/ st->txh.transparent = 1; st->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; gf_sc_texture_set_data(&st->txh); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_sc_texture_push_image(&st->txh, 0, 0); else #endif gf_sc_texture_push_image(&st->txh, 0, 1); ctx->flags |= CTX_NO_ANTIALIAS; ctx->aspect.fill_color = 0; ctx->aspect.line_color = 0xFF000000; ctx->aspect.fill_texture = &st->txh; ctx->flags |= CTX_TEXTURE_DIRTY; /*get the filter region*/ bounds = local_bounds; gf_svg_flatten_attributes((SVG_Element *)filter, &all_atts); if (!all_atts.filterUnits || (*all_atts.filterUnits==SVG_GRADIENTUNITS_OBJECT)) { Fixed v; v = all_atts.x ? all_atts.x->value : INT2FIX(-10); bounds.x += gf_mulfix(v, bounds.width); v = all_atts.y ? all_atts.y->value : INT2FIX(-10); bounds.y += gf_mulfix(v, bounds.height); v = all_atts.width ? all_atts.width->value : INT2FIX(120); bounds.width = gf_mulfix(v, bounds.width); v = all_atts.height ? all_atts.height->value : INT2FIX(120); bounds.height = gf_mulfix(v, bounds.height); } else { bounds.x = all_atts.x ? all_atts.x->value : 0; bounds.y = all_atts.y ? all_atts.y->value : 0; bounds.width = all_atts.width ? all_atts.x->value : bounds.width; bounds.height = all_atts.x ? all_atts.x->value : 120; } gf_mx2d_apply_rect(&backup, &bounds); svg_filter_apply(filter, &st->txh, &bounds); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!st->drawable->mesh) { st->drawable->mesh = new_mesh(); mesh_from_path(st->drawable->mesh, st->drawable->path); } visual_3d_draw_from_context(tr_state->ctx, tr_state); ctx->drawable = NULL; return; } #endif /*we computed the texture in final screen coordinate, so use the identity matrix for the context*/ gf_mx2d_init(tr_state->transform); drawable_finalize_sort(ctx, tr_state, NULL); gf_mx2d_copy(tr_state->transform, backup); }
GF_EXPORT void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) { s32 interval_index; GF_SceneGraph *sg; SMIL_Timing_RTI *rti; SMILTimingAttributesPointers *timingp = NULL; u32 tag = gf_node_get_tag(timed_elt); if ((tag>=GF_NODE_RANGE_FIRST_SVG) && (tag<=GF_NODE_RANGE_LAST_SVG)) { SVGAllAttributes all_atts; SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt; gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); e->timingp = malloc(sizeof(SMILTimingAttributesPointers)); e->timingp->begin = all_atts.begin; e->timingp->clipBegin = all_atts.clipBegin; e->timingp->clipEnd = all_atts.clipEnd; e->timingp->dur = all_atts.dur; e->timingp->end = all_atts.end; e->timingp->fill = all_atts.smil_fill; e->timingp->max = all_atts.max; e->timingp->min = all_atts.min; e->timingp->repeatCount = all_atts.repeatCount; e->timingp->repeatDur = all_atts.repeatDur; e->timingp->restart = all_atts.restart; timingp = e->timingp; } #ifdef GPAC_ENABLE_SVG_SA else if ((tag>=GF_NODE_RANGE_FIRST_SVG_SA) && (tag<=GF_NODE_RANGE_LAST_SVG_SA)) { SVG_SA_Element *e = (SVG_SA_Element *)timed_elt; e->timingp = malloc(sizeof(SMILTimingAttributesPointers)); e->timingp->begin = &e->timing->begin; e->timingp->clipBegin = &e->timing->clipBegin; e->timingp->clipEnd = &e->timing->clipEnd; e->timingp->dur = &e->timing->dur; e->timingp->end = &e->timing->end; e->timingp->fill = &e->timing->fill; e->timingp->max = &e->timing->max; e->timingp->min = &e->timing->min; e->timingp->repeatCount = &e->timing->repeatCount; e->timingp->repeatDur = &e->timing->repeatDur; e->timingp->restart = &e->timing->restart; timingp = e->timingp; } #endif #ifdef GPAC_ENABLE_SVG_SANI else if ((tag>=GF_NODE_RANGE_FIRST_SVG_SANI) && (tag<=GF_NODE_RANGE_LAST_SVG_SANI)) { SVG_SANI_Element *e = (SVG_SANI_Element *)timed_elt; e->timingp = malloc(sizeof(SMILTimingAttributesPointers)); e->timingp->begin = &e->timing->begin; e->timingp->clipBegin = &e->timing->clipBegin; e->timingp->clipEnd = &e->timing->clipEnd; e->timingp->dur = &e->timing->dur; e->timingp->end = &e->timing->end; e->timingp->fill = &e->timing->fill; e->timingp->max = &e->timing->max; e->timingp->min = &e->timing->min; e->timingp->repeatCount = &e->timing->repeatCount; e->timingp->repeatDur = &e->timing->repeatDur; e->timingp->restart = &e->timing->restart; timingp = e->timingp; } #endif else { return; } if (!timingp) return; GF_SAFEALLOC(rti, SMIL_Timing_RTI) timingp->runtime = rti; rti->timed_elt = timed_elt; rti->timingp = timingp; rti->status = SMIL_STATUS_WAITING_TO_BEGIN; rti->evaluate_status = SMIL_TIMING_EVAL_NONE; rti->intervals = gf_list_new(); rti->current_interval = NULL; rti->evaluate = gf_smil_timing_null_timed_function; rti->scene_time = -1; rti->media_duration = -1; gf_smil_timing_init_interval_list(rti); interval_index = gf_smil_timing_find_interval_index(rti, GF_MAX_DOUBLE); if (interval_index >= 0) { rti->current_interval_index = interval_index; rti->current_interval = (SMIL_Interval*)gf_list_get(rti->intervals, rti->current_interval_index); } sg = timed_elt->sgprivate->scenegraph; while (sg->parent_scene) sg = sg->parent_scene; gf_smil_timing_add_to_sg(sg, rti); }
static void svg_traverse_bitmap(GF_Node *node, void *rs, Bool is_destroy) { Fixed cx, cy, angle; /*video stack is just an extension of image stack, type-casting is OK*/ SVG_video_stack *stack = (SVG_video_stack*)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix2D backup_matrix; GF_Matrix mx_3d; DrawableContext *ctx; SVGAllAttributes all_atts; if (is_destroy) { gf_sc_texture_destroy(&stack->txh); gf_sg_mfurl_del(stack->txurl); drawable_del(stack->graph); if (stack->audio) { gf_node_unregister(stack->audio, NULL); } gf_free(stack); return; } /*TRAVERSE_DRAW is NEVER called in 3D mode*/ if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { SVG_Draw_bitmap(tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_PICK) { svg_drawable_pick(node, stack->graph, tr_state); return; } /*flatten attributes and apply animations + inheritance*/ gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) return; if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { gf_term_get_mfurl_from_xlink(node, &stack->txurl); stack->txh.width = stack->txh.height = 0; /*remove associated audio if any*/ if (stack->audio) { svg_audio_smil_evaluate_ex(NULL, 0, SMIL_TIMING_EVAL_REMOVE, stack->audio, stack->txh.owner); gf_node_unregister(stack->audio, NULL); stack->audio = NULL; } stack->audio_dirty = GF_TRUE; if (stack->txurl.count) svg_play_texture(stack, &all_atts); gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY); } if (gf_node_dirty_get(node)) { /*do not clear dirty state until the image is loaded*/ if (stack->txh.width) { gf_node_dirty_clear(node, 0); SVG_Build_Bitmap_Graph((SVG_video_stack*)gf_node_get_private(node), tr_state); } } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (!compositor_svg_is_display_off(tr_state->svg_props)) { gf_path_get_bounds(stack->graph->path, &tr_state->bounds); compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) { GF_Matrix2D mx; tr_state->bounds.width = INT2FIX(stack->txh.width); tr_state->bounds.height = INT2FIX(stack->txh.height); tr_state->bounds.x = cx - tr_state->bounds.width/2; tr_state->bounds.y = cy + tr_state->bounds.height/2; gf_mx2d_init(mx); gf_mx2d_add_rotation(&mx, 0, 0, angle); gf_mx2d_apply_rect(&mx, &tr_state->bounds); } else { gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } } else if (tr_state->traversing_mode == TRAVERSE_SORT) { if (!compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { GF_Matrix mx_bck; Bool restore_mx = GF_FALSE; compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); ctx = drawable_init_context_svg(stack->graph, tr_state); if (!ctx || !ctx->aspect.fill_texture ) return; if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) { drawable_reset_path(stack->graph); gf_path_add_rect_center(stack->graph->path, cx, cy, INT2FIX(stack->txh.width), INT2FIX(stack->txh.height)); gf_mx2d_copy(mx_bck, tr_state->transform); restore_mx = GF_TRUE; gf_mx2d_init(tr_state->transform); gf_mx2d_add_rotation(&tr_state->transform, cx, cy, angle); } /*even if set this is not true*/ ctx->aspect.pen_props.width = 0; ctx->flags |= CTX_NO_ANTIALIAS; /*if rotation, transparent*/ ctx->flags &= ~CTX_IS_TRANSPARENT; if (ctx->transform.m[1] || ctx->transform.m[3]) { ctx->flags |= CTX_IS_TRANSPARENT; ctx->flags &= ~CTX_NO_ANTIALIAS; } else if (ctx->aspect.fill_texture->transparent) ctx->flags |= CTX_IS_TRANSPARENT; else if (tr_state->svg_props->opacity && (tr_state->svg_props->opacity->type==SVG_NUMBER_VALUE) && (tr_state->svg_props->opacity->value!=FIX_ONE)) { ctx->flags = CTX_IS_TRANSPARENT; ctx->aspect.fill_color = GF_COL_ARGB(FIX2INT(0xFF * tr_state->svg_props->opacity->value), 0, 0, 0); } #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!stack->graph->mesh) { stack->graph->mesh = new_mesh(); mesh_from_path(stack->graph->mesh, stack->graph->path); } compositor_3d_draw_bitmap(stack->graph, &ctx->aspect, tr_state, 0, 0, FIX_ONE, FIX_ONE); ctx->drawable = NULL; } else #endif { drawable_finalize_sort(ctx, tr_state, NULL); } if (restore_mx) gf_mx2d_copy(tr_state->transform, mx_bck); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } } if (stack->audio) svg_traverse_audio_ex(stack->audio, rs, GF_FALSE, tr_state->svg_props); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node) { SVG_handlerElement *handler; GF_Err e; SVGAllAttributes atts; GF_Font *font; GF_Node *node_font = gf_node_get_parent(node, 0); if (!node_font) return; if (gf_node_get_tag(node_font)!=TAG_SVG_font) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (!atts.font_family) return; /*register font to font manager*/ GF_SAFEALLOC(font, GF_Font); e = gf_font_manager_register_font(compositor->font_manager, font); if (e) { gf_free(font); return; } font->ft_mgr = compositor->font_manager; font->get_glyphs = svg_font_get_glyphs; font->load_glyph = svg_font_load_glyph; font->udta = node_font; gf_node_set_private(node_font, font); gf_node_set_callback_function(node_font, svg_traverse_font); font->name = gf_strdup(atts.font_family->value); font->em_size = atts.units_per_em ? FIX2INT( gf_ceil(atts.units_per_em->value) ) : 1000; /*Inconsistency between SVG 1.2 and 1.1 when not specify, ascent and descent are computed based on font.vert-origin-y, WHICH DOES NOT EXIST IN Tiny 1.2 !!! We assume it to be 0. */ font->ascent = atts.ascent ? FIX2INT( gf_ceil(atts.ascent->value) ) : 0; if (!font->ascent) font->ascent = font->em_size; font->descent = atts.descent ? FIX2INT( gf_ceil(atts.descent->value) ) : 0; font->baseline = atts.alphabetic ? FIX2INT( gf_ceil(atts.alphabetic->value) ) : 0; font->line_spacing = font->em_size; font->styles = 0; if (atts.font_style) { switch (*atts.font_style) { case SVG_FONTSTYLE_ITALIC: font->styles |= GF_FONT_ITALIC; break; case SVG_FONTSTYLE_OBLIQUE: font->styles |= GF_FONT_OBLIQUE; break; } } if (atts.font_variant && (*atts.font_variant ==SVG_FONTVARIANT_SMALLCAPS)) font->styles |= GF_FONT_SMALLCAPS; if (atts.font_weight) { switch(*atts.font_weight) { case SVG_FONTWEIGHT_100: font->styles |= GF_FONT_WEIGHT_100; break; case SVG_FONTWEIGHT_LIGHTER: font->styles |= GF_FONT_WEIGHT_LIGHTER; break; case SVG_FONTWEIGHT_200: font->styles |= GF_FONT_WEIGHT_200; break; case SVG_FONTWEIGHT_300: font->styles |= GF_FONT_WEIGHT_300; break; case SVG_FONTWEIGHT_400: font->styles |= GF_FONT_WEIGHT_400; break; case SVG_FONTWEIGHT_NORMAL: font->styles |= GF_FONT_WEIGHT_NORMAL; break; case SVG_FONTWEIGHT_500: font->styles |= GF_FONT_WEIGHT_500; break; case SVG_FONTWEIGHT_600: font->styles |= GF_FONT_WEIGHT_600; break; case SVG_FONTWEIGHT_700: font->styles |= GF_FONT_WEIGHT_700; break; case SVG_FONTWEIGHT_BOLD: font->styles |= GF_FONT_WEIGHT_BOLD; break; case SVG_FONTWEIGHT_800: font->styles |= GF_FONT_WEIGHT_800; break; case SVG_FONTWEIGHT_900: font->styles |= GF_FONT_WEIGHT_900; break; case SVG_FONTWEIGHT_BOLDER: font->styles |= GF_FONT_WEIGHT_BOLDER; break; } } gf_svg_flatten_attributes((SVG_Element*)node_font, &atts); font->max_advance_h = atts.horiz_adv_x ? FIX2INT( gf_ceil(atts.horiz_adv_x->value) ) : 0; font->not_loaded = 1; /*wait for onLoad event before activating the font, otherwise we may not have all the glyphs*/ handler = gf_dom_listener_build(node_font, GF_EVENT_LOAD, 0); handler->handle_event = svg_font_on_load; gf_node_set_private((GF_Node *)handler, compositor); }
static void svg2bifs_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) { u32 i; SVG_SANI_BIFS_Converter *converter = (SVG_SANI_BIFS_Converter *)sax_cbck; SVGPropertiesPointers *backup_props; char *id_string = NULL; u32 tag = gf_svg_get_element_tag(name); SVG_Element *elt = (SVG_Element*)gf_node_new(converter->svg_sg, tag); if (!gf_sg_get_root_node(converter->svg_sg)) { gf_node_register((GF_Node *)elt, NULL); gf_sg_set_root_node(converter->svg_sg, (GF_Node *)elt); } else { gf_node_register((GF_Node *)elt, converter->svg_parent); //gf_node_list_add_child(&((GF_ParentNode*)converter->svg_parent)->children, (GF_Node *)elt); } converter->svg_parent = (GF_Node *)elt; // fprintf(stdout, "Converting %s\n", gf_node_get_class_name((GF_Node *)elt)); // if (converter->bifs_parent) fprintf(stdout, "%s\n", gf_node_get_class_name(converter->bifs_parent)); 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")) { gf_svg_parse_style((GF_Node *)elt, att->value); } else if (!stricmp(att->name, "id") || !stricmp(att->name, "xml:id")) { gf_svg_parse_element_id((GF_Node *)elt, att->value, 0); id_string = att->value; } else { GF_FieldInfo info; if (gf_node_get_field_by_name((GF_Node *)elt, att->name, &info)==GF_OK) { gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); } else { fprintf(stdout, "Skipping attribute %s\n", att->name); } } } memset(&converter->all_atts, 0, sizeof(SVGAllAttributes)); gf_svg_flatten_attributes(elt, &converter->all_atts); backup_props = gf_malloc(sizeof(SVGPropertiesPointers)); memcpy(backup_props, &converter->svg_props, sizeof(SVGPropertiesPointers)); gf_node_set_private((GF_Node *)elt, backup_props); gf_svg_apply_inheritance(&converter->all_atts, &converter->svg_props); if (!gf_sg_get_root_node(converter->bifs_sg)) { if (tag == TAG_SVG_svg) { GF_Node *node, *child; converter->bifs_sg->usePixelMetrics = 1; if (converter->all_atts.width && converter->all_atts.width->type == SVG_NUMBER_VALUE) { converter->bifs_sg->width = FIX2INT(converter->all_atts.width->value); } else { converter->bifs_sg->width = 320; } if (converter->all_atts.height && converter->all_atts.height->type == SVG_NUMBER_VALUE) { converter->bifs_sg->height = FIX2INT(converter->all_atts.height->value); } else { converter->bifs_sg->height = 200; } node = gf_node_new(converter->bifs_sg, TAG_MPEG4_OrderedGroup); gf_node_register(node, NULL); gf_sg_set_root_node(converter->bifs_sg, node); /* SVG to BIFS coordinate transformation */ child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Viewport); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); if (converter->all_atts.viewBox) { M_Viewport *vp = (M_Viewport*)child; vp->size.x = converter->all_atts.viewBox->width; vp->size.y = converter->all_atts.viewBox->height; vp->position.x = converter->all_atts.viewBox->x+converter->all_atts.viewBox->width/2; vp->position.y = -(converter->all_atts.viewBox->y+converter->all_atts.viewBox->height/2); } else { M_Viewport *vp = (M_Viewport*)child; vp->size.x = INT2FIX(converter->bifs_sg->width); vp->size.y = INT2FIX(converter->bifs_sg->height); vp->position.x = INT2FIX(converter->bifs_sg->width)/2; vp->position.y = -INT2FIX(converter->bifs_sg->height)/2; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Background2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Background2D *b = (M_Background2D *)child; b->backColor.red = FIX_ONE; b->backColor.green = FIX_ONE; b->backColor.blue = FIX_ONE; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; { M_Transform2D *tr = (M_Transform2D *)node; tr->scale.y = -FIX_ONE; } converter->bifs_parent = node; } } else { GF_Node *node, *child; node = converter->bifs_parent; switch(tag) { case TAG_SVG_g: { if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; } else { M_Group *g = (M_Group*)gf_node_new(converter->bifs_sg, TAG_MPEG4_Group); gf_node_register((GF_Node *)g, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, (GF_Node *)g); node = (GF_Node *)g; converter->bifs_parent = node; } } break; case TAG_SVG_rect: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->all_atts.x || converter->all_atts.y) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } { M_Transform2D *tr = (M_Transform2D *)node; if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value + (converter->all_atts.width?converter->all_atts.width->value/2:0); if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value + (converter->all_atts.height?converter->all_atts.height->value/2:0); } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_Rectangle); gf_node_register(shape->geometry, (GF_Node *)shape); { M_Rectangle *rect = (M_Rectangle *)shape->geometry; if (converter->all_atts.width) rect->size.x = converter->all_atts.width->value; if (converter->all_atts.height) rect->size.y = converter->all_atts.height->value; } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_path: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->all_atts.x || converter->all_atts.y) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } { M_Transform2D *tr = (M_Transform2D *)node; if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value; if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_XCurve2D); gf_node_register(shape->geometry, (GF_Node *)shape); if (converter->all_atts.d) { M_Coordinate2D *c2d; M_XCurve2D *xc = (M_XCurve2D *)shape->geometry; u32 i, j, c; xc->point = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); c2d = (M_Coordinate2D *)xc->point; gf_node_register(xc->point, (GF_Node *)xc); gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, converter->all_atts.d->n_points); j= 0; for (i = 0; i < converter->all_atts.d->n_points; i++) { if (converter->all_atts.d->tags[i] != GF_PATH_CLOSE || i == converter->all_atts.d->n_points-1) { c2d->point.vals[j] = converter->all_atts.d->points[i]; j++; } } c2d->point.count = j; gf_sg_vrml_mf_alloc(&xc->type, GF_SG_VRML_MFINT32, converter->all_atts.d->n_points); c = 0; j = 0; xc->type.vals[0] = 0; for (i = 1; i < converter->all_atts.d->n_points; i++) { switch(converter->all_atts.d->tags[i]) { case GF_PATH_CURVE_ON: if (c < converter->all_atts.d->n_contours && i-1 == converter->all_atts.d->contours[c]) { xc->type.vals[j] = 0; c++; } else { xc->type.vals[j] = 1; } break; case GF_PATH_CURVE_CUBIC: xc->type.vals[j] = 2; i+=2; break; case GF_PATH_CLOSE: xc->type.vals[j] = 6; break; case GF_PATH_CURVE_CONIC: xc->type.vals[j] = 7; i++; break; } j++; } xc->type.count = j; } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_polyline: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; is_parent_set = 1; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_IndexedFaceSet2D); gf_node_register(shape->geometry, (GF_Node *)shape); if (converter->all_atts.points) { M_Coordinate2D *c2d; M_IndexedFaceSet2D *ifs = (M_IndexedFaceSet2D *)shape->geometry; u32 i; ifs->coord = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); c2d = (M_Coordinate2D *)ifs->coord; gf_node_register(ifs->coord, (GF_Node *)ifs); gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, gf_list_count(*converter->all_atts.points)); for (i = 0; i < gf_list_count(*converter->all_atts.points); i++) { SVG_Point *p = (SVG_Point *)gf_list_get(*converter->all_atts.points, i); c2d->point.vals[i].x = p->x; c2d->point.vals[i].y = p->y; } } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_text: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; is_parent_set = 1; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Transform2D *tr = (M_Transform2D *)child; if (converter->all_atts.text_x) tr->translation.x = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_x, 0))->value; if (converter->all_atts.text_y) tr->translation.y = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_y, 0))->value; tr->scale.y = -FIX_ONE; } node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_FontStyle *fs; M_Text *text; M_Shape *shape = (M_Shape *)node; text = (M_Text *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Text); shape->geometry = (GF_Node *)text; converter->bifs_text_node = shape->geometry; gf_node_register(shape->geometry, (GF_Node *)shape); fs = (M_FontStyle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_XFontStyle); gf_node_register((GF_Node *)fs, (GF_Node*)text); text->fontStyle = (GF_Node *)fs; gf_sg_vrml_mf_alloc(&fs->family, GF_SG_VRML_MFSTRING, 1); fs->family.vals[0] = strdup(converter->svg_props.font_family->value); fs->size = converter->svg_props.font_size->value; shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_ellipse: case TAG_SVG_circle: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->all_atts.cx || converter->all_atts.cy) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Transform2D *tr = (M_Transform2D *)child; if (converter->all_atts.cx) tr->translation.x = converter->all_atts.cx->value; if (converter->all_atts.cy) tr->translation.y = converter->all_atts.cy->value; } node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; if (tag == TAG_SVG_ellipse) { M_Ellipse *e = (M_Ellipse *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Ellipse); shape->geometry = (GF_Node *)e; e->radius.x = converter->all_atts.rx->value; e->radius.y = converter->all_atts.ry->value; } else { M_Circle *c = (M_Circle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Circle); shape->geometry = (GF_Node *)c; c->radius = converter->all_atts.r->value; } gf_node_register(shape->geometry, (GF_Node *)shape); shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_defs: { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Switch); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; { M_Switch *sw = (M_Switch *)node; sw->whichChoice = -1; } converter->bifs_parent = node; } break; case TAG_SVG_solidColor: { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; converter->bifs_parent = node; } break; default: { fprintf(stdout, "Warning: element %s not supported \n", gf_node_get_class_name((GF_Node *)elt)); child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; converter->bifs_parent = node; } break; } if (id_string) gf_node_set_id(converter->bifs_parent, gf_node_get_id((GF_Node *)elt), gf_node_get_name((GF_Node *)elt)); } }
static void SVG_Build_Bitmap_Graph(SVG_video_stack *stack, GF_TraverseState *tr_state) { u32 tag; GF_Rect rc, new_rc; Fixed x, y, width, height, txwidth, txheight; Fixed rectx, recty, rectwidth, rectheight; SVGAllAttributes atts; SVG_PreserveAspectRatio pAR; SVG_Element *e = (SVG_Element *)stack->graph->node; gf_svg_flatten_attributes(e, &atts); tag = gf_node_get_tag(stack->graph->node); switch (tag) { case TAG_SVG_image: case TAG_SVG_video: x = (atts.x ? atts.x->value : 0); y = (atts.y ? atts.y->value : 0); width = (atts.width ? atts.width->value : 0); height = (atts.height ? atts.height->value : 0); break; default: return; } if (!width || !height) return; txheight = INT2FIX(stack->txh.height); txwidth = INT2FIX(stack->txh.width); if (!txwidth || !txheight) return; if (!atts.preserveAspectRatio) { pAR.defer = GF_FALSE; pAR.meetOrSlice = SVG_MEETORSLICE_MEET; pAR.align = SVG_PRESERVEASPECTRATIO_XMIDYMID; } else { pAR = *atts.preserveAspectRatio; } if (pAR.defer) { /* TODO */ rectwidth = width; rectheight = height; rectx = x+rectwidth/2; recty = y+rectheight/2; } else { if (pAR.align==SVG_PRESERVEASPECTRATIO_NONE) { rectwidth = width; rectheight = height; rectx = x+rectwidth/2; recty = y+rectheight/2; } else { Fixed scale, scale_w, scale_h; scale_w = gf_divfix(width, txwidth); scale_h = gf_divfix(height, txheight); if (pAR.meetOrSlice==SVG_MEETORSLICE_MEET) { if (scale_w > scale_h) { scale = scale_h; rectwidth = gf_mulfix(txwidth, scale); rectheight = height; } else { scale = scale_w; rectwidth = width; rectheight = gf_mulfix(txheight, scale); } } else { if (scale_w < scale_h) { scale = scale_h; rectwidth = gf_mulfix(txwidth, scale); rectheight = height; } else { scale = scale_w; rectwidth = width; rectheight = gf_mulfix(txheight, scale); } } rectx = x + rectwidth/2; recty = y + rectheight/2; switch (pAR.align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: break; case SVG_PRESERVEASPECTRATIO_XMIDYMIN: rectx += (width - rectwidth)/ 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMIN: rectx += width - rectwidth; break; case SVG_PRESERVEASPECTRATIO_XMINYMID: recty += (height - rectheight)/ 2; break; case SVG_PRESERVEASPECTRATIO_XMIDYMID: rectx += (width - rectwidth)/ 2; recty += (height - rectheight) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMID: rectx += width - rectwidth; recty += ( txheight - rectheight) / 2; break; case SVG_PRESERVEASPECTRATIO_XMINYMAX: recty += height - rectheight; break; case SVG_PRESERVEASPECTRATIO_XMIDYMAX: rectx += (width - rectwidth)/ 2; recty += height - rectheight; break; case SVG_PRESERVEASPECTRATIO_XMAXYMAX: rectx += width - rectwidth; recty += height - rectheight; break; } } } gf_path_get_bounds(stack->graph->path, &rc); drawable_reset_path(stack->graph); gf_path_add_rect_center(stack->graph->path, rectx, recty, rectwidth, rectheight); gf_path_get_bounds(stack->graph->path, &new_rc); if (!gf_rect_equal(rc, new_rc)) drawable_mark_modified(stack->graph, tr_state); else if (stack->txh.flags & GF_SR_TEXTURE_PRIVATE_MEDIA) drawable_mark_modified(stack->graph, tr_state); gf_node_dirty_clear(stack->graph->node, GF_SG_SVG_GEOMETRY_DIRTY); }
static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGPropertiesPointers *props) { SVGAllAttributes all_atts; SVGPropertiesPointers backup_props; u32 backup_flags, restore; GF_TraverseState *tr_state = (GF_TraverseState*)rs; SVG_audio_stack *stack = (SVG_audio_stack *)gf_node_get_private(node); if (is_destroy) { gf_sc_audio_predestroy(&stack->input); gf_sg_mfurl_del(stack->aurl); gf_free(stack); return; } if (stack->is_active) { gf_sc_audio_register(&stack->input, (GF_TraverseState*)rs); } restore = 0; if (!props) { restore = 1; gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) return; props = tr_state->svg_props; } if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { SVGAllAttributes atts; Bool lock_timeline = GF_FALSE; if (stack->is_active) gf_sc_audio_stop(&stack->input); stack->is_error = GF_FALSE; gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY); gf_term_get_mfurl_from_xlink(node, &(stack->aurl)); gf_svg_flatten_attributes((SVG_Element*) node, &atts); if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE; if (stack->aurl.count && (gf_sc_audio_open(&stack->input, &stack->aurl, atts.clipBegin ? (*atts.clipBegin) : 0.0, atts.clipEnd ? (*atts.clipEnd) : -1.0, lock_timeline) == GF_OK) ) { gf_mo_set_speed(stack->input.stream, FIX_ONE); stack->is_active = GF_TRUE; } else if (stack->is_active) { gf_sc_audio_unregister(&stack->input); stack->is_active = GF_FALSE; } } /*store mute flag*/ stack->input.is_muted = GF_FALSE; if (tr_state->switched_off || compositor_svg_is_display_off(props) || (*(props->visibility) == SVG_VISIBILITY_HIDDEN) ) { stack->input.is_muted = GF_TRUE; } stack->input.intensity = tr_state->svg_props->computed_audio_level; if (restore) { memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; } }
static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status, GF_Node *slave_audio, GF_Node *video) { GF_Node *audio; SVG_audio_stack *stack; audio = slave_audio; if (!audio) audio = gf_smil_get_element(rti); stack = (SVG_audio_stack *)gf_node_get_private(audio); switch (status) { case SMIL_TIMING_EVAL_UPDATE: if (!stack->is_active && !stack->is_error) { if (stack->aurl.count) { SVGAllAttributes atts; Bool lock_timeline = GF_FALSE; gf_svg_flatten_attributes((SVG_Element*) (video ? video : audio), &atts); if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE; if (gf_sc_audio_open(&stack->input, &stack->aurl, atts.clipBegin ? (*atts.clipBegin) : 0.0, atts.clipEnd ? (*atts.clipEnd) : -1.0, lock_timeline) == GF_OK) { gf_mo_set_speed(stack->input.stream, FIX_ONE); stack->is_active = GF_TRUE; } else { stack->is_error = GF_TRUE; } } } else if (!slave_audio && stack->input.stream_finished && (gf_smil_get_media_duration(rti) < 0) ) { Double dur = gf_mo_get_duration(stack->input.stream); if (dur <= 0) { dur = gf_mo_get_last_frame_time(stack->input.stream); dur /= 1000; } gf_smil_set_media_duration(rti, dur); } break; case SMIL_TIMING_EVAL_REPEAT: if (stack->is_active) gf_sc_audio_restart(&stack->input); break; case SMIL_TIMING_EVAL_FREEZE: gf_sc_audio_stop(&stack->input); stack->is_active = GF_FALSE; break; case SMIL_TIMING_EVAL_REMOVE: gf_sc_audio_stop(&stack->input); stack->is_active = GF_FALSE; break; case SMIL_TIMING_EVAL_DEACTIVATE: if (stack->is_active) { gf_sc_audio_stop(&stack->input); gf_sc_audio_unregister(&stack->input); stack->is_active = GF_FALSE; } break; } }
void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node) { u16 utf_name[20]; u8 *utf8; u32 len; GF_Rect rc; GF_Glyph *glyph; GF_Font *font; SVG_GlyphStack *st; SVGAllAttributes atts; GF_Node *node_font = gf_node_get_parent(node, 0); /*locate the font node*/ if (node_font) node_font = gf_node_get_parent(node, 0); if (!node_font || (gf_node_get_tag(node_font)!=TAG_SVG_font) ) return; font = gf_node_get_private(node_font); if (!font) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (gf_node_get_tag(node)==TAG_SVG_missing_glyph) { GF_SAFEALLOC(st, SVG_GlyphStack); goto reg_common; } /*we must have unicode specified*/ if (!atts.unicode) return; GF_SAFEALLOC(st, SVG_GlyphStack); utf8 = (u8 *) *atts.unicode; len = gf_utf8_mbstowcs(utf_name, 200, (const char **) &utf8); /*this is a single glyph*/ if (len==1) { st->glyph.utf_name = utf_name[0]; st->uni_len = 1; } else { st->glyph.utf_name = (u32) (PTR_TO_U_CAST st); st->unicode = gf_malloc(sizeof(u16)*len); st->uni_len = len; memcpy(st->unicode, utf_name, sizeof(u16)*len); } reg_common: st->glyph.ID = (u32) (PTR_TO_U_CAST st); st->font = font; st->glyph.horiz_advance = font->max_advance_h; if (atts.horiz_adv_x) st->glyph.horiz_advance = FIX2INT( gf_ceil(atts.horiz_adv_x->value) ); if (atts.d) { st->glyph.path = atts.d; gf_path_get_bounds(atts.d, &rc); st->glyph.width = FIX2INT( gf_ceil(rc.width) ); st->glyph.height = FIX2INT( gf_ceil(rc.height) ); } st->glyph.vert_advance = st->glyph.height; if (!st->glyph.vert_advance) st->glyph.vert_advance = font->max_advance_v; /*register glyph*/ if (!font->glyph) { font->glyph = &st->glyph; } else { glyph = font->glyph; while (glyph->next) glyph = glyph->next; glyph->next = &st->glyph; } gf_node_set_private(node, st); gf_node_set_callback_function(node, svg_traverse_glyph); }
void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state) { DrawAspect2D asp; GF_Matrix2D inv_2d; Fixed x, y; Bool picked = 0; GF_Compositor *compositor = tr_state->visual->compositor; SVGPropertiesPointers backup_props; GF_Matrix2D backup_matrix; GF_Matrix mx_3d; SVGAllAttributes all_atts; if (!drawable->path) return; gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers)); gf_svg_apply_inheritance(&all_atts, tr_state->svg_props); if (compositor_svg_is_display_off(tr_state->svg_props)) return; compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); memset(&asp, 0, sizeof(DrawAspect2D)); drawable_get_aspect_2d_svg(node, &asp, tr_state); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { svg_drawable_3d_pick(drawable, tr_state, &asp); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); return; } #endif gf_mx2d_copy(inv_2d, tr_state->transform); gf_mx2d_inverse(&inv_2d); x = tr_state->ray.orig.x; y = tr_state->ray.orig.y; gf_mx2d_apply_coords(&inv_2d, &x, &y); picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL); if (picked) { u32 count, i; compositor->hit_local_point.x = x; compositor->hit_local_point.y = y; compositor->hit_local_point.z = 0; gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform); gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d); compositor->hit_node = drawable->node; compositor->hit_use_dom_events = 1; compositor->hit_normal.x = compositor->hit_normal.y = 0; compositor->hit_normal.z = FIX_ONE; compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2; compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2; svg_clone_use_stack(compositor, tr_state); /*not use in SVG patterns*/ compositor->hit_appear = NULL; /*also stack any VRML sensors present at the current level. If the event is not catched by a listener in the SVG tree, the event will be forwarded to the VRML tree*/ gf_list_reset(tr_state->visual->compositor->sensors); count = gf_list_count(tr_state->vrml_sensors); for (i=0; i<count; i++) { gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i)); } GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y))); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); }
static void svg_traverse_text(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix2D backup_matrix; GF_Matrix mx3d; GF_ChildNodeItem *child; DrawableContext *ctx; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *text = (SVG_Element *)node; SVGAllAttributes atts; u32 i,imax; Fixed * lw; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { tr_state->text_parent = node; gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } tr_state->text_parent = NULL; return; } gf_svg_flatten_attributes(text, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; tr_state->in_svg_text++; tr_state->text_parent = node; if (tr_state->traversing_mode==TRAVERSE_PICK) { compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); tr_state->svg_flags = backup_flags; tr_state->text_parent = NULL; tr_state->in_svg_text--; return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { gf_font_spans_get_selection(node, st->spans, tr_state); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; tr_state->text_parent = NULL; tr_state->in_svg_text--; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) || tr_state->visual->compositor->reset_fonts ) { u32 mode; child = ((GF_ParentNode *) text)->children; svg_reset_text_stack(st); tr_state->text_end_x = 0; tr_state->text_end_y = 0; /*init the xml:space algo*/ tr_state->last_char_type = 0; /*initialize x and y counters - stored at the traverse level for handling tspan & co*/ if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); else tr_state->count_x=0; if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); else tr_state->count_y=0; if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); else tr_state->count_rotate=0; /*horizontal justifiers container*/ tr_state->x_anchors = gf_list_new(); /*compute length of all text blocks*/ while (child) { svg_compute_text_width(child->node, &atts, tr_state); child=child->next; } /*apply justification of all blocks*/ imax=gf_list_count(tr_state->x_anchors); for (i=0; i<imax; i++) { lw=gf_list_get(tr_state->x_anchors, i); svg_apply_text_anchor(tr_state, lw); } /*re-initialize x and y counters for final compute*/ if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); else tr_state->count_x=0; if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); else tr_state->count_y=0; if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); else tr_state->count_rotate=0; tr_state->idx_rotate = 0; tr_state->chunk_index = 0; /*initialize current text position*/ if (!tr_state->text_end_x) { SVG_Coordinate *xc = (atts.text_x ? (SVG_Coordinate *) gf_list_get(*atts.text_x, 0) : NULL); tr_state->text_end_x = (xc ? xc->value : 0); } if (!tr_state->text_end_y) { SVG_Coordinate *yc = (atts.text_y ? (SVG_Coordinate *) gf_list_get(*atts.text_y, 0) : NULL); tr_state->text_end_y = (yc ? yc->value : 0); } /*pass x and y to children*/ tr_state->text_x = atts.text_x; tr_state->text_y = atts.text_y; tr_state->text_rotate = atts.text_rotate; drawable_reset_path(st->drawable); /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; tr_state->last_char_type = 0; child = ((GF_ParentNode *) text)->children; while (child) { svg_traverse_text_block(child->node, &atts, tr_state, st->spans); child = child->next; } tr_state->traversing_mode = mode; gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; while (gf_list_count(tr_state->x_anchors)) { Fixed *f = gf_list_last(tr_state->x_anchors); gf_list_rem_last(tr_state->x_anchors); gf_free(f); } gf_list_del(tr_state->x_anchors); tr_state->x_anchors = NULL; svg_update_bounds(st); } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ((tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: gf_node_traverse(child->node, tr_state); break; } child = child->next; } } tr_state->in_svg_text--; tr_state->text_parent = NULL; compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg2bifs_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) { u32 i; SVG2BIFS_Converter *converter = (SVG2BIFS_Converter *)sax_cbck; SVGPropertiesPointers *backup_props; char *id_string = NULL; u32 tag; SVG_Element *elt; SVG_DeferedAnimation *anim = NULL; tag = gf_xml_get_element_tag(name, 0); elt = (SVG_Element*)gf_node_new(converter->svg_sg, tag); if (!gf_sg_get_root_node(converter->svg_sg)) { gf_node_register((GF_Node *)elt, NULL); gf_sg_set_root_node(converter->svg_sg, (GF_Node *)elt); } else { gf_node_register((GF_Node *)elt, converter->svg_parent); //gf_node_list_add_child(&((GF_ParentNode*)converter->svg_parent)->children, (GF_Node *)elt); } // fprintf(stdout, "Converting %s\n", gf_node_get_class_name((GF_Node *)elt)); // if (converter->bifs_parent) fprintf(stdout, "%s\n", gf_node_get_class_name(converter->bifs_parent)); if (gf_svg_is_animation_tag(tag)) { GF_SAFEALLOC(anim, SVG_DeferedAnimation); /*default anim target is parent node*/ anim->animation_elt = elt; if (converter->svg_parent) { anim->target = anim->anim_parent = (SVG_Element*) converter->svg_parent; } } 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")) { gf_svg_parse_style((GF_Node *)elt, att->value); } else if (!stricmp(att->name, "id") || !stricmp(att->name, "xml:id")) { gf_svg_parse_element_id((GF_Node *)elt, att->value, 0); id_string = att->value; } else if (anim && !stricmp(att->name, "to")) { anim->to = gf_strdup(att->value); } else if (anim && !stricmp(att->name, "from")) { anim->from = gf_strdup(att->value); } else if (anim && !stricmp(att->name, "by")) { anim->by = gf_strdup(att->value); } else if (anim && !stricmp(att->name, "values")) { anim->values = gf_strdup(att->value); } else if (anim && (tag == TAG_SVG_animateTransform) && !stricmp(att->name, "type")) { anim->type = gf_strdup(att->value); } else { GF_FieldInfo info; if (gf_node_get_field_by_name((GF_Node *)elt, att->name, &info)==GF_OK) { gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); } else { fprintf(stdout, "Skipping attribute %s\n", att->name); } } } if (anim) { svg_parse_animation(converter->svg_sg, anim); } memset(&converter->all_atts, 0, sizeof(SVGAllAttributes)); gf_svg_flatten_attributes(elt, &converter->all_atts); backup_props = gf_malloc(sizeof(SVGPropertiesPointers)); memcpy(backup_props, &converter->svg_props, sizeof(SVGPropertiesPointers)); gf_node_set_private((GF_Node *)elt, backup_props); gf_svg_apply_inheritance(&converter->all_atts, &converter->svg_props); fprintf(stdout, "START\t%s\t%s\t%s", converter->svg_parent ? gf_node_get_class_name(converter->svg_parent) : "none", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none", name); converter->svg_parent = (GF_Node *)elt; if (!gf_sg_get_root_node(converter->bifs_sg)) { if (tag == TAG_SVG_svg) { GF_Node *node, *child; converter->bifs_sg->usePixelMetrics = 1; if (converter->all_atts.width && converter->all_atts.width->type == SVG_NUMBER_VALUE) { converter->bifs_sg->width = FIX2INT(converter->all_atts.width->value); } else { converter->bifs_sg->width = 320; } if (converter->all_atts.height && converter->all_atts.height->type == SVG_NUMBER_VALUE) { converter->bifs_sg->height = FIX2INT(converter->all_atts.height->value); } else { converter->bifs_sg->height = 200; } node = gf_node_new(converter->bifs_sg, TAG_MPEG4_OrderedGroup); gf_node_register(node, NULL); gf_sg_set_root_node(converter->bifs_sg, node); child = gf_node_new(converter->bifs_sg, TAG_MPEG4_QuantizationParameter); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_QuantizationParameter *qp = (M_QuantizationParameter *)child; qp->useEfficientCoding = 1; } /* SVG to BIFS coordinate transformation */ child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Viewport); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Viewport *vp = (M_Viewport*)child; if (converter->all_atts.viewBox) { vp->size.x = converter->all_atts.viewBox->width; vp->size.y = converter->all_atts.viewBox->height; vp->position.x = converter->all_atts.viewBox->x+converter->all_atts.viewBox->width/2; vp->position.y = -(converter->all_atts.viewBox->y+converter->all_atts.viewBox->height/2); } else { vp->size.x = INT2FIX(converter->bifs_sg->width); vp->size.y = INT2FIX(converter->bifs_sg->height); vp->position.x = INT2FIX(converter->bifs_sg->width)/2; vp->position.y = -INT2FIX(converter->bifs_sg->height)/2; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Background2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Background2D *b = (M_Background2D *)child; b->backColor.red = FIX_ONE; b->backColor.green = FIX_ONE; b->backColor.blue = FIX_ONE; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; { M_Transform2D *tr = (M_Transform2D *)node; tr->scale.y = -FIX_ONE; } converter->bifs_parent = node; } } else { GF_Node *node, *child; node = converter->bifs_parent; switch(tag) { case TAG_SVG_g: { if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; } else { M_Group *g = (M_Group*)gf_node_new(converter->bifs_sg, TAG_MPEG4_Group); gf_node_register((GF_Node *)g, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, (GF_Node *)g); node = (GF_Node *)g; converter->bifs_parent = node; } } break; case TAG_SVG_rect: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->force_transform) { node = add_transform2d(converter, node); if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } if (converter->all_atts.x || converter->all_atts.y) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } { M_Transform2D *tr = (M_Transform2D *)node; if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value + (converter->all_atts.width?converter->all_atts.width->value/2:0); if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value + (converter->all_atts.height?converter->all_atts.height->value/2:0); } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_Rectangle); gf_node_register(shape->geometry, (GF_Node *)shape); { M_Rectangle *rect = (M_Rectangle *)shape->geometry; if (converter->all_atts.width) rect->size.x = converter->all_atts.width->value; if (converter->all_atts.height) rect->size.y = converter->all_atts.height->value; } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_path: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->force_transform) { node = add_transform2d(converter, node); if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } if (converter->all_atts.x || converter->all_atts.y) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } { M_Transform2D *tr = (M_Transform2D *)node; if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value; if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_XCurve2D); gf_node_register(shape->geometry, (GF_Node *)shape); if (converter->all_atts.d) { M_Coordinate2D *c2d; M_XCurve2D *xc = (M_XCurve2D *)shape->geometry; u32 i, j, c, k; xc->point = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); c2d = (M_Coordinate2D *)xc->point; gf_node_register(xc->point, (GF_Node *)xc); gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, converter->all_atts.d->n_points); gf_sg_vrml_mf_alloc(&xc->type, GF_SG_VRML_MFINT32, converter->all_atts.d->n_points); c = 0; k = 0; j = 0; c2d->point.vals[k] = converter->all_atts.d->points[0]; k++; xc->type.vals[0] = 0; for (i = 1; i < converter->all_atts.d->n_points; ) { switch(converter->all_atts.d->tags[i]) { case GF_PATH_CURVE_ON: c2d->point.vals[k] = converter->all_atts.d->points[i]; k++; if (i-1 == converter->all_atts.d->contours[c]) { xc->type.vals[j] = 0; c++; } else { xc->type.vals[j] = 1; } i++; break; case GF_PATH_CURVE_CUBIC: c2d->point.vals[k] = converter->all_atts.d->points[i]; c2d->point.vals[k+1] = converter->all_atts.d->points[i+1]; c2d->point.vals[k+2] = converter->all_atts.d->points[i+2]; k+=3; xc->type.vals[j] = 2; if (converter->all_atts.d->tags[i+2]==GF_PATH_CLOSE) { j++; xc->type.vals[j] = 6; } i+=3; break; case GF_PATH_CLOSE: xc->type.vals[j] = 6; i++; break; case GF_PATH_CURVE_CONIC: c2d->point.vals[k] = converter->all_atts.d->points[i]; c2d->point.vals[k+1] = converter->all_atts.d->points[i+1]; k+=2; xc->type.vals[j] = 7; if (converter->all_atts.d->tags[i+1]==GF_PATH_CLOSE) { j++; xc->type.vals[j] = 6; } i+=2; break; } j++; } xc->type.count = j; c2d->point.count = k; } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_polyline: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->force_transform) { node = add_transform2d(converter, node); if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_IndexedFaceSet2D); gf_node_register(shape->geometry, (GF_Node *)shape); if (converter->all_atts.points) { M_Coordinate2D *c2d; M_IndexedFaceSet2D *ifs = (M_IndexedFaceSet2D *)shape->geometry; u32 i; ifs->coord = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); c2d = (M_Coordinate2D *)ifs->coord; gf_node_register(ifs->coord, (GF_Node *)ifs); gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, gf_list_count(*converter->all_atts.points)); for (i = 0; i < gf_list_count(*converter->all_atts.points); i++) { SVG_Point *p = (SVG_Point *)gf_list_get(*converter->all_atts.points, i); c2d->point.vals[i].x = p->x; c2d->point.vals[i].y = p->y; } } shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_text: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->force_transform) { node = add_transform2d(converter, node); if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Transform2D *tr = (M_Transform2D *)child; if (converter->all_atts.text_x) tr->translation.x = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_x, 0))->value; if (converter->all_atts.text_y) tr->translation.y = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_y, 0))->value; tr->scale.y = -FIX_ONE; } node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_FontStyle *fs; M_Text *text; M_Shape *shape = (M_Shape *)node; text = (M_Text *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Text); shape->geometry = (GF_Node *)text; converter->bifs_text_node = shape->geometry; gf_node_register(shape->geometry, (GF_Node *)shape); fs = (M_FontStyle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_XFontStyle); gf_node_register((GF_Node *)fs, (GF_Node*)text); text->fontStyle = (GF_Node *)fs; gf_sg_vrml_mf_alloc(&fs->family, GF_SG_VRML_MFSTRING, 1); fs->family.vals[0] = gf_strdup(converter->svg_props.font_family->value); fs->size = converter->svg_props.font_size->value; shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_ellipse: case TAG_SVG_circle: { Bool is_parent_set = 0; if (converter->all_atts.transform) { node = add_transform_matrix(converter, node); converter->bifs_parent = node; is_parent_set = 1; } if (converter->force_transform) { node = add_transform2d(converter, node); if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } if (converter->all_atts.cx || converter->all_atts.cy) { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); { M_Transform2D *tr = (M_Transform2D *)child; if (converter->all_atts.cx) tr->translation.x = converter->all_atts.cx->value; if (converter->all_atts.cy) tr->translation.y = converter->all_atts.cy->value; } node = child; child = NULL; if (!is_parent_set) { converter->bifs_parent = node; is_parent_set = 1; } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; if (!is_parent_set) converter->bifs_parent = node; { M_Shape *shape = (M_Shape *)node; if (tag == TAG_SVG_ellipse) { M_Ellipse *e = (M_Ellipse *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Ellipse); shape->geometry = (GF_Node *)e; e->radius.x = converter->all_atts.rx->value; e->radius.y = converter->all_atts.ry->value; } else { M_Circle *c = (M_Circle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Circle); shape->geometry = (GF_Node *)c; c->radius = converter->all_atts.r->value; } gf_node_register(shape->geometry, (GF_Node *)shape); shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); gf_node_register(shape->appearance, (GF_Node *)shape); } } break; case TAG_SVG_defs: { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Switch); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; { M_Switch *sw = (M_Switch *)node; sw->whichChoice = -1; } converter->bifs_parent = node; } break; case TAG_SVG_solidColor: { child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; converter->bifs_parent = node; } break; case TAG_SVG_animateTransform: { GF_Node *child_ts; if (!gf_node_get_id(node)) { gf_node_set_id(node, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); } child_ts = gf_node_new(converter->bifs_sg, TAG_MPEG4_TimeSensor); if (!gf_node_get_id(child_ts)) { gf_node_set_id(child_ts, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); } gf_node_register(child_ts, node); gf_node_list_add_child(&((GF_ParentNode *)node)->children, child_ts); { M_TimeSensor *ts = (M_TimeSensor *)child_ts; if (converter->all_atts.dur) { ts->cycleInterval = converter->all_atts.dur->clock_value; } if (converter->all_atts.repeatCount && converter->all_atts.repeatCount->type == SMIL_REPEATCOUNT_INDEFINITE) { ts->loop = 1; } } if (converter->all_atts.transform_type) { GF_FieldInfo fromField, toField; switch (*converter->all_atts.transform_type) { case SVG_TRANSFORM_ROTATE: child = gf_node_new(converter->bifs_sg, TAG_MPEG4_PositionInterpolator2D); if (!gf_node_get_id(child)) { gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); } gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); gf_node_get_field_by_name(child, "set_fraction", &toField); gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); gf_node_get_field_by_name(child, "value_changed", &fromField); gf_node_get_field_by_name(node, "rotationAngle", &toField); gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); { M_PositionInterpolator2D *pi2d = (M_PositionInterpolator2D *)child; if (converter->all_atts.keyTimes) { SFFloat *g; u32 count, i; count = gf_list_count(*converter->all_atts.keyTimes); for (i = 0; i < count; i++) { Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); gf_sg_vrml_mf_append(&pi2d->key, GF_SG_VRML_MFFLOAT, &g); *g = *f; } } if (converter->all_atts.values) { SFVec2f *g; u32 count, i; count = gf_list_count(converter->all_atts.values->values); for (i = 0; i < count; i++) { SVG_Point_Angle *p; p = gf_list_get(converter->all_atts.values->values, i); gf_sg_vrml_mf_append(&pi2d->keyValue, GF_SG_VRML_MFVEC2F, &g); g->x = p->x; g->y = p->y; } } } child = gf_node_new(converter->bifs_sg, TAG_MPEG4_ScalarInterpolator); if (!gf_node_get_id(child)) { gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); } gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); gf_node_get_field_by_name(child, "set_fraction", &toField); gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); gf_node_get_field_by_name(child, "value_changed", &fromField); gf_node_get_field_by_name(node, "center", &toField); gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); { M_ScalarInterpolator *si = (M_ScalarInterpolator *)child; if (converter->all_atts.keyTimes) { SFFloat *g; u32 count, i; count = gf_list_count(*converter->all_atts.keyTimes); for (i = 0; i < count; i++) { Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); gf_sg_vrml_mf_append(&si->key, GF_SG_VRML_MFFLOAT, &g); *g = *f; } } if (converter->all_atts.values) { SFFloat *g; u32 count, i; count = gf_list_count(converter->all_atts.values->values); for (i = 0; i < count; i++) { SVG_Point_Angle *p; p = gf_list_get(converter->all_atts.values->values, i); gf_sg_vrml_mf_append(&si->keyValue, GF_SG_VRML_MFFLOAT, &g); *g = p->angle; } } } break; case SVG_TRANSFORM_SCALE: case SVG_TRANSFORM_TRANSLATE: child = gf_node_new(converter->bifs_sg, TAG_MPEG4_PositionInterpolator2D); if (!gf_node_get_id(child)) { gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); } gf_node_register(child, node); gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); gf_node_get_field_by_name(child, "set_fraction", &toField); gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); gf_node_get_field_by_name(child, "value_changed", &fromField); if (*converter->all_atts.transform_type == SVG_TRANSFORM_SCALE) gf_node_get_field_by_name(node, "scale", &toField); else gf_node_get_field_by_name(node, "translation", &toField); gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); { M_PositionInterpolator2D *pi2d = (M_PositionInterpolator2D *)child; if (converter->all_atts.keyTimes) { SFFloat *g; u32 count, i; count = gf_list_count(*converter->all_atts.keyTimes); for (i = 0; i < count; i++) { Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); gf_sg_vrml_mf_append(&pi2d->key, GF_SG_VRML_MFFLOAT, &g); *g = *f; } } if (converter->all_atts.values) { SFVec2f *g; u32 count, i; count = gf_list_count(converter->all_atts.values->values); for (i = 0; i < count; i++) { SVG_Point *p; p = gf_list_get(converter->all_atts.values->values, i); gf_sg_vrml_mf_append(&pi2d->keyValue, GF_SG_VRML_MFVEC2F, &g); g->x = p->x; g->y = p->y; } } } break; default: fprintf(stdout, "Warning: transformation type not supported \n"); } } //converter->bifs_parent = node; } break; default: { fprintf(stdout, "Warning: element %s not supported \n", gf_node_get_class_name((GF_Node *)elt)); child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); gf_node_register(child, node); //gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); node = child; child = NULL; converter->bifs_parent = node; } break; } if (id_string) gf_node_set_id(converter->bifs_parent, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL);//gf_node_get_name((GF_Node *)elt)); } fprintf(stdout, "\t%s\n", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none"); }
static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy, void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *), Bool is_svg_rect, Bool is_svg_path) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; DrawableContext *ctx; SVGPropertiesPointers backup_props; u32 backup_flags; Drawable *drawable = (Drawable *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; if (is_destroy) { #if USE_GF_PATH /* The path is the same as the one in the SVG node, don't delete it here */ if (is_svg_path) drawable->path = NULL; #endif drawable_node_del(node); return; } assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D); if (tr_state->traversing_mode==TRAVERSE_PICK) { svg_drawable_pick(node, drawable, tr_state); return; } /*flatten attributes and apply animations + inheritance*/ gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) return; /* Recreates the path (i.e the shape) only if the node is dirty */ if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) { /*the rebuild function is responsible for cleaning the path*/ rebuild_path(node, drawable, &all_atts); gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY); drawable_mark_modified(drawable, tr_state); } if (drawable->path) { if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) { if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) { drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO; drawable_mark_modified(drawable, tr_state); } } else { if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) { drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO; drawable_mark_modified(drawable, tr_state); } } } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (! compositor_svg_is_display_off(tr_state->svg_props)) { DrawAspect2D asp; gf_path_get_bounds(drawable->path, &tr_state->bounds); if (!tr_state->ignore_strike) { memset(&asp, 0, sizeof(DrawAspect2D)); drawable_get_aspect_2d_svg(node, &asp, tr_state); if (asp.pen_props.width) { StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL); if (si && si->outline) { gf_path_get_bounds(si->outline, &tr_state->bounds); } } } compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL); if (!tr_state->abort_bounds_traverse) gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL); } } else if (tr_state->traversing_mode == TRAVERSE_SORT) { /*reset our flags - this may break reuse of nodes and change-detection in dirty-rect algo */ gf_node_dirty_clear(node, 0); if (!compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); ctx = drawable_init_context_svg(drawable, tr_state); if (ctx) { if (is_svg_rect) { if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {} else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {} else if (ctx->transform.m[1] || ctx->transform.m[3]) {} else { ctx->flags &= ~CTX_IS_TRANSPARENT; if (!ctx->aspect.pen_props.width) ctx->flags |= CTX_NO_ANTIALIAS; } } if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE) ctx->aspect.pen_props.path_length = all_atts.pathLength->value; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!drawable->mesh) { drawable->mesh = new_mesh(); if (drawable->path) mesh_from_path(drawable->mesh, drawable->path); } visual_3d_draw_from_context(ctx, tr_state); ctx->drawable = NULL; } else #endif { drawable_finalize_sort(ctx, tr_state, NULL); } } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool is_foreign_object) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; GF_Matrix2D translate; SVGPropertiesPointers backup_props; u32 backup_flags, dirty; Bool is_fragment; GF_Node *used_node; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; SVGlinkStack *stack = gf_node_get_private(node); SFVec2f prev_vp; SVG_Number *prev_opacity; if (is_destroy) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); gf_free(stack); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!all_atts.xlink_href) return; if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; dirty = gf_node_dirty_get(node); if (dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); if (dirty & GF_SG_SVG_XLINK_HREF_DIRTY) { stack->fragment_id = NULL; stack->inline_sg = NULL; if (all_atts.xlink_href->string && (all_atts.xlink_href->string[0]=='#')) { stack->fragment_id = all_atts.xlink_href->string; stack->inline_sg = gf_node_get_graph(node); } else { GF_MediaObject *new_res = gf_mo_load_xlink_resource(node, is_foreign_object, 0, -1); if (new_res != stack->resource) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); stack->resource = new_res; } } } gf_node_dirty_clear(node, 0); /*locate the used node - this is done at each step to handle progressive loading*/ is_fragment = 0; used_node = NULL; if (!stack->inline_sg && !stack->fragment_id && all_atts.xlink_href) { if (all_atts.xlink_href->type == XMLRI_ELEMENTID) { used_node = all_atts.xlink_href->target; is_fragment = 1; } else if (stack->resource) { stack->inline_sg = gf_mo_get_scenegraph(stack->resource); if (!is_foreign_object) { stack->fragment_id = strchr(all_atts.xlink_href->string, '#'); } } } if (!used_node && stack->inline_sg) { if (stack->fragment_id) { used_node = gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1); is_fragment = 1; } else if (is_foreign_object) { used_node = gf_sg_get_root_node(stack->inline_sg); } } if (!used_node) goto end; /*stack use nodes for picking*/ gf_list_add(tr_state->use_stack, used_node); gf_list_add(tr_state->use_stack, node); gf_mx2d_init(translate); translate.m[2] = (all_atts.x ? all_atts.x->value : 0); translate.m[5] = (all_atts.y ? all_atts.y->value : 0); /*update VP size (SVG 1.1)*/ prev_vp = tr_state->vp_size; if (all_atts.width && all_atts.height) { tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width); tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height); } prev_opacity = tr_state->parent_use_opacity; tr_state->parent_use_opacity = all_atts.opacity; if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); if (!compositor_svg_is_display_off(tr_state->svg_props)) { gf_node_traverse(used_node, tr_state); gf_mx2d_apply_rect(&translate, &tr_state->bounds); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } /*SORT mode and visible, traverse*/ else if (!compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate); if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix tmp; gf_mx_from_mx2d(&tmp, &translate); visual_3d_matrix_add(tr_state->visual, tmp.m); } } else #endif gf_mx2d_pre_multiply(&tr_state->transform, &translate); drawable_check_focus_highlight(node, tr_state, NULL); if (is_fragment) { gf_node_traverse(used_node, tr_state); } else { gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } gf_list_rem_last(tr_state->use_stack); gf_list_rem_last(tr_state->use_stack); tr_state->vp_size = prev_vp; tr_state->parent_use_opacity = prev_opacity; end: memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
/* Attributes from the timed elements are not easy to use during runtime, the runtime info is a set of easy to use structures. This function initializes them (intervals, status ...) and registers the element with the scenegraph */ GF_EXPORT void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) { GF_SceneGraph *sg; SMIL_Timing_RTI *rti; SMILTimingAttributesPointers *timingp = NULL; u32 tag = gf_node_get_tag(timed_elt); SVGAllAttributes all_atts; SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt; gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); e->timingp = gf_malloc(sizeof(SMILTimingAttributesPointers)); e->timingp->begin = all_atts.begin; e->timingp->clipBegin = all_atts.clipBegin; e->timingp->clipEnd = all_atts.clipEnd; e->timingp->dur = all_atts.dur; e->timingp->end = all_atts.end; e->timingp->fill = all_atts.smil_fill; e->timingp->max = all_atts.max; e->timingp->min = all_atts.min; e->timingp->repeatCount = all_atts.repeatCount; e->timingp->repeatDur = all_atts.repeatDur; e->timingp->restart = all_atts.restart; timingp = e->timingp; if (!timingp) return; if (tag == TAG_SVG_audio || tag == TAG_SVG_video) { /* if the dur attribute is not set, then it should be set to media as this is the default for media elements see http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-DurValueSemantics "For simple media elements that specify continuous media (i.e. media with an inherent notion of time), the implicit duration is the intrinsic duration of the media itself - e.g. video and audio files have a defined duration." TODO: Check if this should work with the animation element */ if (!e->timingp->dur) { GF_FieldInfo info; gf_node_get_attribute_by_tag((GF_Node *)e, TAG_SVG_ATT_dur, 1, 0, &info); e->timingp->dur = (SMIL_Duration *)info.far_ptr; e->timingp->dur->type = SMIL_DURATION_MEDIA; } } GF_SAFEALLOC(rti, SMIL_Timing_RTI) timingp->runtime = rti; rti->timed_elt = timed_elt; GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Initialization\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); rti->timingp = timingp; rti->status = SMIL_STATUS_WAITING_TO_BEGIN; rti->evaluate_status = SMIL_TIMING_EVAL_NONE; rti->evaluate = gf_smil_timing_null_timed_function; rti->scene_time = -1; rti->force_reevaluation = 0; rti->media_duration = -1; GF_SAFEALLOC(rti->current_interval, SMIL_Interval); gf_smil_timing_get_first_interval(rti); GF_SAFEALLOC(rti->next_interval, SMIL_Interval); gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->current_interval->begin); /* Now that the runtime info for this timed element is initialized, we can tell the scene graph that it can start notifying the scene time to this element. Because of the 'animation' element, we can have many scene graphs sharing the same scene time, we therefore add this timed element to the rootmost scene graph. */ sg = timed_elt->sgprivate->scenegraph; while (sg->parent_scene) sg = sg->parent_scene; gf_smil_timing_add_to_sg(sg, rti); }
/*translate string to glyph sequence*/ static GF_Err svg_font_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl) { u32 prev_c; u32 len; u32 i, gl_idx; u16 *utf_res; GF_Node *node = (GF_Node *)udta; GF_ChildNodeItem *child; char *utf8 = (char*) utf_string; /*FIXME - use glyphs unicode attributes for glyph substitution*/ len = utf_string ? strlen(utf_string) : 0; if (!len) { *io_glyph_buffer_size = 0; return GF_OK; } if (*io_glyph_buffer_size < len+1) { *io_glyph_buffer_size = len+1; return GF_BUFFER_TOO_SMALL; } len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char**)&utf8); if ((s32) len < 0) return GF_IO_ERR; /*should not happen*/ if (utf8) return GF_IO_ERR; /*perform bidi relayout*/ utf_res = (u16 *) glyph_buffer; *is_rtl = gf_utf8_reorder_bidi(utf_res, len); /*move 16bit buffer to 32bit*/ for (i=len; i>0; i--) { glyph_buffer[i-1] = utf_res[i-1]; } gl_idx = 0; prev_c = 0; for (i=0;i<len; i++) { SVG_GlyphStack *missing_glyph = NULL; SVG_GlyphStack *st = NULL; child = ((GF_ParentNode *) node)->children; while (child) { u32 tag = gf_node_get_tag(child->node); if (tag==TAG_SVG_missing_glyph) { missing_glyph = gf_node_get_private(child->node); } else if (tag ==TAG_SVG_glyph) { Bool glyph_ok = 0; SVGAllAttributes atts; st = gf_node_get_private(child->node); if (!st) { child = child->next; continue; } if (st->glyph.utf_name==glyph_buffer[i]) { u32 j, count; gf_svg_flatten_attributes((SVG_Element*)child->node, &atts); if (!lang) { glyph_ok = 1; } else { if (!atts.lang) { glyph_ok = 1; } else { count = gf_list_count(*atts.lang); for (j=0; j<count; j++) { char *name = gf_list_get(*atts.lang, j); if (!stricmp(name, lang) || strstr(lang, name)) { glyph_ok = 1; break; } } } } if (atts.arabic_form) { Bool first = (!prev_c || (prev_c==' ')) ? 1 : 0; Bool last = ((i+1==len) || (glyph_buffer[i+1]==' ') ) ? 1 : 0; if (!strcmp(*atts.arabic_form, "isolated")) { if (!first || !last) glyph_ok = 0; } if (!strcmp(*atts.arabic_form, "initial")) { if (!first) glyph_ok = 0; } if (!strcmp(*atts.arabic_form, "medial")) { if (first || last) glyph_ok = 0; } if (!strcmp(*atts.arabic_form, "terminal")) { if (!last) glyph_ok = 0; } } if (glyph_ok) break; } /*perform glyph substitution*/ else if (st->uni_len>1) { u32 j; for (j=0; j<st->uni_len; j++) { if (i+j>=len) break; if (glyph_buffer[i+j] != st->unicode[j]) break; } if (j==st->uni_len) break; } st = NULL; } child = child->next; } prev_c = glyph_buffer[i]; if (!st) st = missing_glyph; glyph_buffer[gl_idx] = st ? st->glyph.ID : 0; if (st && st->uni_len>1) i++; gl_idx++; } *io_glyph_buffer_size = len = gl_idx; return GF_OK; }