Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Cyril Concolato, Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2004-2019
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / SVG Scene Graph 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 <gpac/internal/scenegraph_dev.h>
27 :
28 : #ifndef GPAC_DISABLE_SVG
29 : #include <gpac/nodes_svg.h>
30 :
31 : GF_EXPORT
32 24622 : Bool gf_svg_is_animation_tag(u32 tag)
33 : {
34 49244 : return (tag == TAG_SVG_set ||
35 24622 : tag == TAG_SVG_animate ||
36 38008 : tag == TAG_SVG_animateColor ||
37 19004 : tag == TAG_SVG_animateTransform ||
38 37274 : tag == TAG_SVG_animateMotion ||
39 18637 : tag == TAG_SVG_discard
40 43259 : ) ? 1 : 0;
41 : }
42 :
43 13845 : Bool gf_svg_is_timing_tag(u32 tag)
44 : {
45 13845 : if (gf_svg_is_animation_tag(tag)) return 1;
46 13287 : else return (tag == TAG_SVG_animation ||
47 13287 : tag == TAG_SVG_audio ||
48 13287 : tag == TAG_LSR_conditional ||
49 26346 : tag == TAG_LSR_updates ||
50 39633 : tag == TAG_SVG_video)? GF_TRUE : GF_FALSE;
51 : }
52 :
53 5179 : SVG_Element *gf_svg_create_node(u32 ElementTag)
54 : {
55 : SVG_Element *p;
56 5179 : if (gf_svg_is_timing_tag(ElementTag)) {
57 : SVGTimedAnimBaseElement *tap;
58 338 : GF_SAFEALLOC(tap, SVGTimedAnimBaseElement);
59 : p = (SVG_Element *)tap;
60 4841 : } else if (ElementTag == TAG_SVG_handler) {
61 : SVG_handlerElement *hdl;
62 59 : GF_SAFEALLOC(hdl, SVG_handlerElement);
63 : p = (SVG_Element *)hdl;
64 : } else {
65 4782 : GF_SAFEALLOC(p, SVG_Element);
66 : }
67 5179 : if (!p) return NULL;
68 5179 : gf_node_setup((GF_Node *)p, ElementTag);
69 5179 : gf_sg_parent_setup((GF_Node *) p);
70 5179 : return p;
71 : }
72 :
73 5179 : void gf_svg_node_del(GF_Node *node)
74 : {
75 : SVG_Element *p = (SVG_Element *)node;
76 :
77 5179 : if (p->sgprivate->interact && p->sgprivate->interact->animations) {
78 49 : gf_smil_anim_delete_animations((GF_Node *)p);
79 : }
80 5179 : if (p->sgprivate->tag==TAG_SVG_listener) {
81 : /*remove from target's listener list*/
82 68 : gf_dom_event_remove_listener_from_parent((GF_DOMEventTarget *)node->sgprivate->UserPrivate, (GF_Node *)p);
83 : }
84 : /*if this is a handler with a UserPrivate, this is a handler with an implicit listener
85 : (eg handler with ev:event=""). Destroy the associated listener*/
86 5179 : if (p->sgprivate->tag==TAG_SVG_handler) {
87 59 : GF_Node *listener = p->sgprivate->UserPrivate;
88 59 : if (listener && (listener->sgprivate->tag==TAG_SVG_listener)) {
89 : GF_FieldInfo info;
90 0 : if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, 0, 0, &info) == GF_OK) {
91 0 : XMLRI *iri = (XMLRI *)info.far_ptr;
92 0 : if (iri->target) {
93 : assert(iri->target==p);
94 0 : iri->target = NULL;
95 : }
96 : }
97 0 : gf_node_unregister(listener, NULL);
98 : // gf_svg_node_del(listener);
99 : }
100 : }
101 : /*remove this node from associated listeners*/
102 5179 : if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
103 : u32 i, count;
104 28 : count = gf_dom_listener_count(node);
105 52 : for (i=0; i<count; i++) {
106 24 : GF_Node *listener = (GF_Node *)gf_list_get(node->sgprivate->interact->dom_evt->listeners, i);
107 24 : listener->sgprivate->UserPrivate = NULL;
108 : }
109 : }
110 :
111 5179 : if (gf_svg_is_timing_tag(node->sgprivate->tag)) {
112 : SVGTimedAnimBaseElement *tap = (SVGTimedAnimBaseElement *)node;
113 338 : if (tap->animp) {
114 67 : gf_free(tap->animp);
115 67 : gf_smil_anim_remove_from_target((GF_Node *)tap, (GF_Node *)tap->xlinkp->href->target);
116 : }
117 338 : if (tap->timingp) {
118 80 : gf_smil_timing_delete_runtime_info((GF_Node *)tap, tap->timingp->runtime);
119 80 : gf_free(tap->timingp);
120 : }
121 338 : if (tap->xlinkp) gf_free(tap->xlinkp);
122 : }
123 :
124 5179 : gf_node_delete_attributes(node);
125 5179 : gf_sg_parent_reset(node);
126 5179 : gf_node_free(node);
127 5179 : }
128 :
129 32928 : Bool gf_svg_node_init(GF_Node *node)
130 : {
131 32928 : switch (node->sgprivate->tag) {
132 7 : case TAG_SVG_script:
133 7 : if (node->sgprivate->scenegraph->script_load)
134 3 : node->sgprivate->scenegraph->script_load(node);
135 : return 1;
136 :
137 0 : case TAG_SVG_handler:
138 0 : if (node->sgprivate->scenegraph->script_load)
139 0 : node->sgprivate->scenegraph->script_load(node);
140 0 : if (node->sgprivate->scenegraph->script_action)
141 0 : ((SVG_handlerElement*)node)->handle_event = gf_sg_handle_dom_event;
142 : return 1;
143 1 : case TAG_LSR_conditional:
144 1 : gf_smil_timing_init_runtime_info(node);
145 1 : gf_smil_setup_events(node);
146 1 : return 1;
147 68 : case TAG_SVG_animateMotion:
148 : case TAG_SVG_set:
149 : case TAG_SVG_animate:
150 : case TAG_SVG_animateColor:
151 : case TAG_SVG_animateTransform:
152 68 : gf_smil_anim_init_node(node);
153 68 : gf_smil_setup_events(node);
154 : /*we may get called several times depending on xlink:href resoling for events*/
155 68 : return (node->sgprivate->UserPrivate || node->sgprivate->UserCallback) ? 1 : 0;
156 4 : case TAG_SVG_audio:
157 : case TAG_SVG_video:
158 : case TAG_LSR_updates:
159 4 : gf_smil_timing_init_runtime_info(node);
160 4 : gf_smil_setup_events(node);
161 : /*we may get called several times depending on xlink:href resoling for events*/
162 4 : return (node->sgprivate->UserPrivate || node->sgprivate->UserCallback) ? 1 : 0;
163 7 : case TAG_SVG_animation:
164 7 : gf_smil_timing_init_runtime_info(node);
165 7 : gf_smil_setup_events(node);
166 7 : return 0;
167 : /*discard is implemented as a special animation element */
168 0 : case TAG_SVG_discard:
169 0 : gf_smil_anim_init_discard(node);
170 0 : gf_smil_setup_events(node);
171 0 : return 1;
172 : default:
173 : return 0;
174 : }
175 : return 0;
176 : }
177 :
178 61235 : Bool gf_svg_node_changed(GF_Node *node, GF_FieldInfo *field)
179 : {
180 61235 : switch (node->sgprivate->tag) {
181 8 : case TAG_SVG_animateMotion:
182 : case TAG_SVG_discard:
183 : case TAG_SVG_set:
184 : case TAG_SVG_animate:
185 : case TAG_SVG_animateColor:
186 : case TAG_SVG_animateTransform:
187 : case TAG_LSR_conditional:
188 8 : gf_smil_timing_modified(node, field);
189 8 : return 1;
190 0 : case TAG_SVG_animation:
191 : case TAG_SVG_audio:
192 : case TAG_SVG_video:
193 : case TAG_LSR_updates:
194 0 : gf_smil_timing_modified(node, field);
195 : /*used by compositors*/
196 0 : return 0;
197 : }
198 : return 0;
199 : }
200 :
201 :
202 : #if 0 //unused
203 : void gf_svg_reset_path(SVG_PathData d)
204 : {
205 : #if USE_GF_PATH
206 : gf_path_reset(&d);
207 : #else
208 : u32 i, count;
209 : count = gf_list_count(d.commands);
210 : for (i = 0; i < count; i++) {
211 : u8 *command = (u8 *)gf_list_get(d.commands, i);
212 : gf_free(command);
213 : }
214 : gf_list_del(d.commands);
215 : count = gf_list_count(d.points);
216 : for (i = 0; i < count; i++) {
217 : SVG_Point *pt = (SVG_Point *)gf_list_get(d.points, i);
218 : gf_free(pt);
219 : }
220 : gf_list_del(d.points);
221 : #endif
222 : }
223 : #endif
224 :
225 :
226 : /* TODO: update for elliptical arcs */
227 : #if USE_GF_PATH
228 0 : void gf_svg_path_build(GF_Path *path, GF_List *commands, GF_List *points)
229 : {
230 : u32 i, j, command_count;
231 : SVG_Point orig, ct_orig, ct_end, end, *tmp;
232 0 : command_count = gf_list_count(commands);
233 : orig.x = orig.y = ct_orig.x = ct_orig.y = 0;
234 :
235 0 : for (i=0, j=0; i<command_count; i++) {
236 0 : u8 *command = (u8 *)gf_list_get(commands, i);
237 0 : switch (*command) {
238 0 : case SVG_PATHCOMMAND_M: /* Move To */
239 0 : tmp = (SVG_Point*)gf_list_get(points, j);
240 0 : orig = *tmp;
241 0 : gf_path_add_move_to(path, orig.x, orig.y);
242 0 : j++;
243 : /*provision for nextCurveTo when no curve is specified:
244 : "If there is no previous command or if the previous command was not an C, c, S or s,
245 : assume the first control point is coincident with the current point.
246 : */
247 : ct_orig = orig;
248 0 : break;
249 0 : case SVG_PATHCOMMAND_L: /* Line To */
250 0 : tmp = (SVG_Point*)gf_list_get(points, j);
251 0 : end = *tmp;
252 :
253 0 : gf_path_add_line_to(path, end.x, end.y);
254 0 : j++;
255 : orig = end;
256 : /*cf above*/
257 : ct_orig = orig;
258 0 : break;
259 0 : case SVG_PATHCOMMAND_C: /* Curve To */
260 0 : tmp = (SVG_Point*)gf_list_get(points, j);
261 0 : ct_orig = *tmp;
262 0 : tmp = (SVG_Point*)gf_list_get(points, j+1);
263 0 : ct_end = *tmp;
264 0 : tmp = (SVG_Point*)gf_list_get(points, j+2);
265 0 : end = *tmp;
266 0 : gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
267 : ct_orig = ct_end;
268 : orig = end;
269 0 : j+=3;
270 0 : break;
271 0 : case SVG_PATHCOMMAND_S: /* Next Curve To */
272 0 : ct_orig.x = 2*orig.x - ct_orig.x;
273 0 : ct_orig.y = 2*orig.y - ct_orig.y;
274 0 : tmp = (SVG_Point*)gf_list_get(points, j);
275 0 : ct_end = *tmp;
276 0 : tmp = (SVG_Point*)gf_list_get(points, j+1);
277 0 : end = *tmp;
278 0 : gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
279 : ct_orig = ct_end;
280 : orig = end;
281 0 : j+=2;
282 0 : break;
283 0 : case SVG_PATHCOMMAND_Q: /* Quadratic Curve To */
284 0 : tmp = (SVG_Point*)gf_list_get(points, j);
285 0 : ct_orig = *tmp;
286 0 : tmp = (SVG_Point*)gf_list_get(points, j+1);
287 0 : end = *tmp;
288 0 : gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y);
289 : orig = end;
290 0 : j+=2;
291 0 : break;
292 0 : case SVG_PATHCOMMAND_T: /* Next Quadratic Curve To */
293 0 : ct_orig.x = 2*orig.x - ct_orig.x;
294 0 : ct_orig.y = 2*orig.y - ct_orig.y;
295 0 : tmp = (SVG_Point*)gf_list_get(points, j);
296 0 : end = *tmp;
297 0 : gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y);
298 : orig = end;
299 0 : j++;
300 0 : break;
301 0 : case SVG_PATHCOMMAND_Z: /* Close */
302 0 : gf_path_close(path);
303 0 : break;
304 : }
305 : }
306 0 : }
307 : #endif
308 :
309 :
310 526 : void gf_smil_delete_times(GF_List *list)
311 : {
312 : u32 i, count;
313 526 : count = gf_list_count(list);
314 690 : for (i = 0; i < count; i++) {
315 164 : SMIL_Time *v = (SMIL_Time *)gf_list_get(list, i);
316 164 : if (v->element_id) gf_free(v->element_id);
317 164 : gf_free(v);
318 : }
319 526 : gf_list_del(list);
320 526 : }
321 :
322 : #if 0 //unused
323 : void gf_svg_delete_points(GF_List *list)
324 : {
325 : u32 i, count = gf_list_count(list);
326 : for (i = 0; i < count; i++) {
327 : SVG_Point *p = (SVG_Point *)gf_list_get(list, i);
328 : gf_free(p);
329 : }
330 : gf_list_del(list);
331 : }
332 :
333 : void gf_svg_delete_coordinates(GF_List *list)
334 : {
335 : u32 i, count = gf_list_count(list);
336 : for (i = 0; i < count; i++) {
337 : SVG_Coordinate *c = (SVG_Coordinate *)gf_list_get(list, i);
338 : gf_free(c);
339 : }
340 : gf_list_del(list);
341 : }
342 : #endif
343 :
344 2913 : void gf_svg_reset_iri(GF_SceneGraph *sg, XMLRI *iri)
345 : {
346 2913 : if (!iri) return;
347 2913 : if (iri->string) gf_free(iri->string);
348 2913 : gf_node_unregister_iri(sg, iri);
349 : }
350 :
351 2584 : void gf_svg_delete_paint(GF_SceneGraph *sg, SVG_Paint *paint)
352 : {
353 2584 : if (!paint) return;
354 : //always free since we may allocate the iri to ""
355 2584 : if (sg) gf_svg_reset_iri(sg, &paint->iri);
356 2584 : gf_free(paint);
357 : }
358 :
359 194 : static void svg_delete_one_anim_value(u8 anim_datatype, void *anim_value, GF_SceneGraph *sg)
360 : {
361 : /* TODO: handle specific animation types : Motion, else ? */
362 623 : gf_svg_delete_attribute_value(anim_datatype, anim_value, sg);
363 194 : }
364 :
365 109 : void gf_svg_reset_animate_values(SMIL_AnimateValues anim_values, GF_SceneGraph *sg)
366 : {
367 : u32 i, count;
368 109 : u8 type = anim_values.type;
369 109 : if (anim_values.laser_strings) type = DOM_String_datatype;
370 :
371 109 : count = gf_list_count(anim_values.values);
372 538 : for (i = 0; i < count; i++) {
373 429 : void *value = gf_list_get(anim_values.values, i);
374 : svg_delete_one_anim_value(type, value, sg);
375 : }
376 109 : gf_list_del(anim_values.values);
377 : anim_values.values = NULL;
378 109 : }
379 :
380 : #if 0 //unused
381 : void gf_svg_reset_animate_value(SMIL_AnimateValue anim_value, GF_SceneGraph *sg)
382 : {
383 : svg_delete_one_anim_value(anim_value.type, anim_value.value, sg);
384 : anim_value.value = NULL;
385 : }
386 : #endif
387 :
388 14123 : void gf_svg_delete_attribute_value(u32 type, void *value, GF_SceneGraph *sg)
389 : {
390 : GF_List *l;
391 14123 : switch (type) {
392 2278 : case SVG_Paint_datatype:
393 2278 : gf_svg_delete_paint(sg, (SVG_Paint *)value);
394 2278 : break;
395 573 : case XMLRI_datatype:
396 : case XML_IDREF_datatype:
397 573 : gf_svg_reset_iri(sg, (XMLRI *)value);
398 573 : gf_free(value);
399 573 : break;
400 62 : case SVG_Focus_datatype:
401 62 : gf_svg_reset_iri(sg, & ((SVG_Focus*)value)->target);
402 62 : gf_free(value);
403 62 : break;
404 1089 : case SVG_PathData_datatype:
405 : #if USE_GF_PATH
406 1089 : gf_path_del((GF_Path *)value);
407 : #else
408 : gf_free(value);
409 : #endif
410 1089 : break;
411 3677 : case SVG_ID_datatype:
412 : case DOM_String_datatype:
413 : case SVG_ContentType_datatype:
414 : case SVG_LanguageID_datatype:
415 3677 : if (*(SVG_String *)value) gf_free(*(SVG_String *)value);
416 3677 : gf_free(value);
417 3677 : break;
418 34 : case SVG_StrokeDashArray_datatype:
419 34 : if (((SVG_StrokeDashArray*)value)->array.vals) gf_free(((SVG_StrokeDashArray*)value)->array.vals);
420 34 : if (((SVG_StrokeDashArray*)value)->array.units) gf_free(((SVG_StrokeDashArray*)value)->array.units);
421 34 : gf_free(value);
422 34 : break;
423 757 : case SMIL_KeyTimes_datatype:
424 : case SMIL_KeyPoints_datatype:
425 : case SMIL_KeySplines_datatype:
426 : case SVG_Numbers_datatype:
427 : case SVG_Coordinates_datatype:
428 : case SVG_Points_datatype:
429 757 : l = *(GF_List**)value;
430 2764 : while (gf_list_count(l)) {
431 1250 : void *n = gf_list_last(l);
432 1250 : gf_list_rem_last(l);
433 1250 : gf_free(n);
434 : }
435 757 : gf_list_del(l);
436 757 : gf_free(value);
437 757 : break;
438 136 : case SVG_FontFamily_datatype:
439 : {
440 : SVG_FontFamily *ff = (SVG_FontFamily *)value;
441 136 : if (ff->value) gf_free(ff->value);
442 136 : gf_free(value);
443 : }
444 136 : break;
445 232 : case SMIL_AttributeName_datatype:
446 : {
447 : SMIL_AttributeName *an = (SMIL_AttributeName *)value;
448 232 : if (an->name) gf_free(an->name);
449 232 : gf_free(value);
450 : }
451 232 : break;
452 526 : case SMIL_Times_datatype:
453 526 : gf_smil_delete_times(*(SMIL_Times *)value);
454 526 : gf_free(value);
455 526 : break;
456 194 : case SMIL_AnimateValue_datatype:
457 194 : svg_delete_one_anim_value(((SMIL_AnimateValue *)value)->type, ((SMIL_AnimateValue *)value)->value, sg);
458 194 : gf_free(value);
459 194 : break;
460 109 : case SMIL_AnimateValues_datatype:
461 109 : gf_svg_reset_animate_values(*((SMIL_AnimateValues *)value), sg);
462 109 : gf_free(value);
463 109 : break;
464 19 : case DOM_StringList_datatype:
465 19 : l = *(GF_List**)value;
466 62 : while (gf_list_count(l)) {
467 24 : char *n = gf_list_last(l);
468 24 : gf_list_rem_last(l);
469 24 : gf_free(n);
470 : }
471 19 : gf_list_del(l);
472 19 : gf_free(value);
473 19 : break;
474 54 : case XMLRI_List_datatype:
475 54 : l = *(GF_List**)value;
476 227 : while (gf_list_count(l)) {
477 119 : XMLRI *r = gf_list_last(l);
478 119 : gf_list_rem_last(l);
479 119 : if (r->string) gf_free(r->string);
480 119 : gf_free(r);
481 : }
482 54 : gf_list_del(l);
483 54 : gf_free(value);
484 54 : break;
485 :
486 2 : case 0:
487 2 : if (*(SVG_String *)value) gf_free(*(SVG_String *)value);
488 2 : gf_free(value);
489 2 : break;
490 4381 : case SMIL_RepeatCount_datatype:
491 : case SMIL_Duration_datatype:
492 : case SVG_Length_datatype:
493 : case SVG_Coordinate_datatype:
494 : case SVG_Visibility_datatype:
495 : case SVG_Display_datatype:
496 : default:
497 4381 : gf_free(value);
498 4381 : break;
499 : }
500 14123 : }
501 :
502 : #if 0 //unused
503 : void gf_smil_delete_key_types(GF_List *l)
504 : {
505 : while (gf_list_count(l)) {
506 : Fixed *t = (Fixed *)gf_list_get(l, 0);
507 : gf_list_rem(l, 0);
508 : gf_free(t);
509 : }
510 : gf_list_del(l);
511 : }
512 : #endif
513 :
514 :
515 : #endif /*GPAC_DISABLE_SVG*/
|