Coverage Report

Created: 2025-12-14 06:51

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