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; }
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; }
void gf_sc_get_nodes_bounds(GF_Node *self, GF_ChildNodeItem *children, GF_TraverseState *tr_state, s32 *child_idx) { u32 i; SFVec2f size; GF_Rect rc; GF_Matrix2D cur_mx; if (tr_state->abort_bounds_traverse) { if (self == tr_state->for_node) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &tr_state->transform); } tr_state->abort_bounds_traverse=0; gf_sc_get_nodes_bounds(self, children, tr_state, child_idx); tr_state->abort_bounds_traverse=1; return; } if (!children) return; size.x = size.y = -FIX_ONE; #ifndef GPAC_DISABLE_VRML switch (gf_node_get_tag(self)) { case TAG_MPEG4_Layer2D: size = ((M_Layer2D *)self)->size; break; case TAG_MPEG4_Layer3D: size = ((M_Layer3D *)self)->size; break; case TAG_MPEG4_Form: size = ((M_Form *)self)->size; break; } #endif if ((size.x>=0) && (size.y>=0)) { tr_state->bounds = gf_rect_center(size.x, size.y); return; } gf_mx2d_copy(cur_mx, tr_state->transform); rc = gf_rect_center(0,0); i = 0; while (children) { if (child_idx && (i != (u32) *child_idx)) { children = children->next; continue; } gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (children->node == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(children->node, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_add_matrix(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); children = children->next; if (child_idx) break; } #ifndef GPAC_DISABLE_SVG if (gf_node_get_tag(self)==TAG_SVG_use) { GF_FieldInfo info; if (gf_node_get_attribute_by_tag(self, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { GF_Node *iri = ((XMLRI*)info.far_ptr)->target; if (iri) { gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (iri == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(iri, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); } } } #endif gf_mx2d_copy(tr_state->transform, cur_mx); if (self != tr_state->for_node) { gf_mx2d_apply_rect(&tr_state->transform, &rc); } tr_state->bounds = rc; }
static void svg_traverse_switch(GF_Node *node, void *rs, Bool is_destroy) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; SVGPropertiesPointers backup_props; u32 backup_flags; s32 *selected_idx = gf_node_get_private(node); u32 styling_size = sizeof(SVGPropertiesPointers); SVGAllAttributes all_atts; GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { gf_free(selected_idx); gf_sc_check_focus_upon_destroy(node); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (gf_node_dirty_get(node)) { u32 pos = 0; GF_ChildNodeItem *child = ((SVG_Element*)node)->children; *selected_idx = -1; while (child) { SVGAllAttributes atts; gf_svg_flatten_attributes((SVG_Element *)child->node, &atts); if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &atts)) { *selected_idx = pos; break; } pos++; child = child->next; } drawable_reset_group_highlight(tr_state, node); gf_node_dirty_clear(node, 0); } 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)) { memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; return; } if (*selected_idx >= 0) { 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, selected_idx); } else if (*selected_idx >= 0) { GF_Node *child = gf_node_list_get_child(((SVG_Element *)node)->children, *selected_idx); gf_node_traverse(child, tr_state); 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; }
static void svg_recompute_viewport_transformation(GF_Node *node, SVGsvgStack *stack, GF_TraverseState *tr_state, SVGAllAttributes *atts) { GF_Matrix2D mx; SVG_ViewBox ext_vb, *vb; SVG_PreserveAspectRatio par; Fixed scale, vp_w, vp_h; Fixed parent_width, parent_height, doc_width, doc_height; /*canvas size negociation has already been done when attaching the scene to the compositor*/ if (atts->width && (atts->width->type==SVG_NUMBER_PERCENTAGE) ) { parent_width = gf_mulfix(tr_state->vp_size.x, atts->width->value/100); doc_width = 0; } else if (!stack->root_svg) { doc_width = parent_width = atts->width ? atts->width->value : 0; } else { parent_width = tr_state->vp_size.x; doc_width = atts->width ? atts->width->value : 0; } if (atts->height && (atts->height->type==SVG_NUMBER_PERCENTAGE) ) { parent_height = gf_mulfix(tr_state->vp_size.y, atts->height->value/100); doc_height = 0; } else if (!stack->root_svg) { doc_height = parent_height = atts->height ? atts->height->value : 0; } else { parent_height = tr_state->vp_size.y; doc_height = atts->height ? atts->height->value : 0; } stack->vp = stack->parent_vp = tr_state->vp_size; vb = atts->viewBox; gf_mx2d_init(mx); if (stack->root_svg) { const char *frag_uri = gf_scene_get_fragment_uri(node); if (frag_uri) { /*SVGView*/ if (!strncmp(frag_uri, "svgView", 7)) { if (!strncmp(frag_uri, "svgView(viewBox(", 16)) { Float x, y, w, h; sscanf(frag_uri, "svgView(viewBox(%f,%f,%f,%f))", &x, &y, &w, &h); ext_vb.x = FLT2FIX(x); ext_vb.y = FLT2FIX(y); ext_vb.width = FLT2FIX(w); ext_vb.height = FLT2FIX(h); ext_vb.is_set = 1; vb = &ext_vb; } else if (!strncmp(frag_uri, "svgView(transform(", 18)) { Bool ret = gf_svg_parse_transformlist(&mx, (char *) frag_uri+18); if (!ret) { GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error parsing SVG View transform component: %s\n", frag_uri+18)); } } } /*fragID*/ else { GF_Node *target = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) frag_uri); if (target) { GF_Matrix2D mx; GF_TraverseState bounds_state; memset(&bounds_state, 0, sizeof(bounds_state)); bounds_state.traversing_mode = TRAVERSE_GET_BOUNDS; bounds_state.visual = tr_state->visual; bounds_state.for_node = target; bounds_state.svg_props = tr_state->svg_props; gf_mx2d_init(bounds_state.transform); gf_mx2d_init(bounds_state.mx_at_node); gf_mx_init(tr_state->visual->compositor->hit_world_to_local); gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, &bounds_state, NULL); gf_mx2d_from_mx(&mx, &tr_state->visual->compositor->hit_world_to_local); gf_mx2d_apply_rect(&mx, &bounds_state.bounds); ext_vb.x = bounds_state.bounds.x; ext_vb.y = bounds_state.bounds.y-bounds_state.bounds.height; ext_vb.width = bounds_state.bounds.width; ext_vb.height = bounds_state.bounds.height; ext_vb.is_set = 1; vb = &ext_vb; } } } } gf_mx2d_init(stack->viewbox_mx); if (!vb) { if (!doc_width || !doc_height) { gf_mx2d_copy(stack->viewbox_mx, mx); return; } /*width/height were specified in the doc, use them to compute a dummy viewbox*/ ext_vb.x = 0; ext_vb.y = 0; ext_vb.width = doc_width; ext_vb.height = doc_height; ext_vb.is_set = 1; vb = &ext_vb; } if ((vb->width<=0) || (vb->height<=0) ) { gf_mx2d_copy(stack->viewbox_mx, mx); return; } stack->vp.x = vb->width; stack->vp.y = vb->height; /*setup default*/ par.defer = 0; par.meetOrSlice = SVG_MEETORSLICE_MEET; par.align = SVG_PRESERVEASPECTRATIO_XMIDYMID; /*use parent (animation, image) viewport settings*/ if (tr_state->parent_anim_atts) { if (tr_state->parent_anim_atts->preserveAspectRatio) { if (tr_state->parent_anim_atts->preserveAspectRatio->defer) { if (atts->preserveAspectRatio) par = *atts->preserveAspectRatio; } else { par = *tr_state->parent_anim_atts->preserveAspectRatio; } } } /*use current viewport settings*/ else if (atts->preserveAspectRatio) { par = *atts->preserveAspectRatio; } if (par.meetOrSlice==SVG_MEETORSLICE_MEET) { if (gf_divfix(parent_width, vb->width) > gf_divfix(parent_height, vb->height)) { scale = gf_divfix(parent_height, vb->height); vp_w = gf_mulfix(vb->width, scale); vp_h = parent_height; } else { scale = gf_divfix(parent_width, vb->width); vp_w = parent_width; vp_h = gf_mulfix(vb->height, scale); } } else { if (gf_divfix(parent_width, vb->width) < gf_divfix(parent_height, vb->height)) { scale = gf_divfix(parent_height, vb->height); vp_w = gf_mulfix(vb->width, scale); vp_h = parent_height; } else { scale = gf_divfix(parent_width, vb->width); vp_w = parent_width; vp_h = gf_mulfix(vb->height, scale); } } if (par.align==SVG_PRESERVEASPECTRATIO_NONE) { stack->viewbox_mx.m[0] = gf_divfix(parent_width, vb->width); stack->viewbox_mx.m[4] = gf_divfix(parent_height, vb->height); stack->viewbox_mx.m[2] = - gf_muldiv(vb->x, parent_width, vb->width); stack->viewbox_mx.m[5] = - gf_muldiv(vb->y, parent_height, vb->height); } else { Fixed dx, dy; stack->viewbox_mx.m[0] = stack->viewbox_mx.m[4] = scale; stack->viewbox_mx.m[2] = - gf_mulfix(vb->x, scale); stack->viewbox_mx.m[5] = - gf_mulfix(vb->y, scale); dx = dy = 0; switch (par.align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: break; case SVG_PRESERVEASPECTRATIO_XMIDYMIN: dx = ( parent_width - vp_w) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMIN: dx = parent_width - vp_w; break; case SVG_PRESERVEASPECTRATIO_XMINYMID: dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMIDYMID: dx = ( parent_width - vp_w) / 2; dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMID: dx = parent_width - vp_w; dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMINYMAX: dy = parent_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMIDYMAX: dx = (parent_width - vp_w) / 2; dy = parent_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMAXYMAX: dx = parent_width - vp_w; dy = parent_height - vp_h; break; } gf_mx2d_add_translation(&stack->viewbox_mx, dx, dy); stack->dx = dx; stack->dy = dy; stack->vpw = vp_w; stack->vph = vp_h; /*we need a clipper*/ if (stack->root_svg && !tr_state->parent_anim_atts && (par.meetOrSlice==SVG_MEETORSLICE_SLICE)) { GF_Rect rc; rc.width = parent_width; rc.height = parent_height; if (!stack->root_svg) { rc.x = 0; rc.y = parent_height; gf_mx2d_apply_rect(&stack->viewbox_mx, &rc); } else { rc.x = dx; rc.y = dy + parent_height; } // tr_state->visual->top_clipper = gf_rect_pixelize(&rc); } } gf_mx2d_add_matrix(&stack->viewbox_mx, &mx); }
static void svg_traverse_g(GF_Node *node, void *rs, Bool is_destroy) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; SVGPropertiesPointers backup_props; u32 backup_flags; u32 styling_size = sizeof(SVGPropertiesPointers); GF_TraverseState *tr_state = (GF_TraverseState *) rs; SVGAllAttributes all_atts; if (is_destroy) { SVGgStack *group = gf_node_get_private(node); #ifdef GF_SR_USE_VIDEO_CACHE group_2d_destroy_svg(node, group); #else if (group->cache) group_cache_del(group->cache); #endif gf_free(group); gf_sc_check_focus_upon_destroy(node); return; } /*group cache traverse routine*/ else if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) { SVGgStack *group = gf_node_get_private(node); group_cache_draw(group->cache, tr_state); 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 if (tr_state->traversing_mode == TRAVERSE_SORT) { #ifdef GF_SR_USE_DEPTH Fixed scale, offset, dscale, doffset; #endif Fixed opacity = FIX_ONE; Bool clear = 0; SVGgStack *group; if (!tr_state->in_svg_filter && all_atts.filter && all_atts.filter->iri.target) { svg_draw_filter(all_atts.filter->iri.target, node, tr_state); return; } group = gf_node_get_private(node); if (tr_state->parent_use_opacity) { opacity = tr_state->parent_use_opacity->value; tr_state->parent_use_opacity = NULL; } if (all_atts.opacity) { opacity = gf_mulfix(opacity, all_atts.opacity->value); } if (gf_node_dirty_get(node)&GF_SG_CHILD_DIRTY) { drawable_reset_group_highlight(tr_state, node); clear=1; } #ifdef GF_SR_USE_DEPTH dscale = FIX_ONE; doffset=0; if (all_atts.gpac_depthGain && all_atts.gpac_depthGain->type==SVG_NUMBER_VALUE) dscale = all_atts.gpac_depthGain->value; if (all_atts.gpac_depthOffset && all_atts.gpac_depthOffset->type==SVG_NUMBER_VALUE) doffset = all_atts.gpac_depthOffset->value; scale = tr_state->depth_gain; offset = tr_state->depth_offset; // new offset is multiplied by parent gain and added to parent offset tr_state->depth_offset = gf_mulfix(doffset, scale) + offset; // gain is multiplied by parent gain tr_state->depth_gain = gf_mulfix(scale, dscale); #endif if (opacity < FIX_ONE) { if (!group->cache) { group->cache = group_cache_new(tr_state->visual->compositor, node); group->cache->force_recompute = 1; } group->cache->opacity = opacity; if (tr_state->visual->compositor->zoom_changed) group->cache->force_recompute = 1; group->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE; #ifdef GF_SR_USE_VIDEO_CACHE group_2d_cache_traverse(node, group, tr_state); #else group_cache_traverse(node, group->cache, tr_state, group->cache->force_recompute, 0, 0); #endif } else { #ifdef GF_SR_USE_VIDEO_CACHE Bool group_cached; group_cached = group_2d_cache_traverse(node, group, tr_state); gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); /*group is not cached, traverse the children*/ if (!group_cached) { GF_ChildNodeItem *child; DrawableContext *first_ctx = tr_state->visual->cur_context; u32 cache_too_small = 0; Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0; u32 traverse_time = gf_sys_clock(); u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue); tr_state->cache_too_small = 0; child = ((GF_ParentNode *)node)->children; while (child) { gf_node_traverse(child->node, tr_state); child = child->next; if (tr_state->cache_too_small) cache_too_small++; } if (cache_too_small) { tr_state->cache_too_small = 1; } else { /*get the traversal time for each group*/ traverse_time = gf_sys_clock() - traverse_time; group->traverse_time += traverse_time; /*record the traversal information and turn cache on if possible*/ group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx); } } #else compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); #endif } if (clear) gf_node_dirty_clear(node, 0); drawable_check_focus_highlight(node, tr_state, NULL); #ifdef GF_SR_USE_DEPTH tr_state->depth_gain = scale; tr_state->depth_offset = offset; #endif } else { compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); } 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; }
static void svg_traverse_svg(GF_Node *node, void *rs, Bool is_destroy) { Bool rootmost_svg, send_resize; u32 viewport_color; SVGsvgStack *stack; GF_Matrix2D backup_matrix, vb_bck; #ifndef GPAC_DISABLE_3D GF_Matrix bck_mx; #endif Bool is_dirty; GF_IRect top_clip; SFVec2f prev_vp; SVGPropertiesPointers backup_props, *prev_props; u32 backup_flags; Bool invalidate_flag; u32 styling_size = sizeof(SVGPropertiesPointers); GF_TraverseState *tr_state = (GF_TraverseState *) rs; SVGAllAttributes all_atts; stack = gf_node_get_private(node); if (is_destroy) { if (stack->svg_props) { gf_svg_properties_reset_pointers(stack->svg_props); gf_free(stack->svg_props); } gf_sc_check_focus_upon_destroy(node); if (stack->vp_fill) drawable_del(stack->vp_fill); gf_free(stack); return; } prev_props = tr_state->svg_props; /*SVG props not set: we are either the root-most <svg> of the compositor or an <svg> inside an <animation>*/ if (!tr_state->svg_props) { tr_state->svg_props = stack->svg_props; if (!tr_state->svg_props) return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) { tr_state->svg_props = prev_props; return; } /*enable or disable navigation*/ tr_state->visual->compositor->navigation_disabled = (all_atts.zoomAndPan && *all_atts.zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0; if (compositor_svg_is_display_off(tr_state->svg_props)) { memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; return; } top_clip = tr_state->visual->top_clipper; gf_mx2d_copy(backup_matrix, tr_state->transform); gf_mx2d_copy(vb_bck, tr_state->vb_transform); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_mx_copy(bck_mx, tr_state->model_matrix); #endif invalidate_flag = tr_state->invalidate_all; is_dirty = gf_node_dirty_get(node); if (is_dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); gf_node_dirty_clear(node, 0); send_resize = 0; if ((stack->parent_vp.x != tr_state->vp_size.x) || (stack->parent_vp.y != tr_state->vp_size.y)) { is_dirty = 1; send_resize = 1; } if (is_dirty || tr_state->visual->compositor->recompute_ar) { svg_recompute_viewport_transformation(node, stack, tr_state, &all_atts); } gf_mx2d_copy(tr_state->vb_transform, stack->viewbox_mx); rootmost_svg = (stack->root_svg && !tr_state->parent_anim_atts) ? 1 : 0; if (tr_state->traversing_mode == TRAVERSE_SORT) { SVG_Paint *vp_fill = NULL; Fixed vp_opacity; if (tr_state->parent_anim_atts) { vp_fill = tr_state->parent_anim_atts->viewport_fill; vp_opacity = tr_state->parent_anim_atts->viewport_fill_opacity ? tr_state->parent_anim_atts->viewport_fill_opacity->value : FIX_ONE; } else { vp_fill = tr_state->svg_props->viewport_fill; vp_opacity = tr_state->svg_props->viewport_fill_opacity ? tr_state->svg_props->viewport_fill_opacity->value : FIX_ONE; } if (vp_fill && (vp_fill->type != SVG_PAINT_NONE) && vp_opacity) { Bool col_dirty = 0; viewport_color = GF_COL_ARGB_FIXED(vp_opacity, vp_fill->color.red, vp_fill->color.green, vp_fill->color.blue); if (stack->prev_color != viewport_color) { stack->prev_color = viewport_color; col_dirty = 1; } if (!rootmost_svg) { DrawableContext *ctx; Fixed width = tr_state->parent_anim_atts->width->value; Fixed height = tr_state->parent_anim_atts->height->value; if (!stack->vp_fill) { stack->vp_fill = drawable_new(); stack->vp_fill->node = node; } if ((width != stack->vp_fill->path->bbox.width) || (height != stack->vp_fill->path->bbox.height)) { drawable_reset_path(stack->vp_fill); gf_path_add_rect(stack->vp_fill->path, 0, 0, width, -height); } ctx = drawable_init_context_svg(stack->vp_fill, tr_state); if (ctx) { ctx->flags &= ~CTX_IS_TRANSPARENT; ctx->aspect.pen_props.width = 0; ctx->aspect.fill_color = viewport_color; ctx->aspect.fill_texture = NULL; if (col_dirty) ctx->flags |= CTX_APP_DIRTY; drawable_finalize_sort(ctx, tr_state, NULL); } } else if (col_dirty) { tr_state->visual->compositor->back_color = viewport_color; /*invalidate the entire visual*/ tr_state->invalidate_all = 1; } } } if (!stack->root_svg && (all_atts.x || all_atts.y)) gf_mx2d_add_translation(&tr_state->vb_transform, all_atts.x->value, all_atts.y->value); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix tmp; visual_3d_matrix_push(tr_state->visual); gf_mx_from_mx2d(&tmp, &tr_state->vb_transform); visual_3d_matrix_add(tr_state->visual, tmp.m); } else { gf_mx_add_matrix_2d(&tr_state->model_matrix, &tr_state->vb_transform); } } else #endif { gf_mx2d_pre_multiply(&tr_state->transform, &tr_state->vb_transform); } /*store VP and move it to current VP (eg, the one used to compute the vb_transform)*/ prev_vp = tr_state->vp_size; tr_state->vp_size = stack->vp; /*the event may trigger scripts which may delete nodes / modify the scene. We therefore send the resize event before traversing the scene*/ if (send_resize) { GF_DOM_Event evt; memset(&evt, 0, sizeof(GF_DOM_Event)); evt.bubbles = 1; evt.type = GF_EVENT_RESIZE; gf_dom_event_fire(node, &evt); } if ((stack->vp.x != prev_vp.x) || (stack->vp.y != prev_vp.y)) { GF_Scene *scene = node->sgprivate->scenegraph->userpriv; if (scene) { GF_DOM_Event evt; memset(&evt, 0, sizeof(GF_DOM_Event)); evt.bubbles = 0; evt.screen_rect.width = stack->vpw; evt.screen_rect.height = stack->vph; evt.screen_rect.x = stack->dx; evt.screen_rect.y = stack->dy; evt.prev_translate.x = stack->vp.x; evt.prev_translate.y = stack->vp.y; evt.type = GF_EVENT_VP_RESIZE; gf_scene_notify_event(scene, 0, NULL, &evt, GF_OK); } } 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); } tr_state->vp_size = prev_vp; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (tr_state->traversing_mode==TRAVERSE_SORT) visual_3d_matrix_pop(tr_state->visual); gf_mx_copy(tr_state->model_matrix, bck_mx); } #endif gf_mx2d_copy(tr_state->transform, backup_matrix); gf_mx2d_copy(tr_state->vb_transform, vb_bck); memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; tr_state->visual->top_clipper = top_clip; if (!stack->root_svg) { tr_state->invalidate_all = invalidate_flag; } tr_state->svg_props = prev_props; }