root/src/compositor/svg_geometry.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. svg_drawable_is_over
  2. svg_drawable_is_over
  3. svg_clone_use_stack
  4. svg_drawable_3d_pick
  5. svg_drawable_pick
  6. svg_drawable_traverse
  7. svg_rect_add_arc
  8. svg_rect_rebuild
  9. svg_traverse_rect
  10. compositor_init_svg_rect
  11. svg_circle_rebuild
  12. svg_traverse_circle
  13. compositor_init_svg_circle
  14. svg_ellipse_rebuild
  15. svg_traverse_ellipse
  16. compositor_init_svg_ellipse
  17. svg_line_rebuild
  18. svg_traverse_line
  19. compositor_init_svg_line
  20. svg_polyline_rebuild
  21. svg_traverse_polyline
  22. compositor_init_svg_polyline
  23. svg_polygon_rebuild
  24. svg_traverse_polygon
  25. compositor_init_svg_polygon
  26. svg_path_rebuild
  27. svg_traverse_path
  28. compositor_init_svg_path

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Cyril Concolato - Jean le Feuvre
 *                      Copyright (c) Telecom ParisTech 2005-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / Scene Compositor sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "visual_manager.h"
#include "nodes_stacks.h"

#ifdef GPAC_DISABLE_SVG

Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
{
        return 0;
}

#else

Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
{
        u32 check_fill, check_stroke;
        Bool check_over, check_outline, check_vis, inside;
        GF_Rect rc;
        u8 ptr_evt;

        ptr_evt = *tr_state->svg_props->pointer_events;

        if (ptr_evt==SVG_POINTEREVENTS_NONE) {
                return 0;
        }

        if (glyph_rc) {
                rc = *glyph_rc;
        } else {
                gf_path_get_bounds(drawable->path, &rc);
        }
        inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;

        if (ptr_evt==SVG_POINTEREVENTS_BOUNDINGBOX) return inside;

        check_fill = check_stroke = check_over = check_outline = check_vis = 0;
        /*
        check_vis: if set, return FALSE when visible property is not "visible"
        check_fill:
                if 1, checks whether point is over path,
                if 2, checks if the path is painted (even with fill-opacity=0) before
        check_stroke:
                if 1, checks whether point is over path outline,
                if 2, checks if the path outline is painted (even with stroke-opacity=0) before
        */
        switch (ptr_evt) {
        case SVG_POINTEREVENTS_VISIBLE:
                check_vis = 1;
                check_fill = 1;
                check_stroke = 1;
                break;
        case SVG_POINTEREVENTS_VISIBLEFILL:
                check_vis = 1;
                check_fill = 1;
                break;
        case SVG_POINTEREVENTS_VISIBLESTROKE:
                check_vis = 1;
                check_stroke = 1;
                break;
        case SVG_POINTEREVENTS_VISIBLEPAINTED:
                check_vis = 1;
                check_fill = 2;
                check_stroke = 2;
                break;
        case SVG_POINTEREVENTS_FILL:
                check_fill = 1;
                break;
        case SVG_POINTEREVENTS_STROKE:
                check_stroke = 1;
                break;
        case SVG_POINTEREVENTS_ALL:
                check_fill = 1;
                check_stroke = 1;
                break;
        case SVG_POINTEREVENTS_PAINTED:
                check_fill = 2;
                check_stroke = 2;
                break;
        default:
                return 0;
        }

        /*!!watchout!! asp2D.width is 0 if stroke not visible due to painting properties - we must override this
        for picking*/
        if (check_stroke==1) {
                asp->pen_props.width = tr_state->svg_props->stroke_width ? tr_state->svg_props->stroke_width->value : 0;
        }
        if (!asp->pen_props.width) check_stroke = 0;

        if (check_stroke) {
                /*rough estimation of stroke bounding box to avoid fetching the stroke each time*/
                if (!inside) {
                        Fixed width = asp->pen_props.width;
                        rc.x -= width;
                        rc.y += width;
                        rc.width += 2*width;
                        rc.height += 2*width;
                        inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;
                        if (!inside) return 0;
                }
        } else if (!inside) {
                return 0;
        }

        if (check_vis) {
                if (*tr_state->svg_props->visibility!=SVG_VISIBILITY_VISIBLE) return 0;
        }

        if (check_fill) {
                /*painted or don't care about fill*/
                if ((check_fill!=2) || asp->fill_texture || asp->fill_color) {
                        if (glyph_rc) return 1;
                        /*point is over path*/
                        if (gf_path_point_over(drawable->path, x, y)) return 1;
                }
        }
        if (check_stroke) {
                StrikeInfo2D *si;
                /*not painted or don't care about stroke*/
                if ((check_stroke!=2) || asp->line_texture || asp->line_color) {
                        if (glyph_rc) return 1;
                        si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, asp, tr_state->appear, NULL, 0, NULL);
                        /*point is over outline*/
                        if (si && si->outline && gf_path_point_over(si->outline, x, y))
                                return 1;
                }
        }
        return 0;
}


