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 :
|