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