void svg_clone_use_stack(GF_Compositor *compositor, GF_TraverseState *tr_state)
{
        u32 i, count;
        count = gf_list_count(tr_state->use_stack);
        gf_list_reset(compositor->hit_use_stack);
        for (i=0; i<count; i++) {
                GF_Node *node = gf_list_get(tr_state->use_stack, i);
                gf_list_add(compositor->hit_use_stack, node);
        }
}

#ifndef GPAC_DISABLE_3D

void svg_drawable_3d_pick(Drawable *drawable, GF_TraverseState *tr_state, DrawAspect2D *asp)
{
        SFVec3f local_pt, world_pt, vdiff;
        SFVec3f hit_normal;
        SFVec2f text_coords;
        u32 i, count;
        Fixed sqdist;
        Bool node_is_over;
        GF_Compositor *compositor;
        GF_Matrix mx;
        GF_Ray r;

        compositor = tr_state->visual->compositor;

        r = tr_state->ray;
        gf_mx_copy(mx, tr_state->model_matrix);
        gf_mx_inverse(&mx);
        gf_mx_apply_ray(&mx, &r);

        /*if we already have a hit point don't check anything below...*/
        if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) {
                GF_Plane p;
                GF_BBox box;
                SFVec3f hit = compositor->hit_world_point;
                gf_mx_apply_vec(&mx, &hit);
                p.normal = r.dir;
                p.d = -1 * gf_vec_dot(p.normal, hit);
                gf_bbox_from_rect(&box, &drawable->path->bbox);

                if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) {
                        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)));
                        return;
                }
        }
        node_is_over = 0;
        if (compositor_get_2d_plane_intersection(&r, &local_pt)) {
                node_is_over = svg_drawable_is_over(drawable, local_pt.x, local_pt.y, asp, tr_state, NULL);
        }

        if (!node_is_over) return;

        hit_normal.x = hit_normal.y = 0;
        hit_normal.z = FIX_ONE;
        text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2;
        text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2;

        /*check distance from user and keep the closest hitpoint*/
        world_pt = local_pt;
        gf_mx_apply_vec(&tr_state->model_matrix, &world_pt);

        for (i=0; i<tr_state->num_clip_planes; i++) {
                if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 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)));
                        return;
                }
        }

        gf_vec_diff(vdiff, world_pt, tr_state->ray.orig);
        sqdist = gf_vec_lensq(vdiff);
        if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) {
                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)));
                return;
        }

        compositor->hit_square_dist = sqdist;

        /*also stack any VRML sensors present at the current level. If the event is not catched
        by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
        gf_list_reset(compositor->sensors);
        count = gf_list_count(tr_state->vrml_sensors);
        for (i=0; i<count; i++) {
                gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
        }

        gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
        gf_mx_copy(compositor->hit_local_to_world, mx);
        compositor->hit_local_point = local_pt;
        compositor->hit_world_point = world_pt;
        compositor->hit_world_ray = tr_state->ray;
        compositor->hit_normal = hit_normal;
        compositor->hit_texcoords = text_coords;

        svg_clone_use_stack(compositor, tr_state);
        /*not use in SVG patterns*/
        compositor->hit_appear = NULL;
        compositor->hit_node = drawable->node;
        compositor->hit_text = NULL;
        compositor->hit_use_dom_events = 1;

        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),
                                              FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z)));
}

#endif

