LCOV - code coverage report
Current view: top level - compositor - svg_grouping.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 447 638 70.1 %
Date: 2021-04-29 23:48:07 Functions: 20 23 87.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Cyril Concolato - Jean le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2005-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include "visual_manager.h"
      27             : 
      28             : #ifndef GPAC_DISABLE_SVG
      29             : #include "nodes_stacks.h"
      30             : #include "offscreen_cache.h"
      31             : 
      32             : #include <gpac/internal/scenegraph_dev.h>
      33             : #include <gpac/mediaobject.h>
      34             : 
      35             : #include <gpac/nodes_svg.h>
      36             : #include <gpac/compositor.h>
      37             : 
      38             : /*for svg <g> caching*/
      39             : #include "mpeg4_grouping.h"
      40             : 
      41             : 
      42             : typedef struct
      43             : {
      44             :         Bool root_svg;
      45             :         SVGPropertiesPointers *svg_props;
      46             :         GF_Matrix2D viewbox_mx;
      47             :         Drawable *vp_fill;
      48             :         u32 prev_color;
      49             :         /*parent VP size used to compute the vp->ViewBox matrix*/
      50             :         SFVec2f parent_vp;
      51             :         /*current VP size used by all children*/
      52             :         SFVec2f vp;
      53             :         Fixed dx, dy, vpw, vph;
      54             : } SVGsvgStack;
      55             : 
      56             : 
      57         943 : static void svg_recompute_viewport_transformation(GF_Node *node, SVGsvgStack *stack, GF_TraverseState *tr_state, SVGAllAttributes *atts)
      58             : {
      59             :         GF_Matrix2D mx;
      60             :         SVG_ViewBox ext_vb, *vb;
      61             :         SVG_PreserveAspectRatio par;
      62             :         Fixed scale, vp_w, vp_h;
      63             :         Fixed parent_width, parent_height, doc_width, doc_height;
      64             : 
      65             :         /*canvas size negociation has already been done when attaching the scene to the compositor*/
      66         943 :         if (atts->width && (atts->width->type==SVG_NUMBER_PERCENTAGE) ) {
      67         323 :                 parent_width = gf_mulfix(tr_state->vp_size.x, atts->width->value/100);
      68         323 :                 doc_width = 0;
      69         620 :         } else if (!stack->root_svg) {
      70           0 :                 doc_width = parent_width = atts->width ? atts->width->value : 0;
      71             :         } else {
      72         620 :                 parent_width = tr_state->vp_size.x;
      73         620 :                 doc_width = atts->width ? atts->width->value : 0;
      74             :         }
      75             : 
      76         943 :         if (atts->height && (atts->height->type==SVG_NUMBER_PERCENTAGE) ) {
      77         323 :                 parent_height = gf_mulfix(tr_state->vp_size.y, atts->height->value/100);
      78         323 :                 doc_height = 0;
      79         620 :         } else if (!stack->root_svg) {
      80           0 :                 doc_height = parent_height = atts->height ? atts->height->value : 0;
      81             :         } else {
      82         620 :                 parent_height = tr_state->vp_size.y;
      83         620 :                 doc_height = atts->height ? atts->height->value : 0;
      84             :         }
      85             : 
      86         943 :         stack->vp = stack->parent_vp = tr_state->vp_size;
      87             : 
      88         943 :         vb = atts->viewBox;
      89             : 
      90         943 :         gf_mx2d_init(mx);
      91             : 
      92         943 :         if (stack->root_svg && !tr_state->parent_is_use) {
      93         943 :                 const char *frag_uri = gf_scene_get_fragment_uri(node);
      94         943 :                 if (frag_uri) {
      95             :                         /*SVGView*/
      96           0 :                         if (!strncmp(frag_uri, "svgView", 7)) {
      97           0 :                                 if (!strncmp(frag_uri, "svgView(viewBox(", 16)) {
      98             :                                         Float x, y, w, h;
      99           0 :                                         sscanf(frag_uri, "svgView(viewBox(%f,%f,%f,%f))", &x, &y, &w, &h);
     100           0 :                                         ext_vb.x = FLT2FIX(x);
     101           0 :                                         ext_vb.y = FLT2FIX(y);
     102           0 :                                         ext_vb.width = FLT2FIX(w);
     103           0 :                                         ext_vb.height = FLT2FIX(h);
     104           0 :                                         ext_vb.is_set = 1;
     105             :                                         vb = &ext_vb;
     106             :                                 }
     107           0 :                                 else if (!strncmp(frag_uri, "svgView(transform(", 18)) {
     108           0 :                                         Bool ret = gf_svg_parse_transformlist(&mx, (char *) frag_uri+18);
     109           0 :                                         if (!ret) {
     110           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error parsing SVG View transform component: %s\n", frag_uri+18));
     111             :                                         }
     112             :                                 }
     113             :                         }
     114             :                         /*fragID*/
     115             :                         else {
     116           0 :                                 GF_Node *target = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) frag_uri);
     117           0 :                                 if (target) {
     118             :                                         GF_Matrix2D vp_mx;
     119             :                                         GF_TraverseState bounds_state;
     120             :                                         memset(&bounds_state, 0, sizeof(bounds_state));
     121           0 :                                         bounds_state.traversing_mode = TRAVERSE_GET_BOUNDS;
     122           0 :                                         bounds_state.visual = tr_state->visual;
     123           0 :                                         bounds_state.for_node = target;
     124           0 :                                         bounds_state.svg_props = tr_state->svg_props;
     125           0 :                                         gf_mx2d_init(bounds_state.transform);
     126           0 :                                         gf_mx2d_init(bounds_state.mx_at_node);
     127           0 :                                         gf_mx_init(tr_state->visual->compositor->hit_world_to_local);
     128           0 :                                         gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, &bounds_state, NULL);
     129           0 :                                         gf_mx2d_from_mx(&vp_mx, &tr_state->visual->compositor->hit_world_to_local);
     130           0 :                                         gf_mx2d_apply_rect(&vp_mx, &bounds_state.bounds);
     131           0 :                                         ext_vb.x = bounds_state.bounds.x;
     132           0 :                                         ext_vb.y = bounds_state.bounds.y-bounds_state.bounds.height;
     133           0 :                                         ext_vb.width = bounds_state.bounds.width;
     134           0 :                                         ext_vb.height = bounds_state.bounds.height;
     135           0 :                                         ext_vb.is_set = 1;
     136             :                                         vb = &ext_vb;
     137             :                                 }
     138             :                         }
     139             :                 }
     140             :         }
     141        1886 :         gf_mx2d_init(stack->viewbox_mx);
     142             : 
     143         943 :         if (!vb) {
     144           1 :                 if (!doc_width || !doc_height) {
     145             :                         gf_mx2d_copy(stack->viewbox_mx, mx);
     146           1 :                         return;
     147             :                 }
     148             :                 /*width/height were specified in the doc, use them to compute a dummy viewbox*/
     149           0 :                 ext_vb.x = 0;
     150           0 :                 ext_vb.y = 0;
     151           0 :                 ext_vb.width = doc_width;
     152           0 :                 ext_vb.height = doc_height;
     153           0 :                 ext_vb.is_set = 1;
     154             :                 vb = &ext_vb;
     155             :         }
     156         942 :         if ((vb->width<=0) || (vb->height<=0) ) {
     157             :                 gf_mx2d_copy(stack->viewbox_mx, mx);
     158             :                 return;
     159             :         }
     160         942 :         stack->vp.x = vb->width;
     161         942 :         stack->vp.y = vb->height;
     162             : 
     163             :         /*setup default*/
     164             :         par.defer = 0;
     165             :         par.meetOrSlice = SVG_MEETORSLICE_MEET;
     166             :         par.align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
     167             : 
     168             :         /*use parent (animation, image) viewport settings*/
     169         942 :         if (tr_state->parent_anim_atts) {
     170         413 :                 if (tr_state->parent_anim_atts->preserveAspectRatio) {
     171           0 :                         if (tr_state->parent_anim_atts->preserveAspectRatio->defer) {
     172           0 :                                 if (atts->preserveAspectRatio)
     173           0 :                                         par = *atts->preserveAspectRatio;
     174             :                         } else {
     175           0 :                                 par = *tr_state->parent_anim_atts->preserveAspectRatio;
     176             :                         }
     177             :                 }
     178             :         }
     179             :         /*use current viewport settings*/
     180         529 :         else if (atts->preserveAspectRatio) {
     181         150 :                 par = *atts->preserveAspectRatio;
     182             :         }
     183             : 
     184         942 :         if (par.meetOrSlice==SVG_MEETORSLICE_MEET) {
     185         942 :                 if (gf_divfix(parent_width, vb->width) > gf_divfix(parent_height, vb->height)) {
     186             :                         scale = gf_divfix(parent_height, vb->height);
     187         157 :                         vp_w = gf_mulfix(vb->width, scale);
     188             :                         vp_h = parent_height;
     189             :                 } else {
     190             :                         scale = gf_divfix(parent_width, vb->width);
     191             :                         vp_w = parent_width;
     192         785 :                         vp_h = gf_mulfix(vb->height, scale);
     193             :                 }
     194             :         } else {
     195           0 :                 if (gf_divfix(parent_width, vb->width) < gf_divfix(parent_height, vb->height)) {
     196             :                         scale = gf_divfix(parent_height, vb->height);
     197           0 :                         vp_w = gf_mulfix(vb->width, scale);
     198             :                         vp_h = parent_height;
     199             :                 } else {
     200             :                         scale = gf_divfix(parent_width, vb->width);
     201             :                         vp_w = parent_width;
     202           0 :                         vp_h = gf_mulfix(vb->height, scale);
     203             :                 }
     204             :         }
     205             : 
     206         942 :         if (par.align==SVG_PRESERVEASPECTRATIO_NONE) {
     207         150 :                 stack->viewbox_mx.m[0] = gf_divfix(parent_width, vb->width);
     208         150 :                 stack->viewbox_mx.m[4] = gf_divfix(parent_height, vb->height);
     209         150 :                 stack->viewbox_mx.m[2] = - gf_muldiv(vb->x, parent_width, vb->width);
     210         150 :                 stack->viewbox_mx.m[5] = - gf_muldiv(vb->y, parent_height, vb->height);
     211             :         } else {
     212             :                 Fixed dx, dy;
     213         792 :                 stack->viewbox_mx.m[0] = stack->viewbox_mx.m[4] = scale;
     214         792 :                 stack->viewbox_mx.m[2] = - gf_mulfix(vb->x, scale);
     215         792 :                 stack->viewbox_mx.m[5] = - gf_mulfix(vb->y, scale);
     216             : 
     217             :                 dx = dy = 0;
     218         792 :                 switch (par.align) {
     219             :                 case SVG_PRESERVEASPECTRATIO_XMINYMIN:
     220             :                         break;
     221           0 :                 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
     222           0 :                         dx = ( parent_width - vp_w) / 2;
     223           0 :                         break;
     224           0 :                 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
     225           0 :                         dx = parent_width - vp_w;
     226           0 :                         break;
     227           0 :                 case SVG_PRESERVEASPECTRATIO_XMINYMID:
     228           0 :                         dy = ( parent_height - vp_h) / 2;
     229           0 :                         break;
     230         792 :                 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
     231         792 :                         dx = ( parent_width  - vp_w) / 2;
     232         792 :                         dy = ( parent_height - vp_h) / 2;
     233         792 :                         break;
     234           0 :                 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
     235           0 :                         dx = parent_width  - vp_w;
     236           0 :                         dy = ( parent_height - vp_h) / 2;
     237           0 :                         break;
     238           0 :                 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
     239           0 :                         dy = parent_height - vp_h;
     240           0 :                         break;
     241           0 :                 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
     242           0 :                         dx = (parent_width - vp_w) / 2;
     243           0 :                         dy = parent_height - vp_h;
     244           0 :                         break;
     245           0 :                 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
     246           0 :                         dx = parent_width  - vp_w;
     247           0 :                         dy = parent_height - vp_h;
     248           0 :                         break;
     249             :                 }
     250         792 :                 gf_mx2d_add_translation(&stack->viewbox_mx, dx, dy);
     251         792 :                 stack->dx = dx;
     252         792 :                 stack->dy = dy;
     253         792 :                 stack->vpw = vp_w;
     254         792 :                 stack->vph = vp_h;
     255             : 
     256             : #if 0
     257             :                 /*we need a clipper*/
     258             :                 if (stack->root_svg && !tr_state->parent_anim_atts && (par.meetOrSlice==SVG_MEETORSLICE_SLICE)) {
     259             :                         GF_Rect rc;
     260             :                         rc.width = parent_width;
     261             :                         rc.height = parent_height;
     262             :                         rc.x = dx;
     263             :                         rc.y = dy + parent_height;
     264             :                         tr_state->visual->top_clipper = gf_rect_pixelize(&rc);
     265             :                 }
     266             : #endif
     267             : 
     268             :         }
     269         942 :         gf_mx2d_add_matrix(&stack->viewbox_mx, &mx);
     270             : }
     271             : 
     272        1940 : static void svg_traverse_svg(GF_Node *node, void *rs, Bool is_destroy)
     273             : {
     274             :         Bool rootmost_svg, send_resize;
     275             :         u32 viewport_color;
     276             :         SVGsvgStack *stack;
     277             :         GF_Matrix2D backup_matrix, vb_bck;
     278             : #ifndef GPAC_DISABLE_3D
     279             :         GF_Matrix bck_mx;
     280             : #endif
     281             :         Bool is_dirty;
     282             :         GF_IRect top_clip;
     283             :         SFVec2f prev_vp;
     284             :         SVGPropertiesPointers backup_props, *prev_props;
     285             :         u32 backup_flags;
     286             :         Bool invalidate_flag;
     287             :         u32 styling_size = sizeof(SVGPropertiesPointers);
     288             :         GF_TraverseState *tr_state = (GF_TraverseState *) rs;
     289             :         SVGAllAttributes all_atts;
     290        1940 :         stack = gf_node_get_private(node);
     291             : 
     292        1940 :         if (is_destroy) {
     293          51 :                 if (stack->svg_props) {
     294          51 :                         gf_svg_properties_reset_pointers(stack->svg_props);
     295          51 :                         gf_free(stack->svg_props);
     296             :                 }
     297          51 :                 gf_sc_check_focus_upon_destroy(node);
     298          51 :                 if (stack->vp_fill) drawable_del(stack->vp_fill);
     299          51 :                 gf_free(stack);
     300          51 :                 return;
     301             :         }
     302             : 
     303        1889 :         prev_props = tr_state->svg_props;
     304             :         /*SVG props not set: we are either the root-most <svg> of the compositor
     305             :         or an <svg> inside an <animation>*/
     306        1889 :         if (!tr_state->svg_props) {
     307        1889 :                 tr_state->svg_props = stack->svg_props;
     308        1889 :                 if (!tr_state->svg_props) return;
     309             :         }
     310             : 
     311        1889 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     312        1889 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) {
     313           0 :                 tr_state->svg_props = prev_props;
     314           0 :                 return;
     315             :         }
     316             : 
     317             :         /*enable or disable navigation*/
     318        1889 :         tr_state->visual->compositor->navigation_disabled = (all_atts.zoomAndPan && *all_atts.zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0;
     319             : 
     320        1889 :         if (compositor_svg_is_display_off(tr_state->svg_props)) {
     321           0 :                 memcpy(tr_state->svg_props, &backup_props, styling_size);
     322           0 :                 tr_state->svg_flags = backup_flags;
     323           0 :                 return;
     324             :         }
     325             : 
     326        1889 :         top_clip = tr_state->visual->top_clipper;
     327        1889 :         gf_mx2d_copy(backup_matrix, tr_state->transform);
     328        1889 :         gf_mx2d_copy(vb_bck, tr_state->vb_transform);
     329             : 
     330             : #ifndef GPAC_DISABLE_3D
     331             :         //commented to get rid of GCC warning
     332             :         //if (tr_state->visual->type_3d)
     333        1889 :         gf_mx_copy(bck_mx, tr_state->model_matrix);
     334             : 
     335             : #endif
     336             : 
     337        1889 :         invalidate_flag = tr_state->invalidate_all;
     338             : 
     339        1889 :         is_dirty = gf_node_dirty_get(node);
     340        1889 :         if (is_dirty  & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node);
     341        1889 :         gf_node_dirty_clear(node, 0);
     342             : 
     343             :         send_resize = 0;
     344        1889 :         if ((stack->parent_vp.x != tr_state->vp_size.x) || (stack->parent_vp.y != tr_state->vp_size.y)) {
     345             :                 is_dirty = 1;
     346             :                 send_resize = 1;
     347             :         }
     348             : 
     349        1838 :         if (is_dirty || tr_state->visual->compositor->recompute_ar) {
     350         943 :                 svg_recompute_viewport_transformation(node, stack, tr_state, &all_atts);
     351             :         }
     352             : 
     353        1889 :         gf_mx2d_copy(tr_state->vb_transform, stack->viewbox_mx);
     354             : 
     355        1889 :         rootmost_svg = (stack->root_svg && !tr_state->parent_anim_atts) ? 1 : 0;
     356        1889 :         if (tr_state->traversing_mode == TRAVERSE_SORT) {
     357             :                 SVG_Paint *vp_fill = NULL;
     358             :                 Fixed vp_opacity;
     359             : 
     360        1866 :                 if (tr_state->parent_anim_atts) {
     361         826 :                         vp_fill = tr_state->parent_anim_atts->viewport_fill;
     362         826 :                         vp_opacity = tr_state->parent_anim_atts->viewport_fill_opacity ? tr_state->parent_anim_atts->viewport_fill_opacity->value : FIX_ONE;
     363             :                 } else {
     364        1040 :                         vp_fill = tr_state->svg_props->viewport_fill;
     365        1040 :                         vp_opacity = tr_state->svg_props->viewport_fill_opacity ? tr_state->svg_props->viewport_fill_opacity->value : FIX_ONE;
     366             :                 }
     367        1866 :                 if (tr_state->visual->compositor->noback) {
     368             :                         vp_fill = NULL;
     369             :                         vp_opacity = 0;
     370             :                 }
     371             : 
     372        1865 :                 if (vp_fill && (vp_fill->type != SVG_PAINT_NONE) && vp_opacity) {
     373             :                         Bool col_dirty = 0;
     374         151 :                         viewport_color = GF_COL_ARGB_FIXED(vp_opacity, vp_fill->color.red, vp_fill->color.green, vp_fill->color.blue);
     375             : 
     376         151 :                         if (stack->prev_color != viewport_color) {
     377           2 :                                 stack->prev_color = viewport_color;
     378             :                                 col_dirty = 1;
     379             :                         }
     380             : 
     381         151 :                         if (!rootmost_svg) {
     382             :                                 DrawableContext *ctx;
     383           0 :                                 Fixed width = tr_state->parent_anim_atts ? tr_state->parent_anim_atts->width->value : 0;
     384           0 :                                 Fixed height = tr_state->parent_anim_atts ? tr_state->parent_anim_atts->height->value : 0;
     385             : 
     386           0 :                                 if (!stack->vp_fill) {
     387           0 :                                         stack->vp_fill = drawable_new();
     388           0 :                                         stack->vp_fill->node = node;
     389             :                                 }
     390           0 :                                 if ((width != stack->vp_fill->path->bbox.width) || (height != stack->vp_fill->path->bbox.height)) {
     391           0 :                                         drawable_reset_path(stack->vp_fill);
     392           0 :                                         gf_path_add_rect(stack->vp_fill->path, 0, 0, width, -height);
     393             :                                 }
     394             : 
     395           0 :                                 ctx = drawable_init_context_svg(stack->vp_fill, tr_state);
     396           0 :                                 if (ctx) {
     397           0 :                                         ctx->flags &= ~CTX_IS_TRANSPARENT;
     398           0 :                                         ctx->aspect.pen_props.width = 0;
     399           0 :                                         ctx->aspect.fill_color = viewport_color;
     400           0 :                                         ctx->aspect.fill_texture = NULL;
     401           0 :                                         if (col_dirty) ctx->flags |= CTX_APP_DIRTY;
     402           0 :                                         drawable_finalize_sort(ctx, tr_state, NULL);
     403             :                                 }
     404             : 
     405         151 :                         } else if (col_dirty) {
     406           2 :                                 tr_state->visual->compositor->back_color = viewport_color;
     407             :                                 /*invalidate the entire visual*/
     408           2 :                                 tr_state->invalidate_all = 1;
     409             :                         }
     410             :                 }
     411             :         }
     412             : 
     413             : 
     414        1889 :         if (!stack->root_svg)
     415           0 :                 gf_mx2d_add_translation(&tr_state->vb_transform, all_atts.x ? all_atts.x->value : 0, all_atts.y ? all_atts.y->value : 0);
     416             : 
     417             : #ifndef GPAC_DISABLE_3D
     418        1889 :         if (tr_state->visual->type_3d) {
     419           1 :                 gf_mx_add_matrix_2d(&tr_state->model_matrix, &tr_state->vb_transform);
     420             :         } else
     421             : #endif
     422             :         {
     423        1888 :                 gf_mx2d_pre_multiply(&tr_state->transform, &tr_state->vb_transform);
     424             :         }
     425             : 
     426             :         /*store VP and move it to current VP (eg, the one used to compute the vb_transform)*/
     427        1889 :         prev_vp = tr_state->vp_size;
     428        1889 :         tr_state->vp_size = stack->vp;
     429             : 
     430             :         /*the event may trigger scripts which may delete nodes / modify the scene. We therefore send the resize event
     431             :         before traversing the scene*/
     432        1889 :         if (send_resize) {
     433             :                 GF_DOM_Event evt;
     434             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
     435          51 :                 evt.bubbles = 1;
     436          51 :                 evt.type = GF_EVENT_RESIZE;
     437          51 :                 gf_dom_event_fire(node, &evt);
     438             :         }
     439        1889 :         if ((stack->vp.x != prev_vp.x) || (stack->vp.y != prev_vp.y)) {
     440        1841 :                 GF_Scene *scene = node->sgprivate->scenegraph->userpriv;
     441             : 
     442        1841 :                 if (scene) {
     443             :                         GF_DOM_Event evt;
     444             :                         memset(&evt, 0, sizeof(GF_DOM_Event));
     445             :                         evt.bubbles = 0;
     446        1841 :                         evt.screen_rect.width = stack->vpw;
     447        1841 :                         evt.screen_rect.height = stack->vph;
     448        1841 :                         evt.screen_rect.x = stack->dx;
     449        1841 :                         evt.screen_rect.y = stack->dy;
     450        1841 :                         evt.prev_translate.x = stack->vp.x;
     451        1841 :                         evt.prev_translate.y = stack->vp.y;
     452        1841 :                         evt.type = GF_EVENT_VP_RESIZE;
     453        1841 :                         gf_scene_notify_event(scene, 0, NULL, &evt, GF_OK, GF_TRUE);
     454             :                 }
     455             :         }
     456             : 
     457        1889 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     458           0 :                 gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL);
     459             :         } else {
     460        1889 :                 compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     461             :         }
     462        1889 :         tr_state->vp_size = prev_vp;
     463             : 
     464             : #ifndef GPAC_DISABLE_3D
     465        1889 :         if (tr_state->visual->type_3d) {
     466             :                 gf_mx_copy(tr_state->model_matrix, bck_mx);
     467             :         }
     468             : #endif
     469             :         gf_mx2d_copy(tr_state->transform, backup_matrix);
     470             :         gf_mx2d_copy(tr_state->vb_transform, vb_bck);
     471        1889 :         memcpy(tr_state->svg_props, &backup_props, styling_size);
     472        1889 :         tr_state->svg_flags = backup_flags;
     473        1889 :         tr_state->visual->top_clipper = top_clip;
     474        1889 :         if (!stack->root_svg) {
     475           0 :                 tr_state->invalidate_all = invalidate_flag;
     476             :         }
     477        1889 :         tr_state->svg_props = prev_props;
     478             : }
     479             : 
     480          51 : void compositor_init_svg_svg(GF_Compositor *compositor, GF_Node *node)
     481             : {
     482             :         GF_Node *root;
     483             :         SVGsvgStack *stack;
     484             : 
     485          51 :         GF_SAFEALLOC(stack, SVGsvgStack);
     486          51 :         if (!stack) {
     487           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg stack\n"));
     488             :                 return;
     489             :         }
     490             : 
     491          51 :         root = gf_sg_get_root_node(gf_node_get_graph(node));
     492          51 :         stack->root_svg = (root==node) ? 1 : 0;
     493          51 :         if (stack->root_svg) {
     494          51 :                 GF_SAFEALLOC(stack->svg_props, SVGPropertiesPointers);
     495          51 :                 gf_svg_properties_init_pointers(stack->svg_props);
     496             :         }
     497         102 :         gf_mx2d_init(stack->viewbox_mx);
     498             : 
     499          51 :         gf_node_set_private(node, stack);
     500          51 :         gf_node_set_callback_function(node, svg_traverse_svg);
     501             : }
     502             : 
     503           1 : Bool compositor_svg_get_viewport(GF_Node *n, GF_Rect *rc)
     504             : {
     505             :         SVGsvgStack *stack;
     506           1 :         if (!n || (gf_node_get_tag(n) != TAG_SVG_svg)) return 0;
     507           0 :         stack = gf_node_get_private(n);
     508           0 :         rc->width = stack->parent_vp.x;
     509           0 :         rc->height = stack->parent_vp.y;
     510             :         /*not supported yet*/
     511           0 :         rc->x = rc->y = 0;
     512           0 :         return 1;
     513             : }
     514             : 
     515             : typedef struct
     516             : {
     517             :         GROUPING_NODE_STACK_2D
     518             : #ifndef GF_SR_USE_VIDEO_CACHE
     519             :         struct _group_cache *cache;
     520             : #endif
     521             : 
     522             : } SVGgStack;
     523             : 
     524        9809 : static void svg_traverse_g(GF_Node *node, void *rs, Bool is_destroy)
     525             : {
     526             :         GF_Matrix2D backup_matrix;
     527             :         GF_Matrix mx_3d;
     528             :         SVGPropertiesPointers backup_props;
     529             :         u32 backup_flags;
     530             :         u32 styling_size = sizeof(SVGPropertiesPointers);
     531             : 
     532             :         GF_TraverseState *tr_state = (GF_TraverseState *) rs;
     533             : 
     534             :         SVGAllAttributes all_atts;
     535             : 
     536        9809 :         if (is_destroy) {
     537         866 :                 SVGgStack *group = gf_node_get_private(node);
     538             : #ifdef GF_SR_USE_VIDEO_CACHE
     539             :                 group_2d_destroy_svg(node, group);
     540             : #else
     541         866 :                 if (group->cache) group_cache_del(group->cache);
     542             : #endif
     543         866 :                 gf_free(group);
     544         866 :                 gf_sc_check_focus_upon_destroy(node);
     545         866 :                 return;
     546             :         }
     547             :         /*group cache traverse routine*/
     548       17886 :         else if (tr_state->traversing_mode == TRAVERSE_DRAW_2D
     549             : #ifndef GPAC_DISABLE_3D
     550        8943 :                  || tr_state->traversing_mode == TRAVERSE_DRAW_3D
     551             : #endif
     552             :                 ) {
     553         321 :                 SVGgStack *group = gf_node_get_private(node);
     554         321 :                 group_cache_draw(group->cache, tr_state);
     555         321 :                 return;
     556             :         }
     557             : 
     558        8622 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     559             : 
     560        8622 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
     561             :                 return;
     562             : 
     563        8321 :         if (compositor_svg_is_display_off(tr_state->svg_props)) {
     564             :                 /*              u32 prev_flags = tr_state->switched_off;
     565             :                                 tr_state->switched_off = 1;
     566             :                                 compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     567             :                                 tr_state->switched_off = prev_flags;*/
     568             : 
     569         150 :                 memcpy(tr_state->svg_props, &backup_props, styling_size);
     570         150 :                 tr_state->svg_flags = backup_flags;
     571         150 :                 return;
     572             :         }
     573             : 
     574        8171 :         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     575        8171 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     576           0 :                 gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL);
     577        8171 :         } else if (tr_state->traversing_mode == TRAVERSE_SORT) {
     578             : #ifdef GF_SR_USE_DEPTH
     579             :                 Fixed scale, offset, dscale, doffset;
     580             : #endif
     581             :                 Fixed opacity = FIX_ONE;
     582             :                 Bool clear = 0;
     583             :                 SVGgStack *group;
     584             : 
     585        7860 :                 if (!tr_state->in_svg_filter && all_atts.filter && all_atts.filter->iri.target) {
     586             : #ifdef GPAC_ENABLE_SVG_FILTERS
     587             :                         svg_draw_filter(all_atts.filter->iri.target, node, tr_state);
     588             : #endif
     589             :                         return;
     590             :                 }
     591        7860 :                 group = gf_node_get_private(node);
     592             : 
     593        7860 :                 if (tr_state->parent_use_opacity) {
     594           0 :                         opacity = tr_state->parent_use_opacity->value;
     595           0 :                         tr_state->parent_use_opacity = NULL;
     596             :                 }
     597        7860 :                 if (all_atts.opacity) {
     598         465 :                         opacity = gf_mulfix(opacity, all_atts.opacity->value);
     599             :                 }
     600        7860 :                 if (gf_node_dirty_get(node)&GF_SG_CHILD_DIRTY) {
     601        2639 :                         drawable_reset_group_highlight(tr_state, node);
     602             :                         clear=1;
     603             :                 }
     604             : 
     605             : #ifdef GF_SR_USE_DEPTH
     606             :                 dscale = FIX_ONE;
     607             :                 doffset=0;
     608        7860 :                 if (all_atts.gpac_depthGain && all_atts.gpac_depthGain->type==SVG_NUMBER_VALUE) dscale = all_atts.gpac_depthGain->value;
     609        7860 :                 if (all_atts.gpac_depthOffset && all_atts.gpac_depthOffset->type==SVG_NUMBER_VALUE) doffset = all_atts.gpac_depthOffset->value;
     610        7860 :                 scale = tr_state->depth_gain;
     611        7860 :                 offset = tr_state->depth_offset;
     612             :                 // new offset is multiplied by parent gain and added to parent offset
     613        7860 :                 tr_state->depth_offset = gf_mulfix(doffset, scale) + offset;
     614             :                 // gain is multiplied by parent gain
     615        7860 :                 tr_state->depth_gain = gf_mulfix(scale, dscale);
     616             : #endif
     617             : 
     618        7860 :                 if (!tr_state->override_appearance && (opacity < FIX_ONE)) {
     619         315 :                         if (!group->cache) {
     620          18 :                                 group->cache = group_cache_new(tr_state->visual->compositor, node);
     621          18 :                                 group->cache->force_recompute = 1;
     622             :                         }
     623         315 :                         group->cache->opacity = opacity;
     624         315 :                         if (tr_state->visual->compositor->zoom_changed)
     625          18 :                                 group->cache->force_recompute = 1;
     626         315 :                         group->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE;
     627             : #ifdef GF_SR_USE_VIDEO_CACHE
     628             :                         group_2d_cache_traverse(node, group, tr_state);
     629             : #else
     630         315 :                         group_cache_traverse(node, group->cache, tr_state, group->cache->force_recompute, 0, 0);
     631             : #endif
     632             :                 } else {
     633             : #ifdef GF_SR_USE_VIDEO_CACHE
     634             :                         Bool group_cached;
     635             : 
     636             :                         group_cached = group_2d_cache_traverse(node, group, tr_state);
     637             :                         gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
     638             :                         /*group is not cached, traverse the children*/
     639             :                         if (!group_cached) {
     640             :                                 GF_ChildNodeItem *child;
     641             :                                 DrawableContext *first_ctx = tr_state->visual->cur_context;
     642             :                                 u32 cache_too_small = 0;
     643             :                                 Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0;
     644             :                                 u32 traverse_time = gf_sys_clock();
     645             :                                 u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue);
     646             :                                 tr_state->cache_too_small = 0;
     647             : 
     648             :                                 child = ((GF_ParentNode *)node)->children;
     649             :                                 while (child) {
     650             :                                         gf_node_traverse(child->node, tr_state);
     651             :                                         child = child->next;
     652             :                                         if (tr_state->cache_too_small)
     653             :                                                 cache_too_small++;
     654             :                                 }
     655             : 
     656             :                                 if (cache_too_small) {
     657             :                                         tr_state->cache_too_small = 1;
     658             :                                 } else {
     659             :                                         /*get the traversal time for each group*/
     660             :                                         traverse_time = gf_sys_clock() - traverse_time;
     661             :                                         group->traverse_time += traverse_time;
     662             :                                         /*record the traversal information and turn cache on if possible*/
     663             :                                         group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx);
     664             :                                 }
     665             :                         }
     666             : #else
     667        7545 :                         compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     668             : #endif
     669             :                 }
     670        7860 :                 if (clear) gf_node_dirty_clear(node, 0);
     671             : 
     672        7860 :                 drawable_check_focus_highlight(node, tr_state, NULL);
     673             : 
     674             : #ifdef GF_SR_USE_DEPTH
     675        7860 :                 tr_state->depth_gain = scale;
     676        7860 :                 tr_state->depth_offset = offset;
     677             : #endif
     678             :         } else {
     679         311 :                 compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     680             :         }
     681        8171 :         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     682        8171 :         memcpy(tr_state->svg_props, &backup_props, styling_size);
     683        8171 :         tr_state->svg_flags = backup_flags;
     684             : }
     685             : 
     686             : 
     687         866 : void compositor_init_svg_g(GF_Compositor *compositor, GF_Node *node)
     688             : {
     689             :         SVGgStack *stack;
     690         866 :         GF_SAFEALLOC(stack, SVGgStack);
     691         866 :         if (!stack) return;
     692         866 :         gf_node_set_private(node, stack);
     693             : 
     694         866 :         gf_node_set_callback_function(node, svg_traverse_g);
     695             : }
     696             : 
     697             : 
     698             : #if 0 //unused
     699             : static void svg_traverse_defs(GF_Node *node, void *rs, Bool is_destroy)
     700             : {
     701             :         SVGPropertiesPointers backup_props;
     702             :         u32 prev_flags, backup_flags;
     703             :         u32 styling_size = sizeof(SVGPropertiesPointers);
     704             : 
     705             :         GF_TraverseState *tr_state = (GF_TraverseState *) rs;
     706             : 
     707             :         SVGAllAttributes all_atts;
     708             : 
     709             :         if (is_destroy) {
     710             :                 gf_sc_check_focus_upon_destroy(node);
     711             :                 return;
     712             :         }
     713             :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     714             : 
     715             :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
     716             :                 return;
     717             : 
     718             :         prev_flags = tr_state->switched_off;
     719             :         tr_state->switched_off = 1;
     720             :         compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     721             :         tr_state->switched_off = prev_flags;
     722             : 
     723             :         memcpy(tr_state->svg_props, &backup_props, styling_size);
     724             :         tr_state->svg_flags = backup_flags;
     725             : }
     726             : 
     727             : 
     728             : void compositor_init_svg_defs(GF_Compositor *compositor, GF_Node *node)
     729             : {
     730             :         gf_node_set_callback_function(node, svg_traverse_defs);
     731             : }
     732             : #endif
     733             : 
     734             : 
     735             : 
     736             : 
     737          60 : static void svg_traverse_switch(GF_Node *node, void *rs, Bool is_destroy)
     738             : {
     739             :         GF_Matrix2D backup_matrix;
     740             :         GF_Matrix mx_3d;
     741             :         SVGPropertiesPointers backup_props;
     742             :         u32 backup_flags;
     743          60 :         s32 *selected_idx = gf_node_get_private(node);
     744             :         u32 styling_size = sizeof(SVGPropertiesPointers);
     745             :         SVGAllAttributes all_atts;
     746             :         GF_TraverseState *tr_state = (GF_TraverseState *) rs;
     747             : 
     748          60 :         if (is_destroy) {
     749          20 :                 gf_free(selected_idx);
     750          20 :                 gf_sc_check_focus_upon_destroy(node);
     751          20 :                 return;
     752             :         }
     753             : 
     754          40 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     755          40 :         if (gf_node_dirty_get(node)) {
     756             :                 u32 pos = 0;
     757          20 :                 GF_ChildNodeItem *child = ((SVG_Element*)node)->children;
     758          20 :                 *selected_idx = -1;
     759          42 :                 while (child) {
     760             :                         SVGAllAttributes atts;
     761          22 :                         gf_svg_flatten_attributes((SVG_Element *)child->node, &atts);
     762          22 :                         if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &atts)) {
     763          20 :                                 *selected_idx = pos;
     764          20 :                                 break;
     765             :                         }
     766           2 :                         pos++;
     767           2 :                         child = child->next;
     768             :                 }
     769          20 :                 drawable_reset_group_highlight(tr_state, node);
     770          20 :                 gf_node_dirty_clear(node, 0);
     771             :         }
     772             : 
     773          40 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
     774             :                 return;
     775             : 
     776          40 :         if (compositor_svg_is_display_off(tr_state->svg_props)) {
     777           0 :                 memcpy(tr_state->svg_props, &backup_props, styling_size);
     778           0 :                 tr_state->svg_flags = backup_flags;
     779           0 :                 return;
     780             :         }
     781             : 
     782          40 :         if (*selected_idx >= 0) {
     783          40 :                 compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     784          40 :                 if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     785           0 :                         gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, selected_idx);
     786             :                 } else {
     787          40 :                         GF_Node *child = gf_node_list_get_child(((SVG_Element *)node)->children, *selected_idx);
     788          40 :                         gf_node_traverse(child, tr_state);
     789             : 
     790          40 :                         drawable_check_focus_highlight(node, tr_state, NULL);
     791             :                 }
     792          40 :                 compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     793             :         }
     794             : 
     795          40 :         memcpy(tr_state->svg_props, &backup_props, styling_size);
     796          40 :         tr_state->svg_flags = backup_flags;
     797             : }
     798             : 
     799          20 : void compositor_init_svg_switch(GF_Compositor *compositor, GF_Node *node)
     800             : {
     801             :         s32 *selected_idx;
     802          20 :         GF_SAFEALLOC(selected_idx, s32);
     803          20 :         if (!selected_idx) {
     804           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate font for svg switch stack\n"));
     805             :                 return;
     806             :         }
     807          20 :         *selected_idx = -1;
     808          20 :         gf_node_set_private(node, selected_idx);
     809          20 :         gf_node_set_callback_function(node, svg_traverse_switch);
     810             : }
     811             : 
     812         756 : static void svg_traverse_a(GF_Node *node, void *rs, Bool is_destroy)
     813             : {
     814             :         GF_Matrix2D backup_matrix;
     815             :         GF_Matrix mx_3d;
     816             :         SVGPropertiesPointers backup_props;
     817             :         u32 styling_size = sizeof(SVGPropertiesPointers);
     818             :         u32 backup_flags;
     819             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     820             :         SVGAllAttributes all_atts;
     821             : 
     822         756 :         if (is_destroy) {
     823           5 :                 gf_sc_check_focus_upon_destroy(node);
     824           5 :                 return;
     825             :         }
     826             : 
     827         751 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     828             : 
     829         751 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
     830             :                 return;
     831             : 
     832         450 :         if (compositor_svg_is_display_off(tr_state->svg_props)) {
     833             :                 /*u32 prev_flags = tr_state->switched_off;
     834             :                 tr_state->switched_off = 1;
     835             :                 compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     836             :                 tr_state->switched_off = prev_flags;*/
     837             : 
     838           0 :                 memcpy(tr_state->svg_props, &backup_props, styling_size);
     839           0 :                 tr_state->svg_flags = backup_flags;
     840           0 :                 return;
     841             :         }
     842             : 
     843         450 :         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     844         450 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     845           0 :                 gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL);
     846             :         } else {
     847         450 :                 compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
     848         450 :                 if (tr_state->traversing_mode==TRAVERSE_SORT)
     849         450 :                         drawable_check_focus_highlight(node, tr_state, NULL);
     850             :         }
     851         450 :         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     852         450 :         memcpy(tr_state->svg_props, &backup_props, styling_size);
     853         450 :         tr_state->svg_flags = backup_flags;
     854             : }
     855             : 
     856           0 : static void svg_a_set_view(GF_Node *handler, GF_Compositor *compositor, const char *url)
     857             : {
     858           0 :         gf_scene_set_fragment_uri(handler, url);
     859             :         /*force recompute viewbox of root SVG - FIXME in full this should be the parent svg*/
     860           0 :         gf_node_dirty_set(gf_sg_get_root_node(gf_node_get_graph(handler)), 0, 0);
     861             : 
     862           0 :         compositor->trans_x = compositor->trans_y = 0;
     863           0 :         compositor->rotation = 0;
     864           0 :         compositor->zoom = FIX_ONE;
     865           0 :         compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0);
     866           0 :         gf_sc_invalidate(compositor, NULL);
     867           0 : }
     868             : 
     869           0 : static void svg_a_handle_event(GF_Node *handler, GF_DOM_Event *event, GF_Node *observer)
     870             : {
     871             :         GF_Compositor *compositor;
     872             :         GF_Event evt;
     873             :         SVG_Element *a;
     874             :         SVGAllAttributes all_atts;
     875             : 
     876           0 :         if (event->event_phase & GF_DOM_EVENT_PHASE_PREVENT) return;
     877             : 
     878             :         assert(gf_node_get_tag((GF_Node*)event->currentTarget->ptr)==TAG_SVG_a);
     879           0 :         a = (SVG_Element *) event->currentTarget->ptr;
     880           0 :         gf_svg_flatten_attributes(a, &all_atts);
     881             : 
     882           0 :         compositor = (GF_Compositor *)gf_node_get_private((GF_Node *)a);
     883             : 
     884           0 :         if (!all_atts.xlink_href) return;
     885             : 
     886           0 :         if (event->type==GF_EVENT_MOUSEOVER) {
     887           0 :                 evt.type = GF_EVENT_NAVIGATE_INFO;
     888             : 
     889           0 :                 if (all_atts.xlink_title) evt.navigate.to_url = *all_atts.xlink_title;
     890           0 :                 else if (all_atts.xlink_href->string) evt.navigate.to_url = all_atts.xlink_href->string;
     891             :                 else {
     892           0 :                         evt.navigate.to_url = gf_node_get_name(all_atts.xlink_href->target);
     893           0 :                         if (!evt.navigate.to_url) evt.navigate.to_url = "document internal link";
     894             :                 }
     895             : 
     896           0 :                 gf_sc_send_event(compositor, &evt);
     897           0 :                 return;
     898             :         }
     899             : 
     900           0 :         evt.type = GF_EVENT_NAVIGATE;
     901             : 
     902           0 :         if (all_atts.xlink_href->type == XMLRI_STRING) {
     903           0 :                 evt.navigate.to_url = gf_scene_resolve_xlink(handler, all_atts.xlink_href->string);
     904           0 :                 if (evt.navigate.to_url) {
     905           0 :                         if (all_atts.target) {
     906           0 :                                 evt.navigate.parameters = (const char **) &all_atts.target;
     907           0 :                                 evt.navigate.param_count = 1;
     908             :                         } else {
     909           0 :                                 evt.navigate.parameters = NULL;
     910           0 :                                 evt.navigate.param_count = 0;
     911             :                         }
     912             : 
     913           0 :                         if (evt.navigate.to_url[0] != '#') {
     914           0 :                                 gf_scene_process_anchor(handler, &evt);
     915           0 :                                 gf_free((char *)evt.navigate.to_url);
     916           0 :                                 return;
     917             :                         }
     918           0 :                         all_atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(handler), (char *) evt.navigate.to_url+1);
     919           0 :                         if (all_atts.xlink_href->target) {
     920           0 :                                 all_atts.xlink_href->type = XMLRI_ELEMENTID;
     921           0 :                                 gf_free((char *)evt.navigate.to_url);
     922             :                         } else {
     923           0 :                                 svg_a_set_view(handler, compositor, evt.navigate.to_url + 1);
     924           0 :                                 gf_free((char *)evt.navigate.to_url);
     925           0 :                                 return;
     926             :                         }
     927             :                 }
     928             :         }
     929           0 :         if (!all_atts.xlink_href->target) {
     930             :                 return;
     931             :         }
     932             :         /*this is a time event*/
     933           0 :         switch (gf_node_get_tag(all_atts.xlink_href->target)) {
     934           0 :         case TAG_SVG_set:
     935             :         case TAG_SVG_animate:
     936             :         case TAG_SVG_animateColor:
     937             :         case TAG_SVG_animateTransform:
     938             :         case TAG_SVG_animateMotion:
     939             :         case TAG_SVG_discard:
     940             :         case TAG_SVG_animation:
     941             :         case TAG_SVG_video:
     942             :         case TAG_SVG_audio:
     943           0 :                 gf_smil_timing_insert_clock(all_atts.xlink_href->target, 0, gf_node_get_scene_time((GF_Node *)handler) );
     944           0 :                 break;
     945           0 :         default:
     946             :                 /*this is an implicit SVGView event*/
     947           0 :                 svg_a_set_view(handler, compositor, gf_node_get_name(all_atts.xlink_href->target));
     948           0 :                 break;
     949             :         }
     950             : }
     951             : 
     952           5 : void compositor_init_svg_a(GF_Compositor *compositor, GF_Node *node)
     953             : {
     954             :         SVG_handlerElement *handler;
     955           5 :         gf_node_set_callback_function(node, svg_traverse_a);
     956           5 :         gf_node_set_private((GF_Node *)node, compositor);
     957             : 
     958             :         /*listener for onClick event*/
     959           5 :         handler = gf_dom_listener_build(node, GF_EVENT_CLICK, 0);
     960             :         /*and overwrite handler*/
     961           5 :         handler->handle_event = svg_a_handle_event;
     962             : 
     963             :         /*listener for activate event*/
     964           5 :         handler = gf_dom_listener_build(node, GF_EVENT_ACTIVATE, 0);
     965             :         /*and overwrite handler*/
     966           5 :         handler->handle_event = svg_a_handle_event;
     967             : 
     968             :         /*listener for mousemove event*/
     969           5 :         handler = gf_dom_listener_build(node, GF_EVENT_MOUSEOVER, 0);
     970             :         /*and overwrite handler*/
     971           5 :         handler->handle_event = svg_a_handle_event;
     972             : 
     973           5 : }
     974             : 
     975             : typedef struct
     976             : {
     977             :         GF_MediaObject *resource;
     978             : //      GF_Node *used_node;
     979             :         GF_SceneGraph *inline_sg;
     980             :         const char *fragment_id;
     981             :         Bool needs_play;
     982             :         u32 init_vis_state;
     983             : } SVGlinkStack;
     984             : 
     985             : 
     986        1780 : static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool is_foreign_object)
     987             : {
     988             :         GF_Matrix2D backup_matrix;
     989             :         GF_Matrix mx_3d;
     990             :         GF_Matrix2D translate;
     991             :         SVGPropertiesPointers backup_props;
     992             :         u32 backup_flags, dirty;
     993             :         Bool is_fragment, parent_is_use;
     994             :         GF_Node *used_node;
     995             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     996             :         SVGAllAttributes all_atts;
     997        1780 :         SVGlinkStack *stack = gf_node_get_private(node);
     998             :         SFVec2f prev_vp;
     999             :         SVG_Number *prev_opacity;
    1000             : 
    1001        1780 :         if (is_destroy) {
    1002          24 :                 if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource);
    1003          24 :                 gf_free(stack);
    1004          24 :                 return;
    1005             :         }
    1006             : 
    1007             : 
    1008        1756 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
    1009        1756 :         if (!all_atts.xlink_href) return;
    1010             : 
    1011        1605 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
    1012             :                 return;
    1013             : 
    1014        1605 :         dirty = gf_node_dirty_get(node);
    1015        1605 :         if (dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node);
    1016             : 
    1017        1605 :         if (dirty & GF_SG_SVG_XLINK_HREF_DIRTY) {
    1018          23 :                 stack->fragment_id = NULL;
    1019          23 :                 stack->inline_sg = NULL;
    1020          23 :                 if (all_atts.xlink_href->string && (all_atts.xlink_href->string[0]=='#')) {
    1021          20 :                         stack->fragment_id = all_atts.xlink_href->string;
    1022          20 :                         stack->inline_sg = gf_node_get_graph(node);
    1023             :                 } else {
    1024           3 :                         GF_MediaObject *new_res = gf_mo_load_xlink_resource(node, is_foreign_object, 0, -1);
    1025           3 :                         if (new_res != stack->resource) {
    1026           2 :                                 if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource);
    1027           2 :                                 stack->resource = new_res;
    1028             :                         }
    1029             :                 }
    1030             :         }
    1031        1605 :         gf_node_dirty_clear(node, 0);
    1032             : 
    1033             :         /*locate the used node - this is done at each step to handle progressive loading*/
    1034             :         is_fragment = 0;
    1035             :         used_node = NULL;
    1036        1605 :         if (!stack->inline_sg && !stack->fragment_id && all_atts.xlink_href) {
    1037         152 :                 if (all_atts.xlink_href->type == XMLRI_ELEMENTID) {
    1038         150 :                         used_node = all_atts.xlink_href->target;
    1039             :                         is_fragment = 1;
    1040           2 :                 } else if (stack->resource) {
    1041           2 :                         stack->inline_sg = gf_mo_get_scenegraph(stack->resource);
    1042           2 :                         if (!is_foreign_object && all_atts.xlink_href->string) {
    1043           2 :                                 stack->fragment_id = strchr(all_atts.xlink_href->string, '#');
    1044             :                         }
    1045             :                 }
    1046             :         }
    1047        1605 :         if (!used_node && stack->inline_sg) {
    1048        1455 :                 if (stack->fragment_id) {
    1049        1304 :                         used_node = gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1);
    1050             :                         is_fragment = 1;
    1051         151 :                 } else if (is_foreign_object) {
    1052           0 :                         used_node = gf_sg_get_root_node(stack->inline_sg);
    1053             :                 }
    1054             :         }
    1055        1605 :         if (!used_node) goto end;
    1056             : 
    1057             :         /*stack use nodes for picking*/
    1058        1152 :         gf_list_add(tr_state->use_stack, used_node);
    1059        1152 :         gf_list_add(tr_state->use_stack, node);
    1060             : 
    1061        1152 :         gf_mx2d_init(translate);
    1062        1152 :         translate.m[2] = (all_atts.x ? all_atts.x->value : 0);
    1063        1152 :         translate.m[5] = (all_atts.y ? all_atts.y->value : 0);
    1064             : 
    1065             :         /*update VP size (SVG 1.1)*/
    1066        1152 :         prev_vp = tr_state->vp_size;
    1067        1152 :         if (all_atts.width && all_atts.height) {
    1068           0 :                 tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width);
    1069           0 :                 tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height);
    1070             :         }
    1071             : 
    1072        1152 :         prev_opacity = tr_state->parent_use_opacity;
    1073        1152 :         tr_state->parent_use_opacity = all_atts.opacity;
    1074        1152 :         parent_is_use = tr_state->parent_is_use;
    1075        1152 :         tr_state->parent_is_use = is_foreign_object ? 0 : 1;
    1076             : 
    1077        1152 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
    1078           0 :                 compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
    1079           0 :                 if (!compositor_svg_is_display_off(tr_state->svg_props)) {
    1080           0 :                         gf_node_traverse(used_node, tr_state);
    1081           0 :                         gf_mx2d_apply_rect(&translate, &tr_state->bounds);
    1082             :                 }
    1083           0 :                 compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
    1084             :         }
    1085             :         /*SORT mode and visible, traverse*/
    1086        1152 :         else if (!compositor_svg_is_display_off(tr_state->svg_props)
    1087        1152 :                  && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)) {
    1088             : 
    1089        1152 :                 compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
    1090             : 
    1091             : #ifndef GPAC_DISABLE_3D
    1092        1152 :                 if (tr_state->visual->type_3d) {
    1093           0 :                         gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate);
    1094             :                 } else
    1095             : #endif
    1096        1152 :                         gf_mx2d_pre_multiply(&tr_state->transform, &translate);
    1097             : 
    1098             : 
    1099        1152 :                 drawable_check_focus_highlight(node, tr_state, NULL);
    1100        1152 :                 if (is_fragment) {
    1101        1152 :                         gf_node_traverse(used_node, tr_state);
    1102             :                 } else {
    1103           0 :                         gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state);
    1104             :                 }
    1105        1152 :                 compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
    1106             : 
    1107             :         }
    1108        1152 :         gf_list_rem_last(tr_state->use_stack);
    1109        1152 :         gf_list_rem_last(tr_state->use_stack);
    1110        1152 :         tr_state->vp_size = prev_vp;
    1111             : 
    1112        1152 :         tr_state->parent_is_use = parent_is_use;
    1113        1152 :         tr_state->parent_use_opacity = prev_opacity;
    1114             : 
    1115        2058 : end:
    1116        1605 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
    1117        1605 :         tr_state->svg_flags = backup_flags;
    1118             : }
    1119             : 
    1120        1628 : static void svg_traverse_use(GF_Node *node, void *rs, Bool is_destroy)
    1121             : {
    1122        1628 :         svg_traverse_resource(node, rs, is_destroy, 0);
    1123        1628 : }
    1124             : 
    1125          23 : void compositor_init_svg_use(GF_Compositor *compositor, GF_Node *node)
    1126             : {
    1127             :         SVGlinkStack *stack;
    1128          23 :         GF_SAFEALLOC(stack, SVGlinkStack);
    1129          23 :         if (!stack) return;
    1130          23 :         gf_node_set_private(node, stack);
    1131          23 :         gf_node_set_callback_function(node, svg_traverse_use);
    1132             :         /*force first processing of xlink-href*/
    1133          23 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0);
    1134             : }
    1135             : 
    1136             : 
    1137             : 
    1138             : /***********************************
    1139             :  *  'animation' specific functions *
    1140             :  ***********************************/
    1141             : 
    1142         836 : static void svg_animation_smil_update(GF_Node *node, SVGlinkStack *stack, Fixed normalized_scene_time)
    1143             : {
    1144         836 :         if (stack->init_vis_state == 3) {
    1145           0 :                 stack->init_vis_state = 4;
    1146           0 :                 gf_mo_resume(stack->resource);
    1147         836 :         } else if (stack->needs_play || (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY )) {
    1148             :                 SVGAllAttributes all_atts;
    1149             :                 Double clipBegin, clipEnd;
    1150             :                 GF_MediaObject *new_res;
    1151           7 :                 gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
    1152           7 :                 clipBegin = all_atts.clipBegin ? *all_atts.clipBegin : 0;
    1153           7 :                 clipEnd = all_atts.clipEnd ? *all_atts.clipEnd : -1;
    1154             : 
    1155           7 :                 if (stack->needs_play) {
    1156           0 :                         gf_mo_play(stack->resource, clipBegin, clipEnd, 0);
    1157           0 :                         stack->needs_play = 0;
    1158             :                 } else {
    1159           7 :                         Bool primary = all_atts.gpac_useAsPrimary ? *all_atts.gpac_useAsPrimary : 1;
    1160           7 :                         new_res = gf_mo_load_xlink_resource(node, primary, clipBegin, clipEnd);
    1161           7 :                         if (new_res != stack->resource) {
    1162           7 :                                 if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource);
    1163           7 :                                 if (all_atts.xlink_href) all_atts.xlink_href->target = NULL;
    1164           7 :                                 stack->resource = new_res;
    1165           7 :                                 stack->fragment_id = NULL;
    1166           7 :                                 stack->inline_sg = NULL;
    1167             :                         }
    1168           7 :                         gf_node_dirty_clear(node, 0);
    1169             :                 }
    1170             :         }
    1171         836 : }
    1172             : 
    1173         841 : static void svg_animation_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState status)
    1174             : {
    1175             :         Bool reset_target = GF_FALSE;
    1176         841 :         GF_Node *node = gf_smil_get_element(rti);
    1177         841 :         SVGlinkStack *stack = gf_node_get_private(node);
    1178         841 :         switch (status) {
    1179         836 :         case SMIL_TIMING_EVAL_UPDATE:
    1180         836 :                 svg_animation_smil_update(node, stack, normalized_scene_time);
    1181             :                 break;
    1182           0 :         case SMIL_TIMING_EVAL_FREEZE:
    1183           0 :                 if (stack->resource) {
    1184           0 :                         gf_mo_stop(&stack->resource);
    1185           0 :                         stack->needs_play = 1;
    1186             :                 }
    1187             :                 break;
    1188           1 :         case SMIL_TIMING_EVAL_REMOVE:
    1189           1 :                 if (stack->resource) {
    1190             :                         reset_target = GF_TRUE;
    1191           1 :                         gf_mo_unload_xlink_resource(node, stack->resource);
    1192           1 :                         stack->resource = NULL;
    1193           1 :                         stack->fragment_id = NULL;
    1194           1 :                         stack->inline_sg = NULL;
    1195           1 :                         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0);
    1196             :                 }
    1197             :                 break;
    1198           4 :         case SMIL_TIMING_EVAL_REPEAT:
    1199           4 :                 if (stack->resource) {
    1200             :                         reset_target = GF_TRUE;
    1201           4 :                         stack->fragment_id = NULL;
    1202           4 :                         stack->inline_sg = NULL;
    1203           4 :                         gf_mo_restart(stack->resource);
    1204             :                 }
    1205             :                 break;
    1206             :         default:
    1207             :                 break;
    1208             :         }
    1209             :         if (reset_target) {
    1210             :                 SVGAllAttributes all_atts;
    1211           5 :                 gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
    1212           5 :                 if (all_atts.xlink_href) all_atts.xlink_href->target=NULL;
    1213             :         }
    1214         841 : }
    1215             : 
    1216             : 
    1217        1064 : static void svg_traverse_animation(GF_Node *node, void *rs, Bool is_destroy)
    1218             : {
    1219             :         SVGAllAttributes all_atts;
    1220             :         GF_Matrix2D backup_matrix;
    1221             :         GF_Matrix backup_matrix3d;
    1222             :         SVGPropertiesPointers backup_props;
    1223             :         u32 backup_flags;
    1224             :         SFVec2f prev_vp;
    1225             :         GF_Rect rc;
    1226             :         GF_IRect clip, prev_clip;
    1227             :         SVGAllAttributes *prev_vp_atts;
    1228             :         GF_TraverseState *tr_state = (GF_TraverseState*)rs;
    1229             :         GF_Matrix2D translate;
    1230             :         SVGPropertiesPointers *old_props;
    1231        1064 :         SVGlinkStack *stack = gf_node_get_private(node);
    1232             : 
    1233        1064 :         if (is_destroy) {
    1234           7 :                 if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource);
    1235           7 :                 gf_free(stack);
    1236           7 :                 return;
    1237             :         }
    1238        1057 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
    1239             : 
    1240             : 
    1241        1057 :         if (!stack->inline_sg && !stack->resource) {
    1242         212 :                 if (!stack->init_vis_state) {
    1243           3 :                         if (all_atts.initialVisibility && (*all_atts.initialVisibility==SVG_INITIALVISIBILTY_ALWAYS)) {
    1244           0 :                                 stack->init_vis_state = 2;
    1245           0 :                                 svg_animation_smil_update(node, stack, 0);
    1246             :                         } else {
    1247           3 :                                 stack->init_vis_state = 1;
    1248             :                         }
    1249             :                 }
    1250         212 :                 if (!stack->inline_sg && !stack->resource)
    1251             :                         return;
    1252             :         }
    1253             : 
    1254         845 :         if (!all_atts.width || !all_atts.height) return;
    1255         845 :         if (!all_atts.width->value || !all_atts.height->value) return;
    1256             : 
    1257         845 :         if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags))
    1258             :                 return;
    1259             : 
    1260        1690 :         if (compositor_svg_is_display_off(tr_state->svg_props) ||
    1261         845 :                 *(tr_state->svg_props->visibility) == SVG_VISIBILITY_HIDDEN) {
    1262             :                 goto end;
    1263             :         }
    1264             : 
    1265         845 :         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &backup_matrix3d);
    1266             : 
    1267             :         /*add x/y translation*/
    1268         845 :         gf_mx2d_init(translate);
    1269         845 :         translate.m[2] = (all_atts.x ? all_atts.x->value : 0);
    1270         845 :         translate.m[5] = (all_atts.y ? all_atts.y->value : 0);
    1271             : #ifndef GPAC_DISABLE_3D
    1272         845 :         if (tr_state->visual->type_3d) {
    1273           0 :                 gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate);
    1274             :         } else
    1275             : #endif
    1276         845 :                 gf_mx2d_pre_multiply(&tr_state->transform, &translate);
    1277             : 
    1278             :         /*reset SVG props to reload a new inheritance context*/
    1279         845 :         old_props = tr_state->svg_props;
    1280         845 :         tr_state->svg_props = NULL;
    1281             : 
    1282             :         /*store this node's attribute to compute PAR/ViewBox of the child <svg>*/
    1283         845 :         prev_vp_atts = tr_state->parent_anim_atts;
    1284         845 :         tr_state->parent_anim_atts = &all_atts;
    1285             : 
    1286             :         /*update VP size*/
    1287         845 :         prev_vp = tr_state->vp_size;
    1288             : 
    1289         845 :         tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width);
    1290         845 :         tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height);
    1291             : 
    1292             :         /*setup new clipper*/
    1293         845 :         rc.width = tr_state->vp_size.x;
    1294         845 :         rc.height = tr_state->vp_size.y;
    1295         845 :         rc.x = 0;
    1296         845 :         rc.y = tr_state->vp_size.y;
    1297         845 :         gf_mx2d_apply_rect(&tr_state->transform, &rc);
    1298         845 :         prev_clip = tr_state->visual->top_clipper;
    1299         845 :         clip = gf_rect_pixelize(&rc);
    1300         845 :         gf_irect_intersect(&tr_state->visual->top_clipper, &clip);
    1301             : 
    1302         845 :         if (!stack->inline_sg && stack->resource) {
    1303          11 :                 stack->inline_sg = gf_mo_get_scenegraph(stack->resource);
    1304             :         }
    1305             :         /*if we have the focus, move it to the subtree*/
    1306         845 :         if (tr_state->visual->compositor->focus_node==node) {
    1307           0 :                 GF_Node *subroot = gf_sg_get_root_node(stack->inline_sg);
    1308           0 :                 if (subroot) tr_state->visual->compositor->focus_node = subroot;
    1309             :         }
    1310             : 
    1311         845 :         if (stack->inline_sg && stack->resource->odm) {
    1312         845 :                 gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state);
    1313             :         }
    1314             : 
    1315         845 :         if (stack->init_vis_state == 2) {
    1316           0 :                 stack->init_vis_state = 3;
    1317           0 :                 gf_mo_pause(stack->resource);
    1318             :         }
    1319             : 
    1320         845 :         tr_state->svg_props = old_props;
    1321         845 :         tr_state->visual->top_clipper = prev_clip;
    1322             : 
    1323         845 :         tr_state->parent_anim_atts = prev_vp_atts;
    1324         845 :         tr_state->vp_size = prev_vp;
    1325             : 
    1326         845 :         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &backup_matrix3d);
    1327             : 
    1328         845 : end:
    1329         845 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
    1330         845 :         tr_state->svg_flags = backup_flags;
    1331             : }
    1332             : 
    1333           7 : void compositor_init_svg_animation(GF_Compositor *compositor, GF_Node *node)
    1334             : {
    1335             :         SVGlinkStack *stack;
    1336             : 
    1337           7 :         GF_SAFEALLOC(stack, SVGlinkStack);
    1338           7 :         if (!stack) return;
    1339           7 :         gf_node_set_private(node, stack);
    1340           7 :         gf_node_set_callback_function(node, svg_traverse_animation);
    1341             : 
    1342           7 :         gf_smil_set_evaluation_callback(node, svg_animation_smil_evaluate);
    1343             : 
    1344             :         /*force first processing of xlink-href*/
    1345           7 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0);
    1346             : }
    1347             : 
    1348           0 : void svg_pause_animation(GF_Node *n, Bool pause)
    1349             : {
    1350           0 :         SVGlinkStack *st =  gf_node_get_private(n);
    1351           0 :         if (!st) return;
    1352           0 :         if (pause) gf_mo_pause(st->resource);
    1353           0 :         else gf_mo_resume(st->resource);
    1354             : }
    1355             : 
    1356         152 : static void svg_traverse_foreign_object(GF_Node *node, void *rs, Bool is_destroy)
    1357             : {
    1358         152 :         svg_traverse_resource(node, rs, is_destroy, 1);
    1359         152 : }
    1360             : 
    1361           1 : void compositor_init_svg_foreign_object(GF_Compositor *compositor, GF_Node *node)
    1362             : {
    1363             :         SVGlinkStack *stack;
    1364           1 :         GF_SAFEALLOC(stack, SVGlinkStack);
    1365           1 :         if (!stack) return;
    1366           1 :         gf_node_set_private(node, stack);
    1367           1 :         gf_node_set_callback_function(node, svg_traverse_foreign_object);
    1368             :         /*force first processing of xlink-href*/
    1369           1 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0);
    1370             : }
    1371             : 
    1372          20 : GF_Node *compositor_svg_get_xlink_resource_node(GF_Node *node, XMLRI *xlink)
    1373             : {
    1374             :         SVGlinkStack *stack;
    1375          20 :         switch (gf_node_get_tag(node)) {
    1376           7 :         case TAG_SVG_animation:
    1377           7 :                 stack = gf_node_get_private(node);
    1378           7 :                 return gf_sg_get_root_node(stack->inline_sg);
    1379          13 :         case TAG_SVG_use:
    1380          13 :                 stack = gf_node_get_private(node);
    1381          13 :                 if (stack && stack->fragment_id)
    1382           0 :                         return gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1);
    1383          13 :                 return xlink ? xlink->target : NULL;
    1384             :         }
    1385             :         return NULL;
    1386             : }
    1387             : 
    1388             : #if 0 //unused
    1389             : GF_SceneGraph *gf_sc_animation_get_scenegraph(GF_Node *node)
    1390             : {
    1391             :         SVGlinkStack *stack;
    1392             :         if (node->sgprivate->tag!=TAG_SVG_animation) return NULL;
    1393             :         stack = gf_node_get_private(node);
    1394             :         return stack->inline_sg;
    1395             : }
    1396             : #endif
    1397             : 
    1398             : 
    1399             : #endif
    1400             : 
    1401             : 

Generated by: LCOV version 1.13