LCOV - code coverage report
Current view: top level - compositor - svg_geometry.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 278 348 79.9 %
Date: 2021-04-29 23:48:07 Functions: 26 27 96.3 %

          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             : #include "nodes_stacks.h"
      28             : 
      29             : #ifdef GPAC_DISABLE_SVG
      30             : 
      31             : Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
      32             : {
      33             :         return 0;
      34             : }
      35             : 
      36             : #else
      37             : 
      38         617 : Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
      39             : {
      40             :         u32 check_fill, check_stroke;
      41             :         Bool check_over, check_outline, check_vis, inside;
      42             :         GF_Rect rc;
      43             :         u8 ptr_evt;
      44             : 
      45         617 :         ptr_evt = *tr_state->svg_props->pointer_events;
      46             : 
      47         617 :         if (ptr_evt==SVG_POINTEREVENTS_NONE) {
      48             :                 return 0;
      49             :         }
      50             : 
      51         617 :         if (glyph_rc) {
      52         278 :                 rc = *glyph_rc;
      53             :         } else {
      54         339 :                 gf_path_get_bounds(drawable->path, &rc);
      55             :         }
      56         617 :         inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;
      57             : 
      58         617 :         if (ptr_evt==SVG_POINTEREVENTS_BOUNDINGBOX) return inside;
      59             : 
      60             :         check_fill = check_stroke = check_over = check_outline = check_vis = 0;
      61             :         /*
      62             :         check_vis: if set, return FALSE when visible property is not "visible"
      63             :         check_fill:
      64             :                 if 1, checks whether point is over path,
      65             :                 if 2, checks if the path is painted (even with fill-opacity=0) before
      66             :         check_stroke:
      67             :                 if 1, checks whether point is over path outline,
      68             :                 if 2, checks if the path outline is painted (even with stroke-opacity=0) before
      69             :         */
      70         617 :         switch (ptr_evt) {
      71             :         case SVG_POINTEREVENTS_VISIBLE:
      72             :                 check_vis = 1;
      73             :                 check_fill = 1;
      74             :                 check_stroke = 1;
      75             :                 break;
      76           0 :         case SVG_POINTEREVENTS_VISIBLEFILL:
      77             :                 check_vis = 1;
      78             :                 check_fill = 1;
      79           0 :                 break;
      80           0 :         case SVG_POINTEREVENTS_VISIBLESTROKE:
      81             :                 check_vis = 1;
      82             :                 check_stroke = 1;
      83           0 :                 break;
      84         617 :         case SVG_POINTEREVENTS_VISIBLEPAINTED:
      85             :                 check_vis = 1;
      86             :                 check_fill = 2;
      87             :                 check_stroke = 2;
      88         617 :                 break;
      89           0 :         case SVG_POINTEREVENTS_FILL:
      90             :                 check_fill = 1;
      91           0 :                 break;
      92           0 :         case SVG_POINTEREVENTS_STROKE:
      93             :                 check_stroke = 1;
      94           0 :                 break;
      95           0 :         case SVG_POINTEREVENTS_ALL:
      96             :                 check_fill = 1;
      97             :                 check_stroke = 1;
      98           0 :                 break;
      99           0 :         case SVG_POINTEREVENTS_PAINTED:
     100             :                 check_fill = 2;
     101             :                 check_stroke = 2;
     102           0 :                 break;
     103             :         default:
     104             :                 return 0;
     105             :         }
     106             : 
     107             :         /*!!watchout!! asp2D.width is 0 if stroke not visible due to painting properties - we must override this
     108             :         for picking*/
     109         617 :         if (check_stroke==1) {
     110           0 :                 asp->pen_props.width = tr_state->svg_props->stroke_width ? tr_state->svg_props->stroke_width->value : 0;
     111             :         }
     112         617 :         if (!asp->pen_props.width) check_stroke = 0;
     113             : 
     114         137 :         if (check_stroke) {
     115             :                 /*rough estimation of stroke bounding box to avoid fetching the stroke each time*/
     116         137 :                 if (!inside) {
     117             :                         Fixed width = asp->pen_props.width;
     118         118 :                         rc.x -= width;
     119         118 :                         rc.y += width;
     120         118 :                         rc.width += 2*width;
     121         118 :                         rc.height += 2*width;
     122         118 :                         inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;
     123             :                         if (!inside) return 0;
     124             :                 }
     125         480 :         } else if (!inside) {
     126             :                 return 0;
     127             :         }
     128             : 
     129          40 :         if (check_vis) {
     130          40 :                 if (*tr_state->svg_props->visibility!=SVG_VISIBILITY_VISIBLE) return 0;
     131             :         }
     132             : 
     133          40 :         if (check_fill) {
     134             :                 /*painted or don't care about fill*/
     135          40 :                 if ((check_fill!=2) || asp->fill_texture || asp->fill_color) {
     136          23 :                         if (glyph_rc) return 1;
     137             :                         /*point is over path*/
     138          21 :                         if (gf_path_point_over(drawable->path, x, y)) return 1;
     139             :                 }
     140             :         }
     141          28 :         if (check_stroke) {
     142             :                 /*not painted or don't care about stroke*/
     143          17 :                 if ((check_stroke!=2) || asp->line_texture || asp->line_color) {
     144             :                         StrikeInfo2D *si;
     145          17 :                         if (glyph_rc) return 1;
     146          17 :                         si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, asp, tr_state->appear, NULL, 0, NULL);
     147             :                         /*point is over outline*/
     148          17 :                         if (si && si->outline && gf_path_point_over(si->outline, x, y))
     149             :                                 return 1;
     150             :                 }
     151             :         }
     152             :         return 0;
     153             : }
     154             : 
     155             : 
     156          10 : void svg_clone_use_stack(GF_Compositor *compositor, GF_TraverseState *tr_state)
     157             : {
     158             :         u32 i, count;
     159          10 :         count = gf_list_count(tr_state->use_stack);
     160          10 :         gf_list_reset(compositor->hit_use_stack);
     161          10 :         for (i=0; i<count; i++) {
     162           0 :                 GF_Node *node = gf_list_get(tr_state->use_stack, i);
     163           0 :                 gf_list_add(compositor->hit_use_stack, node);
     164             :         }
     165          10 : }
     166             : 
     167             : #ifndef GPAC_DISABLE_3D
     168             : 
     169           0 : void svg_drawable_3d_pick(Drawable *drawable, GF_TraverseState *tr_state, DrawAspect2D *asp)
     170             : {
     171             :         SFVec3f local_pt, world_pt, vdiff;
     172             :         SFVec3f hit_normal;
     173             :         SFVec2f text_coords;
     174             :         u32 i, count;
     175             :         Fixed sqdist;
     176             :         Bool node_is_over;
     177             :         GF_Compositor *compositor;
     178             :         GF_Matrix mx;
     179             :         GF_Ray r;
     180             : 
     181           0 :         compositor = tr_state->visual->compositor;
     182             : 
     183           0 :         r = tr_state->ray;
     184           0 :         gf_mx_copy(mx, tr_state->model_matrix);
     185           0 :         gf_mx_inverse(&mx);
     186           0 :         gf_mx_apply_ray(&mx, &r);
     187             : 
     188             :         /*if we already have a hit point don't check anything below...*/
     189           0 :         if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) {
     190             :                 GF_Plane p;
     191             :                 GF_BBox box;
     192           0 :                 SFVec3f hit = compositor->hit_world_point;
     193           0 :                 gf_mx_apply_vec(&mx, &hit);
     194           0 :                 p.normal = r.dir;
     195           0 :                 p.d = -1 * gf_vec_dot(p.normal, hit);
     196           0 :                 gf_bbox_from_rect(&box, &drawable->path->bbox);
     197             : 
     198           0 :                 if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) {
     199           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node)));
     200           0 :                         return;
     201             :                 }
     202             :         }
     203             :         node_is_over = 0;
     204           0 :         if (compositor_get_2d_plane_intersection(&r, &local_pt)) {
     205           0 :                 node_is_over = svg_drawable_is_over(drawable, local_pt.x, local_pt.y, asp, tr_state, NULL);
     206             :         }
     207             : 
     208           0 :         if (!node_is_over) return;
     209             : 
     210             :         hit_normal.x = hit_normal.y = 0;
     211             :         hit_normal.z = FIX_ONE;
     212           0 :         text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2;
     213           0 :         text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2;
     214             : 
     215             :         /*check distance from user and keep the closest hitpoint*/
     216           0 :         world_pt = local_pt;
     217           0 :         gf_mx_apply_vec(&tr_state->model_matrix, &world_pt);
     218             : 
     219           0 :         for (i=0; i<tr_state->num_clip_planes; i++) {
     220           0 :                 if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) {
     221           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node)));
     222             :                         return;
     223             :                 }
     224             :         }
     225             : 
     226           0 :         gf_vec_diff(vdiff, world_pt, tr_state->ray.orig);
     227           0 :         sqdist = gf_vec_lensq(vdiff);
     228           0 :         if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) {
     229           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is farther (%g) than current pick (%g)\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node), FIX2FLT(sqdist), FIX2FLT(compositor->hit_square_dist)));
     230             :                 return;
     231             :         }
     232             : 
     233           0 :         compositor->hit_square_dist = sqdist;
     234             : 
     235             :         /*also stack any VRML sensors present at the current level. If the event is not catched
     236             :         by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
     237           0 :         gf_list_reset(compositor->sensors);
     238           0 :         count = gf_list_count(tr_state->vrml_sensors);
     239           0 :         for (i=0; i<count; i++) {
     240           0 :                 gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
     241             :         }
     242             : 
     243           0 :         gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
     244           0 :         gf_mx_copy(compositor->hit_local_to_world, mx);
     245           0 :         compositor->hit_local_point = local_pt;
     246           0 :         compositor->hit_world_point = world_pt;
     247           0 :         compositor->hit_world_ray = tr_state->ray;
     248           0 :         compositor->hit_normal = hit_normal;
     249           0 :         compositor->hit_texcoords = text_coords;
     250             : 
     251           0 :         svg_clone_use_stack(compositor, tr_state);
     252             :         /*not use in SVG patterns*/
     253           0 :         compositor->hit_appear = NULL;
     254           0 :         compositor->hit_node = drawable->node;
     255           0 :         compositor->hit_text = NULL;
     256           0 :         compositor->hit_use_dom_events = 1;
     257             : 
     258           0 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node),
     259             :                                               FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z)));
     260             : }
     261             : 
     262             : #endif
     263             : 
     264         339 : void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state)
     265             : {
     266             :         DrawAspect2D asp;
     267             :         GF_Matrix2D inv_2d;
     268             :         Fixed x, y;
     269             :         Bool picked = 0;
     270         339 :         GF_Compositor *compositor = tr_state->visual->compositor;
     271             :         SVGPropertiesPointers backup_props;
     272             :         GF_Matrix2D backup_matrix;
     273             :         GF_Matrix mx_3d;
     274             :         SVGAllAttributes all_atts;
     275             : 
     276         339 :         if (!drawable->path) return;
     277             : 
     278         339 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     279             : 
     280         339 :         memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers));
     281         339 :         gf_svg_apply_inheritance(&all_atts, tr_state->svg_props);
     282         339 :         if (compositor_svg_is_display_off(tr_state->svg_props)) return;
     283             : 
     284         339 :         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     285             : 
     286             :         memset(&asp, 0, sizeof(DrawAspect2D));
     287         339 :         drawable_get_aspect_2d_svg(node, &asp, tr_state);
     288             : 
     289             : #ifndef GPAC_DISABLE_3D
     290         339 :         if (tr_state->visual->type_3d) {
     291           0 :                 svg_drawable_3d_pick(drawable, tr_state, &asp);
     292           0 :                 compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     293           0 :                 memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     294             :                 return;
     295             :         }
     296             : #endif
     297         339 :         gf_mx2d_copy(inv_2d, tr_state->transform);
     298         339 :         gf_mx2d_inverse(&inv_2d);
     299         339 :         x = tr_state->ray.orig.x;
     300         339 :         y = tr_state->ray.orig.y;
     301         339 :         gf_mx2d_apply_coords(&inv_2d, &x, &y);
     302             : 
     303         339 :         picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL);
     304             : 
     305         339 :         if (picked) {
     306             :                 u32 count, i;
     307          10 :                 compositor->hit_local_point.x = x;
     308          10 :                 compositor->hit_local_point.y = y;
     309          10 :                 compositor->hit_local_point.z = 0;
     310             : 
     311          10 :                 gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
     312          10 :                 gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);
     313             : 
     314          10 :                 compositor->hit_node = drawable->node;
     315          10 :                 compositor->hit_use_dom_events = 1;
     316          10 :                 compositor->hit_normal.x = compositor->hit_normal.y = 0;
     317          10 :                 compositor->hit_normal.z = FIX_ONE;
     318          10 :                 compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2;
     319          10 :                 compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2;
     320          10 :                 svg_clone_use_stack(compositor, tr_state);
     321             :                 /*not use in SVG patterns*/
     322          10 :                 compositor->hit_appear = NULL;
     323             : 
     324             :                 /*also stack any VRML sensors present at the current level. If the event is not catched
     325             :                 by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
     326          10 :                 gf_list_reset(tr_state->visual->compositor->sensors);
     327          10 :                 count = gf_list_count(tr_state->vrml_sensors);
     328          10 :                 for (i=0; i<count; i++) {
     329           0 :                         gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
     330             :                 }
     331             : 
     332          10 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y)));
     333             :         }
     334             : 
     335         339 :         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     336         339 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     337             : }
     338             : 
     339       12991 : static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy,
     340             :                                   void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *),
     341             :                                   Bool is_svg_rect, Bool is_svg_path)
     342             : {
     343             :         GF_Matrix2D backup_matrix;
     344             :         GF_Matrix mx_3d;
     345             :         DrawableContext *ctx;
     346             :         SVGPropertiesPointers backup_props;
     347             :         u32 backup_flags;
     348       12991 :         Drawable *drawable = (Drawable *)gf_node_get_private(node);
     349             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     350             :         SVGAllAttributes all_atts;
     351             : 
     352       12991 :         if (is_destroy) {
     353             : #if USE_GF_PATH
     354             :                 /* The path is the same as the one in the SVG node, don't delete it here */
     355         960 :                 if (is_svg_path) drawable->path = NULL;
     356             : #endif
     357         960 :                 drawable_node_del(node);
     358         960 :                 return;
     359             :         }
     360             :         assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D);
     361             : 
     362             : 
     363       12031 :         if (tr_state->traversing_mode==TRAVERSE_PICK) {
     364         335 :                 svg_drawable_pick(node, drawable, tr_state);
     365         335 :                 return;
     366             :         }
     367             : 
     368             :         /*flatten attributes and apply animations + inheritance*/
     369       11696 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     370       11696 :         if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
     371             :                 return;
     372             : 
     373             :         /* Recreates the path (i.e the shape) only if the node is dirty */
     374       11696 :         if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) {
     375             :                 /*the rebuild function is responsible for cleaning the path*/
     376        2191 :                 rebuild_path(node, drawable, &all_atts);
     377        2191 :                 gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY);
     378        2191 :                 drawable_mark_modified(drawable, tr_state);
     379             :         }
     380       11696 :         if (drawable->path) {
     381       11696 :                 if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) {
     382       11621 :                         if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) {
     383        1102 :                                 drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO;
     384        1102 :                                 drawable_mark_modified(drawable, tr_state);
     385             :                         }
     386             :                 } else {
     387          75 :                         if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) {
     388           2 :                                 drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
     389           2 :                                 drawable_mark_modified(drawable, tr_state);
     390             :                         }
     391             :                 }
     392             :         }
     393             : 
     394       11696 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     395          19 :                 if (! compositor_svg_is_display_off(tr_state->svg_props)) {
     396             :                         DrawAspect2D asp;
     397          19 :                         gf_path_get_bounds(drawable->path, &tr_state->bounds);
     398          19 :                         if (!tr_state->ignore_strike) {
     399             :                                 memset(&asp, 0, sizeof(DrawAspect2D));
     400          19 :                                 drawable_get_aspect_2d_svg(node, &asp, tr_state);
     401          19 :                                 if (asp.pen_props.width) {
     402          19 :                                         StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL);
     403          19 :                                         if (si && si->outline) {
     404          19 :                                                 gf_path_get_bounds(si->outline, &tr_state->bounds);
     405             :                                         }
     406             :                                 }
     407             :                         }
     408          19 :                         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL);
     409          19 :                         if (!tr_state->abort_bounds_traverse)
     410          19 :                                 gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds);
     411          19 :                         gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL);
     412             : 
     413          19 :                         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL);
     414             :                 }
     415       11677 :         } else if (tr_state->traversing_mode == TRAVERSE_SORT) {
     416             :                 /*reset our flags - this may break reuse of nodes and change-detection in dirty-rect algo */
     417       11677 :                 gf_node_dirty_clear(node, 0);
     418             : 
     419       23296 :                 if (!compositor_svg_is_display_off(tr_state->svg_props) &&
     420       11619 :                         ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) {
     421             : 
     422       11469 :                         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     423             : 
     424       11469 :                         ctx = drawable_init_context_svg(drawable, tr_state);
     425       11469 :                         if (ctx) {
     426       11469 :                                 if (is_svg_rect) {
     427        4537 :                                         if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {}
     428        4537 :                                         else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {}
     429        2913 :                                         else if (ctx->transform.m[1] || ctx->transform.m[3]) {}
     430             :                                         else {
     431        2809 :                                                 ctx->flags &= ~CTX_IS_TRANSPARENT;
     432        2809 :                                                 if (!ctx->aspect.pen_props.width)
     433        2356 :                                                         ctx->flags |= CTX_NO_ANTIALIAS;
     434             :                                         }
     435             :                                 }
     436             : 
     437       11469 :                                 if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE)
     438         150 :                                         ctx->aspect.pen_props.path_length = all_atts.pathLength->value;
     439             : 
     440             : #ifndef GPAC_DISABLE_3D
     441       11469 :                                 if (tr_state->visual->type_3d) {
     442           9 :                                         if (!drawable->mesh) {
     443           9 :                                                 drawable->mesh = new_mesh();
     444           9 :                                                 if (drawable->path) mesh_from_path(drawable->mesh, drawable->path);
     445             :                                         }
     446           9 :                                         visual_3d_draw_from_context(ctx, tr_state);
     447           9 :                                         ctx->drawable = NULL;
     448             :                                 } else
     449             : #endif
     450             :                                 {
     451       11460 :                                         drawable_finalize_sort(ctx, tr_state, NULL);
     452             :                                 }
     453             :                         }
     454       11469 :                         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     455             :                 }
     456             :         }
     457             : 
     458       11696 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     459       11696 :         tr_state->svg_flags = backup_flags;
     460             : }
     461             : 
     462          36 : static GF_Err svg_rect_add_arc(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed cx, Fixed cy, Fixed rx, Fixed ry)
     463             : {
     464             :         Fixed angle, start_angle, end_angle, sweep, _vx, _vy, start_x, start_y;
     465             :         s32 i, num_steps;
     466             : 
     467          36 :         if (!gp->n_points) return GF_BAD_PARAM;
     468             : 
     469          36 :         start_x = gp->points[gp->n_points-1].x;
     470          36 :         start_y = gp->points[gp->n_points-1].y;
     471             : 
     472             :         //start angle and end angle
     473          36 :         start_angle = gf_atan2(start_y-cy, start_x-cx);
     474          36 :         end_angle = gf_atan2(end_y-cy, end_x-cx);
     475          36 :         sweep = end_angle - start_angle;
     476             : 
     477          36 :         if (sweep<0) sweep += 2*GF_PI;
     478             : 
     479             :         num_steps = 16;
     480         612 :         for (i=1; i<=num_steps; i++) {
     481         576 :                 angle = start_angle + sweep*i/num_steps;
     482         576 :                 _vx = cx + gf_mulfix(rx, gf_cos(angle));
     483         576 :                 _vy = cy + gf_mulfix(ry, gf_sin(angle));
     484         576 :                 gf_path_add_line_to(gp, _vx, _vy);
     485             :         }
     486             :         return GF_OK;
     487             : }
     488             : 
     489        1009 : static void svg_rect_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     490             : {
     491        1009 :         Fixed rx = (atts->rx ? atts->rx->value : 0);
     492        1009 :         Fixed ry = (atts->ry ? atts->ry->value : 0);
     493        1009 :         Fixed x = (atts->x ? atts->x->value : 0);
     494        1009 :         Fixed y = (atts->y ? atts->y->value : 0);
     495        1009 :         Fixed width = (atts->width ? atts->width->value : 0);
     496        1009 :         Fixed height = (atts->height ? atts->height->value : 0);
     497             : 
     498        1009 :         drawable_reset_path(stack);
     499        1009 :         if (!width || !height) return;
     500             : 
     501             :         /*we follow SVG 1.1 and not 1.2 !!*/
     502        1008 :         if (rx || ry) {
     503             :                 Fixed cx, cy;
     504           9 :                 if (rx >= width/2) rx = width/2;
     505           9 :                 if (ry >= height/2) ry = height/2;
     506           9 :                 if (rx == 0) rx = ry;
     507           9 :                 if (ry == 0) ry = rx;
     508           9 :                 gf_path_add_move_to(stack->path, x+rx, y);
     509             : 
     510           9 :                 if (width-rx!=rx)
     511           0 :                         gf_path_add_line_to(stack->path, x+width-rx, y);
     512             : 
     513           9 :                 cx = x+width-rx;
     514           9 :                 cy = y+ry;
     515           9 :                 svg_rect_add_arc(stack->path, x+width, y+ry, cx, cy, rx, ry);
     516             : 
     517           9 :                 if (height-ry!=ry)
     518           5 :                         gf_path_add_line_to(stack->path, x+width, y+height-ry);
     519             : 
     520             :                 cx = x+width-rx;
     521           9 :                 cy = y+height-ry;
     522           9 :                 svg_rect_add_arc(stack->path, x+width-rx, y+height, cx, cy, rx, ry);
     523             : 
     524           9 :                 if (width-rx!=rx)
     525           0 :                         gf_path_add_line_to(stack->path, x+rx, y+height);
     526             : 
     527             :                 cx = x+rx;
     528             :                 cy = y+height-ry;
     529           9 :                 svg_rect_add_arc(stack->path, x, y+height-ry, cx, cy, rx, ry);
     530             : 
     531           9 :                 if (height-ry!=ry)
     532           5 :                         gf_path_add_line_to(stack->path, x, y+ry);
     533             : 
     534             :                 cx = x+rx;
     535             :                 cy = y+ry;
     536           9 :                 svg_rect_add_arc(stack->path, x+rx, y, cx, cy, rx, ry);
     537             : 
     538           9 :                 gf_path_close(stack->path);
     539             :         } else {
     540         999 :                 gf_path_add_move_to(stack->path, x, y);
     541         999 :                 gf_path_add_line_to(stack->path, x+width, y);
     542         999 :                 gf_path_add_line_to(stack->path, x+width, y+height);
     543         999 :                 gf_path_add_line_to(stack->path, x, y+height);
     544         999 :                 gf_path_close(stack->path);
     545             :         }
     546             : }
     547             : 
     548        4709 : static void svg_traverse_rect(GF_Node *node, void *rs, Bool is_destroy)
     549             : {
     550        4709 :         svg_drawable_traverse(node, rs, is_destroy, svg_rect_rebuild, 1, 0);
     551        4709 : }
     552             : 
     553          75 : void compositor_init_svg_rect(GF_Compositor *compositor, GF_Node *node)
     554             : {
     555          75 :         drawable_stack_new(compositor, node);
     556          75 :         gf_node_set_callback_function(node, svg_traverse_rect);
     557          75 : }
     558             : 
     559          22 : static void svg_circle_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     560             : {
     561          22 :         Fixed r = 2*(atts->r ? atts->r->value : 0);
     562          22 :         drawable_reset_path(stack);
     563          22 :         gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0), (atts->cy ? atts->cy->value : 0), r, r);
     564          22 : }
     565             : 
     566         565 : static void svg_traverse_circle(GF_Node *node, void *rs, Bool is_destroy)
     567             : {
     568         565 :         svg_drawable_traverse(node, rs, is_destroy, svg_circle_rebuild, 0, 0);
     569         565 : }
     570             : 
     571          23 : void compositor_init_svg_circle(GF_Compositor *compositor, GF_Node *node)
     572             : {
     573          23 :         drawable_stack_new(compositor, node);
     574          23 :         gf_node_set_callback_function(node, svg_traverse_circle);
     575          23 : }
     576             : 
     577          26 : static void svg_ellipse_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     578             : {
     579          26 :         drawable_reset_path(stack);
     580         129 :         gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0),
     581          26 :                             (atts->cy ? atts->cy->value : 0),
     582          52 :                             (atts->rx ? 2*atts->rx->value : 0),
     583          51 :                             (atts->ry ? 2*atts->ry->value : 0));
     584          26 : }
     585         524 : static void svg_traverse_ellipse(GF_Node *node, void *rs, Bool is_destroy)
     586             : {
     587         524 :         svg_drawable_traverse(node, rs, is_destroy, svg_ellipse_rebuild, 0, 0);
     588         524 : }
     589             : 
     590          26 : void compositor_init_svg_ellipse(GF_Compositor *compositor, GF_Node *node)
     591             : {
     592          26 :         drawable_stack_new(compositor, node);
     593          26 :         gf_node_set_callback_function(node, svg_traverse_ellipse);
     594          26 : }
     595             : 
     596         168 : static void svg_line_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     597             : {
     598         168 :         drawable_reset_path(stack);
     599         168 :         gf_path_add_move_to(stack->path, (atts->x1 ? atts->x1->value : 0), (atts->y1 ? atts->y1->value : 0));
     600         168 :         gf_path_add_line_to(stack->path, (atts->x2 ? atts->x2->value : 0), (atts->y2 ? atts->y2->value : 0));
     601         168 : }
     602        1320 : static void svg_traverse_line(GF_Node *node, void *rs, Bool is_destroy)
     603             : {
     604        1320 :         svg_drawable_traverse(node, rs, is_destroy, svg_line_rebuild, 0, 0);
     605        1320 : }
     606             : 
     607          19 : void compositor_init_svg_line(GF_Compositor *compositor, GF_Node *node)
     608             : {
     609          19 :         drawable_stack_new(compositor, node);
     610          19 :         gf_node_set_callback_function(node, svg_traverse_line);
     611          19 : }
     612             : 
     613          12 : static void svg_polyline_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     614             : {
     615             :         u32 i, nbPoints;
     616          12 :         drawable_reset_path(stack);
     617          12 :         if (atts->points)
     618          12 :                 nbPoints = gf_list_count(*atts->points);
     619             :         else
     620             :                 nbPoints = 0;
     621             : 
     622          12 :         if (nbPoints) {
     623          12 :                 SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
     624          12 :                 gf_path_add_move_to(stack->path, p->x, p->y);
     625          63 :                 for (i = 1; i < nbPoints; i++) {
     626          51 :                         p = (SVG_Point *)gf_list_get(*atts->points, i);
     627          51 :                         gf_path_add_line_to(stack->path, p->x, p->y);
     628             :                 }
     629             :         } else {
     630           0 :                 gf_path_add_move_to(stack->path, 0, 0);
     631             :         }
     632          12 : }
     633         777 : static void svg_traverse_polyline(GF_Node *node, void *rs, Bool is_destroy)
     634             : {
     635         777 :         svg_drawable_traverse(node, rs, is_destroy, svg_polyline_rebuild, 0, 0);
     636         777 : }
     637             : 
     638          12 : void compositor_init_svg_polyline(GF_Compositor *compositor, GF_Node *node)
     639             : {
     640          12 :         drawable_stack_new(compositor, node);
     641          12 :         gf_node_set_callback_function(node, svg_traverse_polyline);
     642          12 : }
     643             : 
     644          31 : static void svg_polygon_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     645             : {
     646             :         u32 i, nbPoints;
     647          31 :         drawable_reset_path(stack);
     648          31 :         if (atts->points)
     649          31 :                 nbPoints = gf_list_count(*atts->points);
     650             :         else
     651             :                 nbPoints = 0;
     652             : 
     653          31 :         if (nbPoints) {
     654          31 :                 SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
     655          31 :                 gf_path_add_move_to(stack->path, p->x, p->y);
     656         139 :                 for (i = 1; i < nbPoints; i++) {
     657         108 :                         p = (SVG_Point *)gf_list_get(*atts->points, i);
     658         108 :                         gf_path_add_line_to(stack->path, p->x, p->y);
     659             :                 }
     660             :         } else {
     661           0 :                 gf_path_add_move_to(stack->path, 0, 0);
     662             :         }
     663             :         /*according to the spec, the polygon path is closed*/
     664          31 :         gf_path_close(stack->path);
     665          31 : }
     666        1408 : static void svg_traverse_polygon(GF_Node *node, void *rs, Bool is_destroy)
     667             : {
     668        1408 :         svg_drawable_traverse(node, rs, is_destroy, svg_polygon_rebuild, 0, 0);
     669        1408 : }
     670             : 
     671          31 : void compositor_init_svg_polygon(GF_Compositor *compositor, GF_Node *node)
     672             : {
     673          31 :         drawable_stack_new(compositor, node);
     674          31 :         gf_node_set_callback_function(node, svg_traverse_polygon);
     675          31 : }
     676             : 
     677             : 
     678         923 : static void svg_path_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
     679             : {
     680             : #if USE_GF_PATH
     681         923 :         drawable_reset_path_outline(stack);
     682         923 :         stack->path = atts->d;
     683             : #else
     684             :         drawable_reset_path(stack);
     685             :         gf_svg_path_build(stack->path, atts->d->commands, atts->d->points);
     686             : #endif
     687         923 : }
     688             : 
     689        3688 : static void svg_traverse_path(GF_Node *node, void *rs, Bool is_destroy)
     690             : {
     691        3688 :         svg_drawable_traverse(node, rs, is_destroy, svg_path_rebuild, 0, 1);
     692        3688 : }
     693             : 
     694         774 : void compositor_init_svg_path(GF_Compositor *compositor, GF_Node *node)
     695             : {
     696         774 :         Drawable *dr = drawable_stack_new(compositor, node);
     697         774 :         gf_path_del(dr->path);
     698         774 :         dr->path = NULL;
     699         774 :         gf_node_set_callback_function(node, svg_traverse_path);
     700         774 : }
     701             : 
     702             : #endif
     703             : 
     704             : 

Generated by: LCOV version 1.13