void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state)
{
        DrawAspect2D asp;
        GF_Matrix2D inv_2d;
        Fixed x, y;
        Bool picked = 0;
        GF_Compositor *compositor = tr_state->visual->compositor;
        SVGPropertiesPointers backup_props;
        GF_Matrix2D backup_matrix;
        GF_Matrix mx_3d;
        SVGAllAttributes all_atts;

        if (!drawable->path) return;

        gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);

        memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers));
        gf_svg_apply_inheritance(&all_atts, tr_state->svg_props);
        if (compositor_svg_is_display_off(tr_state->svg_props)) return;

        compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);

        memset(&asp, 0, sizeof(DrawAspect2D));
        drawable_get_aspect_2d_svg(node, &asp, tr_state);

#ifndef GPAC_DISABLE_3D
        if (tr_state->visual->type_3d) {
                svg_drawable_3d_pick(drawable, tr_state, &asp);
                compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
                memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
                return;
        }
#endif
        gf_mx2d_copy(inv_2d, tr_state->transform);
        gf_mx2d_inverse(&inv_2d);
        x = tr_state->ray.orig.x;
        y = tr_state->ray.orig.y;
        gf_mx2d_apply_coords(&inv_2d, &x, &y);

        picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL);

        if (picked) {
                u32 count, i;
                compositor->hit_local_point.x = x;
                compositor->hit_local_point.y = y;
                compositor->hit_local_point.z = 0;

                gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
                gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);

                compositor->hit_node = drawable->node;
                compositor->hit_use_dom_events = 1;
                compositor->hit_normal.x = compositor->hit_normal.y = 0;
                compositor->hit_normal.z = FIX_ONE;
                compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2;
                compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2;
                svg_clone_use_stack(compositor, tr_state);
                /*not use in SVG patterns*/
                compositor->hit_appear = NULL;

                /*also stack any VRML sensors present at the current level. If the event is not catched
                by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
                gf_list_reset(tr_state->visual->compositor->sensors);
                count = gf_list_count(tr_state->vrml_sensors);
                for (i=0; i<count; i++) {
                        gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
                }

                GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y)));
        }

        compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
        memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
}

static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy,
                                  void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *),
                                  Bool is_svg_rect, Bool is_svg_path)
{
        GF_Matrix2D backup_matrix;
        GF_Matrix mx_3d;
        DrawableContext *ctx;
        SVGPropertiesPointers backup_props;
        u32 backup_flags;
        Drawable *drawable = (Drawable *)gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState *)rs;
        SVGAllAttributes all_atts;

        if (is_destroy) {
#if USE_GF_PATH
                /* The path is the same as the one in the SVG node, don't delete it here */
                if (is_svg_path) drawable->path = NULL;
#endif
                drawable_node_del(node);
                return;
        }
        assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D);


        if (tr_state->traversing_mode==TRAVERSE_PICK) {
                svg_drawable_pick(node, drawable, tr_state);
                return;
        }

        /*flatten attributes and apply animations + inheritance*/
        gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
        if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
                return;

        /* Recreates the path (i.e the shape) only if the node is dirty */
        if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) {
                /*the rebuild function is responsible for cleaning the path*/
                rebuild_path(node, drawable, &all_atts);
                gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY);
                drawable_mark_modified(drawable, tr_state);
        }
        if (drawable->path) {
                if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) {
                        if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) {
                                drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO;
                                drawable_mark_modified(drawable, tr_state);
                        }
                } else {
                        if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) {
                                drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
                                drawable_mark_modified(drawable, tr_state);
                        }
                }
        }

        if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
                if (! compositor_svg_is_display_off(tr_state->svg_props)) {
                        DrawAspect2D asp;
                        gf_path_get_bounds(drawable->path, &tr_state->bounds);
                        if (!tr_state->ignore_strike) {
                                memset(&asp, 0, sizeof(DrawAspect2D));
                                drawable_get_aspect_2d_svg(node, &asp, tr_state);
                                if (asp.pen_props.width) {
                                        StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL);
                                        if (si && si->outline) {
                                                gf_path_get_bounds(si->outline, &tr_state->bounds);
                                        }
                                }
                        }
                        compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL);
                        if (!tr_state->abort_bounds_traverse)
                                gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds);
                        gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL);

                        compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL);
                }
        } else if (tr_state->traversing_mode == TRAVERSE_SORT) {
                /*reset our flags - this may break reuse of nodes and change-detection in dirty-rect algo */
                gf_node_dirty_clear(node, 0);

                if (!compositor_svg_is_display_off(tr_state->svg_props) &&
                        ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) {

                        compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);

                        ctx = drawable_init_context_svg(drawable, tr_state);
                        if (ctx) {
                                if (is_svg_rect) {
                                        if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {}
                                        else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {}
                                        else if (ctx->transform.m[1] || ctx->transform.m[3]) {}
                                        else {
                                                ctx->flags &= ~CTX_IS_TRANSPARENT;
                                                if (!ctx->aspect.pen_props.width)
                                                        ctx->flags |= CTX_NO_ANTIALIAS;
                                        }
                                }

                                if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE)
                                        ctx->aspect.pen_props.path_length = all_atts.pathLength->value;

#ifndef GPAC_DISABLE_3D
                                if (tr_state->visual->type_3d) {
                                        if (!drawable->mesh) {
                                                drawable->mesh = new_mesh();
                                                if (drawable->path) mesh_from_path(drawable->mesh, drawable->path);
                                        }
                                        visual_3d_draw_from_context(ctx, tr_state);
                                        ctx->drawable = NULL;
                                } else
#endif
                                {
                                        drawable_finalize_sort(ctx, tr_state, NULL);
                                }
                        }
                        compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
                }
        }

        memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
        tr_state->svg_flags = backup_flags;
}

static GF_Err svg_rect_add_arc(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed cx, Fixed cy, Fixed rx, Fixed ry)
{
        Fixed angle, start_angle, end_angle, sweep, _vx, _vy, start_x, start_y;
        s32 i, num_steps;

        if (!gp->n_points) return GF_BAD_PARAM;

        start_x = gp->points[gp->n_points-1].x;
        start_y = gp->points[gp->n_points-1].y;

        //start angle and end angle
        start_angle = gf_atan2(start_y-cy, start_x-cx);
        end_angle = gf_atan2(end_y-cy, end_x-cx);
        sweep = end_angle - start_angle;

        if (sweep<0) sweep += 2*GF_PI;

        num_steps = 16;
        for (i=1; i<=num_steps; i++) {
                angle = start_angle + sweep*i/num_steps;
                _vx = cx + gf_mulfix(rx, gf_cos(angle));
                _vy = cy + gf_mulfix(ry, gf_sin(angle));
                gf_path_add_line_to(gp, _vx, _vy);
        }
        return GF_OK;
}

static void svg_rect_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        Fixed rx = (atts->rx ? atts->rx->value : 0);
        Fixed ry = (atts->ry ? atts->ry->value : 0);
        Fixed x = (atts->x ? atts->x->value : 0);
        Fixed y = (atts->y ? atts->y->value : 0);
        Fixed width = (atts->width ? atts->width->value : 0);
        Fixed height = (atts->height ? atts->height->value : 0);

        drawable_reset_path(stack);
        if (!width || !height) return;

        /*we follow SVG 1.1 and not 1.2 !!*/
        if (rx || ry) {
                Fixed cx, cy;
                if (rx >= width/2) rx = width/2;
                if (ry >= height/2) ry = height/2;
                if (rx == 0) rx = ry;
                if (ry == 0) ry = rx;
                gf_path_add_move_to(stack->path, x+rx, y);

                if (width-rx!=rx)
                        gf_path_add_line_to(stack->path, x+width-rx, y);

                cx = x+width-rx;
                cy = y+ry;
                svg_rect_add_arc(stack->path, x+width, y+ry, cx, cy, rx, ry);

                if (height-ry!=ry)
                        gf_path_add_line_to(stack->path, x+width, y+height-ry);

                cx = x+width-rx;
                cy = y+height-ry;
                svg_rect_add_arc(stack->path, x+width-rx, y+height, cx, cy, rx, ry);

                if (width-rx!=rx)
                        gf_path_add_line_to(stack->path, x+rx, y+height);

                cx = x+rx;
                cy = y+height-ry;
                svg_rect_add_arc(stack->path, x, y+height-ry, cx, cy, rx, ry);

                if (height-ry!=ry)
                        gf_path_add_line_to(stack->path, x, y+ry);

                cx = x+rx;
                cy = y+ry;
                svg_rect_add_arc(stack->path, x+rx, y, cx, cy, rx, ry);

                gf_path_close(stack->path);
        } else {
                gf_path_add_move_to(stack->path, x, y);
                gf_path_add_line_to(stack->path, x+width, y);
                gf_path_add_line_to(stack->path, x+width, y+height);
                gf_path_add_line_to(stack->path, x, y+height);
                gf_path_close(stack->path);
        }
}

static void svg_traverse_rect(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_rect_rebuild, 1, 0);
}

void compositor_init_svg_rect(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_rect);
}

static void svg_circle_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        Fixed r = 2*(atts->r ? atts->r->value : 0);
        drawable_reset_path(stack);
        gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0), (atts->cy ? atts->cy->value : 0), r, r);
}

static void svg_traverse_circle(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_circle_rebuild, 0, 0);
}

void compositor_init_svg_circle(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_circle);
}

static void svg_ellipse_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        drawable_reset_path(stack);
        gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0),
                            (atts->cy ? atts->cy->value : 0),
                            (atts->rx ? 2*atts->rx->value : 0),
                            (atts->ry ? 2*atts->ry->value : 0));
}
static void svg_traverse_ellipse(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_ellipse_rebuild, 0, 0);
}

void compositor_init_svg_ellipse(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_ellipse);
}

static void svg_line_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        drawable_reset_path(stack);
        gf_path_add_move_to(stack->path, (atts->x1 ? atts->x1->value : 0), (atts->y1 ? atts->y1->value : 0));
        gf_path_add_line_to(stack->path, (atts->x2 ? atts->x2->value : 0), (atts->y2 ? atts->y2->value : 0));
}
static void svg_traverse_line(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_line_rebuild, 0, 0);
}

void compositor_init_svg_line(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_line);
}

static void svg_polyline_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        u32 i, nbPoints;
        drawable_reset_path(stack);
        if (atts->points)
                nbPoints = gf_list_count(*atts->points);
        else
                nbPoints = 0;

        if (nbPoints) {
                SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
                gf_path_add_move_to(stack->path, p->x, p->y);
                for (i = 1; i < nbPoints; i++) {
                        p = (SVG_Point *)gf_list_get(*atts->points, i);
                        gf_path_add_line_to(stack->path, p->x, p->y);
                }
        } else {
                gf_path_add_move_to(stack->path, 0, 0);
        }
}
static void svg_traverse_polyline(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_polyline_rebuild, 0, 0);
}

void compositor_init_svg_polyline(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_polyline);
}

static void svg_polygon_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
        u32 i, nbPoints;
        drawable_reset_path(stack);
        if (atts->points)
                nbPoints = gf_list_count(*atts->points);
        else
                nbPoints = 0;

        if (nbPoints) {
                SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
                gf_path_add_move_to(stack->path, p->x, p->y);
                for (i = 1; i < nbPoints; i++) {
                        p = (SVG_Point *)gf_list_get(*atts->points, i);
                        gf_path_add_line_to(stack->path, p->x, p->y);
                }
        } else {
                gf_path_add_move_to(stack->path, 0, 0);
        }
        /*according to the spec, the polygon path is closed*/
        gf_path_close(stack->path);
}
static void svg_traverse_polygon(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_polygon_rebuild, 0, 0);
}

void compositor_init_svg_polygon(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, svg_traverse_polygon);
}


static void svg_path_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
#if USE_GF_PATH
        drawable_reset_path_outline(stack);
        stack->path = atts->d;
#else
        drawable_reset_path(stack);
        gf_svg_path_build(stack->path, atts->d->commands, atts->d->points);
#endif
}

static void svg_traverse_path(GF_Node *node, void *rs, Bool is_destroy)
{
        svg_drawable_traverse(node, rs, is_destroy, svg_path_rebuild, 0, 1);
}

void compositor_init_svg_path(GF_Compositor *compositor, GF_Node *node)
{
        Drawable *dr = drawable_stack_new(compositor, node);
        gf_path_del(dr->path);
        dr->path = NULL;
        gf_node_set_callback_function(node, svg_traverse_path);
}

#endif



/* [<][>][^][v][top][bottom][index][help] */