Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Cyril Concolato
5 : * Copyright (c) Telecom ParisTech 2004-2012
6 : *
7 : * This file is part of GPAC / SVG Scene Graph sub-project
8 : *
9 : * GPAC is free software; you can redistribute it and/or modify
10 : * it under the terms of the GNU Lesser General Public License as published by
11 : * the Free Software Foundation; either version 2, or (at your option)
12 : * any later version.
13 : *
14 : * GPAC is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU Lesser General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU Lesser General Public
20 : * License along with this library; see the file COPYING. If not, write to
21 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 : *
23 : */
24 :
25 : #include <gpac/internal/scenegraph_dev.h>
26 : #include <gpac/nodes_svg.h>
27 :
28 : #ifndef GPAC_DISABLE_LOG
29 : u32 time_spent_in_anim = 0;
30 : #endif
31 :
32 : #ifndef GPAC_DISABLE_SVG
33 :
34 :
35 : /**************************************************************************************
36 : * Each GF_Node holds the (SVG/SMIL) animation elements which target itself in a list *
37 : * The following are the generic functions to manipulate this list: *
38 : * - add a new animation to the list, *
39 : * - get an animation from the list, *
40 : * - remove an animation from the list, *
41 : * - count the animations in the list, *
42 : * - delete the list *
43 : **************************************************************************************/
44 58 : GF_Err gf_node_animation_add(GF_Node *node, void *animation)
45 : {
46 58 : if (!node || !animation) return GF_BAD_PARAM;
47 58 : if (!node->sgprivate->interact) {
48 48 : GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
49 48 : if (!node->sgprivate->interact) return GF_OUT_OF_MEM;
50 : }
51 58 : if (!node->sgprivate->interact->animations) node->sgprivate->interact->animations = gf_list_new();
52 58 : return gf_list_add(node->sgprivate->interact->animations, animation);
53 : }
54 :
55 49 : GF_Err gf_node_animation_del(GF_Node *node)
56 : {
57 49 : if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_BAD_PARAM;
58 49 : gf_list_del(node->sgprivate->interact->animations);
59 49 : node->sgprivate->interact->animations = NULL;
60 49 : return GF_OK;
61 : }
62 :
63 41130 : u32 gf_node_animation_count(GF_Node *node)
64 : {
65 41130 : if (!node || !node->sgprivate->interact|| !node->sgprivate->interact->animations) return 0;
66 4646 : return gf_list_count(node->sgprivate->interact->animations);
67 : }
68 :
69 5929 : void *gf_node_animation_get(GF_Node *node, u32 i)
70 : {
71 5929 : if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return 0;
72 5929 : return gf_list_get(node->sgprivate->interact->animations, i);
73 : }
74 :
75 7 : GF_Err gf_node_animation_rem(GF_Node *node, u32 i)
76 : {
77 7 : if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_OK;
78 7 : return gf_list_rem(node->sgprivate->interact->animations, i);
79 : }
80 : /**************************************************************************************
81 : * End of Generic GF_Node animations list *
82 : **************************************************************************************/
83 :
84 :
85 : /**************************************************************************************
86 : * Helping functions for animation *
87 : **************************************************************************************/
88 : /* Sets the pointer to the attribute value with the pointer
89 : to the value which passed (if unspecified) */
90 0 : void gf_svg_attributes_resolve_unspecified(GF_FieldInfo *in, GF_FieldInfo *p, GF_FieldInfo *t)
91 : {
92 4678 : if (in->fieldType == 0) {
93 92 : if (p->fieldType == SVG_Transform_datatype) {
94 : /* if the input value is not specified, and the presentation value is of type Transform,
95 : then we should use the default identity transform instead of the presentation value */
96 0 : *in = *t;
97 : } else {
98 92 : *in = *p;
99 : }
100 : }
101 0 : }
102 :
103 : /* Replaces the pointer to the attribute value with the pointer
104 : to the value which is inherited (if inherited) */
105 0 : void gf_svg_attributes_resolve_inherit(GF_FieldInfo *in, GF_FieldInfo *prop)
106 : {
107 1608 : if (gf_svg_is_inherit(in)) *in = *prop;
108 0 : }
109 :
110 : /* Replaces the pointer to the attribute value with the pointer
111 : to the value of the color attribute (if the current value is set to currentColor) */
112 1608 : void gf_svg_attributes_resolve_currentColor(GF_FieldInfo *in, GF_FieldInfo *current_color)
113 : {
114 1608 : if ((in->fieldType == SVG_Paint_datatype) && gf_svg_is_current_color(in)) {
115 0 : *in = *current_color;
116 : }
117 1608 : }
118 :
119 : /**************************************************************************************
120 : * The main function doing evaluation of the animation is: gf_smil_anim_evaluate *
121 : * Depending on the timing status of the animation it calls: *
122 : * - gf_smil_anim_animate *
123 : * - gf_smil_anim_animate_with_fraction *
124 : * - gf_smil_anim_freeze *
125 : * - gf_smil_anim_remove *
126 : * *
127 : * The gf_smil_anim_animate consists in *
128 : * - interpolating using gf_smil_anim_compute_interpolation_value *
129 : * - accumulating using gf_smil_anim_apply_accumulate *
130 : * - applying additive behavior *
131 : * *
132 : * Depending on the animation attributes, one of the following functions is called *
133 : * by the function gf_smil_anim_compute_interpolation_value *
134 : * - gf_smil_anim_set *
135 : * - gf_smil_anim_animate_using_values *
136 : * - gf_smil_anim_animate_from_to *
137 : * - gf_smil_anim_animate_from_by *
138 : * - gf_smil_anim_animate_using_path *
139 : * *
140 : * In most animation methods, the important step in the animation is to resolve *
141 : * the inherit and currentColor values to perform further interpolation, i.e. calls: *
142 : * gf_svg_attributes_resolve_currentColor(&info, &rai->owner->current_color_value); *
143 : * gf_svg_attributes_resolve_inherit(&info, &rai->owner->parent_presentation_value); *
144 : * *
145 : **************************************************************************************/
146 0 : static void gf_smil_anim_set(SMIL_Anim_RTI *rai)
147 : {
148 : GF_FieldInfo to_info;
149 0 : SMILAnimationAttributesPointers *animp = rai->animp;
150 :
151 0 : if (!animp->to) {
152 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,
153 : ("[SMIL Animation] Animation %s - set element without to attribute\n",
154 : gf_node_get_log_name((GF_Node *)rai->anim_elt)));
155 0 : return;
156 : }
157 0 : if (!animp->to->type) {
158 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,
159 : ("[SMIL Animation] Animation %s - set element with an unparsed to attribute\n",
160 : gf_node_get_log_name((GF_Node *)rai->anim_elt)));
161 : return;
162 : }
163 :
164 0 : if (rai->change_detection_mode) {
165 : /* if the set has been applied, unless next animations are additive we don't need
166 : to apply it again */
167 0 : if (rai->previous_coef > 0) rai->interpolated_value_changed = 0;
168 0 : else rai->interpolated_value_changed = 1;
169 : return;
170 : } else {
171 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
172 : ("[SMIL Animation] Time %f - Animation %s - applying set animation\n",
173 : gf_node_get_scene_time((GF_Node*)rai->anim_elt),
174 : gf_node_get_log_name((GF_Node *)rai->anim_elt)));
175 :
176 0 : to_info.fieldType = animp->to->type;
177 0 : to_info.far_ptr = animp->to->value;
178 : /* we do not need to resolve currentColor values or inherit values here,
179 : because no further interpolation is required for the animation and
180 : because inheritance is applied after animations in the compositor. */
181 :
182 0 : gf_svg_attributes_copy(&rai->interpolated_value, &to_info, 0);
183 0 : rai->previous_coef = FIX_ONE;
184 : }
185 : }
186 :
187 2894 : static void gf_smil_anim_use_keypoints_keytimes(SMIL_Anim_RTI *rai, Fixed normalized_simple_time,
188 : Fixed *interpolation_coefficient, u32 *keyValueIndex)
189 : {
190 2894 : SMILAnimationAttributesPointers *animp = rai->animp;
191 : u32 keyTimeIndex = 0;
192 : Fixed interval_duration;
193 :
194 2894 : *interpolation_coefficient = normalized_simple_time;
195 :
196 : /* Computing new interpolation coefficient */
197 2894 : if (rai->key_times_count) {
198 : Fixed keyTimeBefore = 0, keyTimeAfter=0;
199 301 : for (keyTimeIndex = rai->previous_keytime_index; keyTimeIndex< rai->key_times_count; keyTimeIndex++) {
200 301 : Fixed *t = (Fixed *)gf_list_get(*animp->keyTimes, keyTimeIndex);
201 301 : if (normalized_simple_time < *t) {
202 : Fixed *tm1;
203 300 : rai->previous_keytime_index = keyTimeIndex;
204 300 : tm1 = (Fixed *) gf_list_get(*animp->keyTimes, keyTimeIndex-1);
205 300 : if (tm1) keyTimeBefore = *tm1;
206 : else keyTimeBefore = 0;
207 300 : keyTimeAfter = *t;
208 300 : break;
209 : }
210 : }
211 300 : keyTimeIndex--;
212 300 : interval_duration = keyTimeAfter - keyTimeBefore;
213 300 : if (keyValueIndex) *keyValueIndex = keyTimeIndex;
214 300 : if (interval_duration)
215 300 : *interpolation_coefficient = gf_divfix(normalized_simple_time - keyTimeBefore, interval_duration);
216 : else
217 0 : *interpolation_coefficient = FIX_ONE;
218 300 : if (!rai->change_detection_mode)
219 150 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
220 : ("[SMIL Animation] Time %f - Animation %s - Using Key Times: index %d, interval duration %.2f, coeff: %.2f\n",
221 : gf_node_get_scene_time((GF_Node*)rai->anim_elt),
222 : gf_node_get_log_name((GF_Node *)rai->anim_elt),
223 : keyTimeIndex,
224 : interval_duration,
225 : interpolation_coefficient));
226 : }
227 :
228 2894 : if (rai->anim_elt->sgprivate->tag == TAG_SVG_animateMotion && rai->key_points_count) {
229 : Fixed *p1;
230 1 : p1 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex);
231 1 : if (animp->calcMode && *animp->calcMode == SMIL_CALCMODE_DISCRETE) {
232 0 : *interpolation_coefficient = *p1;
233 : } else {
234 1 : Fixed *p2 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex+1);
235 2 : *interpolation_coefficient = gf_mulfix(FIX_ONE - *interpolation_coefficient, *p1)
236 1 : + gf_mulfix(*interpolation_coefficient, (p2 ? *p2 : *p1));
237 : }
238 1 : if (keyValueIndex) *keyValueIndex = 0;
239 1 : if (!rai->change_detection_mode)
240 1 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
241 : ("[SMIL Animation] Time %f - Animation %s - Using Key Points: key Point Index %d, coeff: %.2f\n",
242 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyTimeIndex, *interpolation_coefficient));
243 : }
244 2894 : }
245 :
246 2283 : static void gf_smil_anim_animate_using_values(SMIL_Anim_RTI *rai, Fixed normalized_simple_time)
247 : {
248 2283 : SMILAnimationAttributesPointers *animp = rai->animp;
249 : GF_List *values;
250 : GF_FieldInfo value_info, value_info_next;
251 : u32 keyValueIndex;
252 : Fixed interpolation_coefficient;
253 : u32 real_calcMode;
254 :
255 2283 : values = animp->values->values;
256 :
257 : memset(&value_info, 0, sizeof(GF_FieldInfo));
258 2283 : value_info.fieldType = animp->values->type;
259 2283 : value_info_next = value_info;
260 :
261 2283 : real_calcMode = (gf_svg_attribute_is_interpolatable(animp->values->type)?
262 2283 : (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR):
263 : SMIL_CALCMODE_DISCRETE
264 : );
265 :
266 2283 : if (rai->values_count == 1) {
267 0 : if (rai->change_detection_mode) {
268 : /* Since we have only 1 value, the previous key index should always be 0,
269 : unless the animation has not started or is reset (-1) */
270 0 : if (rai->previous_key_index == 0) rai->interpolated_value_changed = 0;
271 0 : else rai->interpolated_value_changed = 1;
272 0 : return;
273 : } else {
274 0 : value_info.far_ptr = gf_list_get(values, 0);
275 : /* no further interpolation needed
276 : therefore no need to resolve inherit and currentColor */
277 0 : gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0);
278 0 : rai->previous_key_index = 0;
279 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
280 : ("[SMIL Animation] Time %f - Animation %s - Using values[0] as interpolation value\n",
281 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
282 : return;
283 : }
284 : }
285 :
286 : /* Computing new key value index and interpolation coefficient */
287 2283 : if (!rai->key_times_count) {
288 1983 : if (real_calcMode == SMIL_CALCMODE_DISCRETE) {
289 1803 : if (normalized_simple_time == FIX_ONE) {
290 151 : keyValueIndex = rai->values_count-1;
291 151 : interpolation_coefficient = FIX_ONE;
292 : } else {
293 1652 : Fixed tmp = normalized_simple_time*rai->values_count;
294 1652 : Fixed tmp_floor = gf_floor(tmp);
295 1652 : if ((tmp - tmp_floor) == 0 && tmp) {
296 16 : keyValueIndex = FIX2INT(tmp_floor) - 1;
297 : } else {
298 1636 : keyValueIndex = FIX2INT(tmp_floor);
299 : }
300 1652 : interpolation_coefficient = tmp - INT2FIX(keyValueIndex);
301 : }
302 : } else {
303 180 : Fixed tmp = normalized_simple_time*(rai->values_count-1);
304 180 : if (normalized_simple_time == FIX_ONE) {
305 0 : keyValueIndex = rai->values_count-2;
306 : } else {
307 180 : keyValueIndex = FIX2INT(gf_floor(tmp));
308 : }
309 180 : interpolation_coefficient = tmp - INT2FIX(keyValueIndex);
310 : }
311 : //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - No KeyTimes: key index %d, coeff: %.2f\n", gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex, FIX2FLT(interpolation_coefficient)));
312 : } else {
313 300 : gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, &keyValueIndex);
314 : }
315 :
316 2283 : if (rai->change_detection_mode) {
317 2010 : if (real_calcMode == SMIL_CALCMODE_DISCRETE && rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef != -FIX_ONE) {
318 1737 : rai->interpolated_value_changed = 0;
319 273 : } else if (rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef == interpolation_coefficient)
320 0 : rai->interpolated_value_changed = 0;
321 : else
322 273 : rai->interpolated_value_changed = 1;
323 : } else {
324 273 : rai->previous_key_index = keyValueIndex;
325 273 : rai->previous_coef = interpolation_coefficient;
326 :
327 273 : switch (real_calcMode) {
328 33 : case SMIL_CALCMODE_DISCRETE:
329 33 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
330 : ("[SMIL Animation] Time %f - Animation %s - applying discrete animation using values (key value index: %d)\n",
331 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex));
332 33 : value_info.far_ptr = gf_list_get(values, keyValueIndex);
333 : /* no further interpolation needed
334 : therefore no need to resolve inherit and currentColor */
335 33 : gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0);
336 33 : break;
337 240 : case SMIL_CALCMODE_PACED:
338 : /* TODO: at the moment assume it is linear */
339 : case SMIL_CALCMODE_SPLINE:
340 : /* TODO: at the moment assume it is linear */
341 : case SMIL_CALCMODE_LINEAR:
342 240 : if (keyValueIndex == rai->values_count - 1) {
343 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
344 : ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (setting last key value: %d)\n",
345 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex));
346 0 : value_info.far_ptr = gf_list_get(values, rai->values_count - 1);
347 : /* no further interpolation needed
348 : therefore no need to resolve inherit and currentColor */
349 0 : gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0);
350 : } else {
351 :
352 240 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
353 : ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (key value indices: %d, %d / coeff: %f)\n",
354 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex, keyValueIndex+1, interpolation_coefficient));
355 240 : value_info.far_ptr = gf_list_get(values, keyValueIndex);
356 240 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) {
357 0 : gf_svg_attributes_resolve_currentColor(&value_info, &rai->owner->current_color_value);
358 0 : gf_svg_attributes_resolve_inherit(&value_info, &rai->owner->parent_presentation_value);
359 : }
360 :
361 240 : value_info_next.far_ptr = gf_list_get(values, keyValueIndex+1);
362 240 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) {
363 0 : gf_svg_attributes_resolve_currentColor(&value_info_next, &rai->owner->current_color_value);
364 0 : gf_svg_attributes_resolve_inherit(&value_info_next, &rai->owner->parent_presentation_value);
365 : }
366 :
367 240 : gf_svg_attributes_interpolate(&value_info,
368 : &value_info_next,
369 : &rai->interpolated_value,
370 : interpolation_coefficient, 1);
371 : }
372 : break;
373 : }
374 : }
375 : }
376 :
377 4883 : static void gf_smil_anim_animate_from_to(SMIL_Anim_RTI *rai, Fixed normalized_simple_time)
378 : {
379 : GF_FieldInfo from_info, to_info;
380 4883 : SMILAnimationAttributesPointers *animp = rai->animp;
381 : Fixed interpolation_coefficient;
382 4883 : s32 useFrom = (normalized_simple_time<=FIX_ONE/2);
383 : u32 real_calcMode;
384 :
385 9614 : real_calcMode = (animp->to && gf_svg_attribute_is_interpolatable(animp->to->type)?
386 9462 : (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR):
387 : SMIL_CALCMODE_DISCRETE
388 : );
389 :
390 4883 : if (rai->change_detection_mode) {
391 2589 : if (rai->previous_coef == normalized_simple_time)
392 2 : rai->interpolated_value_changed = 0;
393 : else {
394 2888 : if (real_calcMode == SMIL_CALCMODE_DISCRETE &&
395 301 : useFrom == rai->previous_key_index) {
396 298 : rai->interpolated_value_changed = 0;
397 : } else {
398 2289 : rai->interpolated_value_changed = 1;
399 : }
400 : }
401 : } else {
402 :
403 2294 : if (animp->from) {
404 2293 : from_info.fieldType = animp->from->type;
405 2293 : from_info.far_ptr = animp->from->value;
406 : } else {
407 1 : from_info.fieldType = 0;
408 1 : from_info.far_ptr = NULL;
409 : }
410 :
411 2294 : if (rai->is_first_anim)
412 : gf_svg_attributes_resolve_unspecified(&from_info,
413 1931 : &rai->owner->specified_value,
414 : &rai->default_transform_value);
415 : else
416 : gf_svg_attributes_resolve_unspecified(&from_info,
417 363 : &rai->owner->presentation_value,
418 : &rai->default_transform_value);
419 :
420 2294 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) {
421 790 : gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value);
422 790 : gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value);
423 : }
424 2294 : if (animp->to) {
425 2293 : to_info.fieldType = animp->to->type;
426 2293 : to_info.far_ptr = animp->to->value;
427 : } else {
428 1 : to_info.fieldType = 0;
429 1 : to_info.far_ptr = NULL;
430 : }
431 :
432 2294 : if (rai->is_first_anim)
433 : gf_svg_attributes_resolve_unspecified(&to_info,
434 1931 : &rai->owner->specified_value,
435 : &rai->default_transform_value);
436 : else
437 : gf_svg_attributes_resolve_unspecified(&to_info,
438 363 : &rai->owner->presentation_value,
439 : &rai->default_transform_value);
440 :
441 2294 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(to_info.fieldType)) {
442 790 : gf_svg_attributes_resolve_currentColor(&to_info, &rai->owner->current_color_value);
443 790 : gf_svg_attributes_resolve_inherit(&to_info, &rai->owner->parent_presentation_value);
444 : }
445 :
446 2294 : gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL);
447 :
448 2294 : rai->previous_coef = interpolation_coefficient;
449 :
450 2294 : switch (real_calcMode) {
451 3 : case SMIL_CALCMODE_DISCRETE:
452 : {
453 : /* before half of the duration stay at 'from' and then switch to 'to' */
454 3 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
455 : ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (using %s value)\n",
456 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), (useFrom?"from":"to")));
457 3 : gf_svg_attributes_copy(&rai->interpolated_value, (useFrom?&from_info:&to_info), 0);
458 3 : rai->previous_key_index = useFrom;
459 : }
460 3 : break;
461 2291 : case SMIL_CALCMODE_SPLINE:
462 : case SMIL_CALCMODE_PACED:
463 : case SMIL_CALCMODE_LINEAR:
464 : default:
465 2291 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
466 : ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (linear interpolation, using coefficient %f)\n",
467 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), interpolation_coefficient));
468 2291 : gf_svg_attributes_interpolate(&from_info, &to_info, &rai->interpolated_value, interpolation_coefficient, 1);
469 2291 : break;
470 : }
471 : }
472 4883 : }
473 :
474 180 : static void gf_smil_anim_animate_from_by(SMIL_Anim_RTI *rai, Fixed normalized_simple_time)
475 : {
476 : Fixed from_coef;
477 : GF_FieldInfo from_info, by_info;
478 180 : SMILAnimationAttributesPointers *animp = rai->animp;
479 180 : s32 useFrom = (normalized_simple_time<=FIX_ONE/2);
480 :
481 180 : if (rai->change_detection_mode) {
482 90 : if (rai->previous_coef == normalized_simple_time)
483 0 : rai->interpolated_value_changed = 0;
484 : else {
485 90 : if (animp->calcMode &&
486 0 : *animp->calcMode == SMIL_CALCMODE_DISCRETE &&
487 0 : useFrom == rai->previous_key_index) {
488 0 : rai->interpolated_value_changed = 0;
489 : } else {
490 90 : rai->interpolated_value_changed = 1;
491 : }
492 : }
493 : } else {
494 90 : rai->previous_coef = normalized_simple_time;
495 :
496 90 : if (animp->from) {
497 0 : from_info.fieldType = animp->from->type;
498 0 : from_info.far_ptr = animp->from->value;
499 : from_coef = FIX_ONE;
500 : } else {
501 90 : from_info.fieldType = 0;
502 90 : from_info.far_ptr = NULL;
503 : /* this is a by animation only, then, it is always additive,
504 : we don't need the from value*/
505 : from_coef = 0;
506 : }
507 :
508 90 : if (rai->is_first_anim)
509 : gf_svg_attributes_resolve_unspecified(&from_info,
510 0 : &rai->owner->specified_value,
511 : &rai->default_transform_value);
512 : else
513 : gf_svg_attributes_resolve_unspecified(&from_info,
514 90 : &rai->owner->presentation_value,
515 : &rai->default_transform_value);
516 :
517 90 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) {
518 0 : gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value);
519 0 : gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value);
520 : }
521 :
522 90 : if (animp->by) {
523 90 : by_info.fieldType = animp->by->type;
524 90 : by_info.far_ptr = animp->by->value;
525 : } else {
526 0 : by_info.fieldType = 0;
527 0 : by_info.far_ptr = NULL;
528 : }
529 :
530 90 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) {
531 0 : gf_svg_attributes_resolve_currentColor(&by_info, &rai->owner->current_color_value);
532 0 : gf_svg_attributes_resolve_inherit(&by_info, &rai->owner->parent_presentation_value);
533 : }
534 :
535 90 : switch ((animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR)) {
536 0 : case SMIL_CALCMODE_DISCRETE:
537 : {
538 : /* before half of the duration stay at 'from' and then switch to 'to' */
539 0 : if (useFrom) {
540 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
541 : ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from)",
542 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
543 0 : gf_svg_attributes_muladd(from_coef, &from_info, 0, &by_info, &rai->interpolated_value, 0);
544 : } else {
545 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
546 : ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from+by)",
547 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
548 0 : gf_svg_attributes_muladd(from_coef, &from_info, FIX_ONE, &by_info, &rai->interpolated_value, 0);
549 : }
550 0 : rai->previous_key_index = useFrom;
551 : }
552 0 : break;
553 90 : case SMIL_CALCMODE_SPLINE:
554 : case SMIL_CALCMODE_PACED:
555 : case SMIL_CALCMODE_LINEAR:
556 : default:
557 90 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
558 : ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (linear interpolation between from and from+by, coef: %f)\n",
559 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time));
560 90 : gf_svg_attributes_muladd(from_coef, &from_info, normalized_simple_time, &by_info, &rai->interpolated_value, 0);
561 90 : break;
562 : }
563 : }
564 180 : }
565 :
566 152 : static void gf_svg_compute_path_anim(SMIL_Anim_RTI *rai, GF_Matrix2D *m, Fixed normalized_simple_time)
567 : {
568 : Fixed offset;
569 152 : offset = gf_mulfix(normalized_simple_time, rai->length);
570 304 : gf_mx2d_init(*m);
571 :
572 152 : gf_path_iterator_get_transform(rai->path_iterator, offset, 1, m, 1, 0);
573 : //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("offset: %f, position: (%f, %f)", offset, ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[2], ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[5]));
574 152 : switch (rai->rotate) {
575 : case SVG_NUMBER_AUTO:
576 : break;
577 0 : case SVG_NUMBER_AUTO_REVERSE:
578 0 : gf_mx2d_add_rotation(m, m->m[2], m->m[5], GF_PI);
579 0 : break;
580 1 : default:
581 1 : m->m[0] = FIX_ONE;
582 1 : m->m[1] = 0;
583 1 : m->m[3] = 0;
584 1 : m->m[4] = FIX_ONE;
585 : }
586 152 : }
587 :
588 300 : static void gf_smil_anim_animate_using_path(SMIL_Anim_RTI *rai, Fixed normalized_simple_time)
589 : {
590 : Fixed interpolation_coefficient;
591 :
592 300 : gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL);
593 :
594 300 : if (rai->change_detection_mode) {
595 150 : if (rai->previous_coef == interpolation_coefficient)
596 0 : rai->interpolated_value_changed = 0;
597 : else {
598 150 : rai->interpolated_value_changed = 1;
599 : }
600 : } else {
601 150 : rai->previous_coef = interpolation_coefficient;
602 :
603 150 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
604 : ("[SMIL Animation] Time %f - Animation %s - applying path animation (coef: %f)\n",
605 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time));
606 :
607 150 : gf_svg_compute_path_anim(rai, (GF_Matrix2D*)rai->interpolated_value.far_ptr, interpolation_coefficient);
608 : }
609 300 : }
610 :
611 7646 : static void gf_smil_anim_compute_interpolation_value(SMIL_Anim_RTI *rai, Fixed normalized_simple_time)
612 : {
613 7646 : SMILAnimationAttributesPointers *animp = rai->animp;
614 :
615 7646 : if (rai->path) {
616 300 : gf_smil_anim_animate_using_path(rai, normalized_simple_time);
617 7346 : } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) {
618 0 : gf_smil_anim_set(rai);
619 7346 : } else if (rai->values_count) {
620 : /* Ignore 'from'/'to'/'by'*/
621 2283 : gf_smil_anim_animate_using_values(rai, normalized_simple_time);
622 5063 : } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) {
623 : /* 'to' is not specified but 'by' is, so this is a 'by' animation or a 'from'-'by' animation */
624 180 : gf_smil_anim_animate_from_by(rai, normalized_simple_time);
625 : } else {
626 : /* Ignore 'by' if specified */
627 4883 : gf_smil_anim_animate_from_to(rai, normalized_simple_time);
628 : }
629 :
630 : #ifndef GPAC_DISABLE_LOG
631 7646 : if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) {
632 0 : char *str = gf_svg_dump_attribute(rai->anim_elt, &rai->interpolated_value);
633 :
634 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - Interpolation value changed for attribute %s, new value: %s \n",
635 : gf_node_get_scene_time(rai->anim_elt), gf_node_get_log_name(rai->anim_elt),
636 : gf_svg_get_attribute_name(rai->anim_elt, rai->owner->presentation_value.fieldIndex), str)
637 : );
638 :
639 0 : if (str) gf_free(str);
640 : }
641 : #endif
642 7646 : }
643 :
644 68 : void gf_smil_anim_set_anim_runtime_in_timing(GF_Node *n)
645 : {
646 : u32 i, j;
647 : SVGTimedAnimBaseElement *timed_elt = NULL;
648 : SMIL_Timing_RTI *rti = NULL;
649 : GF_Node *target = NULL;
650 :
651 135 : if (!n) return;
652 : timed_elt = (SVGTimedAnimBaseElement *)n;
653 :
654 68 : if (!gf_svg_is_animation_tag(n->sgprivate->tag)) return;
655 :
656 68 : target = timed_elt->xlinkp->href->target;
657 68 : if (!target) return;
658 :
659 68 : if (timed_elt->timingp) rti = timed_elt->timingp->runtime;
660 68 : if (!rti) return;
661 :
662 68 : rti->rai = NULL;
663 :
664 90 : for (i = 0; i < gf_node_animation_count(target); i++) {
665 : SMIL_Anim_RTI *rai_tmp;
666 89 : SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i);
667 89 : j=0;
668 215 : while ((rai_tmp = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) {
669 104 : if (rai_tmp->timingp->runtime == rti) {
670 67 : rti->rai = rai_tmp;
671 67 : return;
672 : }
673 : }
674 : }
675 : }
676 :
677 67 : static void gf_smil_anim_get_last_specified_value(SMIL_Anim_RTI *rai)
678 : {
679 67 : SMILAnimationAttributesPointers *animp = rai->animp;
680 :
681 67 : if (!animp) return;
682 :
683 67 : if (rai->path) {
684 2 : if (!rai->last_specified_value.far_ptr) {
685 2 : rai->last_specified_value.far_ptr = gf_malloc(sizeof(GF_Matrix2D));
686 2 : rai->last_specified_value.fieldType = SVG_Matrix2D_datatype;
687 : }
688 2 : gf_svg_compute_path_anim(rai, rai->last_specified_value.far_ptr, FIX_ONE);
689 2 : return;
690 65 : } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) {
691 0 : if (animp->to) {
692 0 : rai->last_specified_value.fieldType = animp->to->type;
693 0 : rai->last_specified_value.far_ptr = animp->to->value;
694 : } else {
695 : /* TODO ??? */
696 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,
697 : ("[SMIL Animation] Animation %s - set element without to attribute\n",
698 : gf_node_get_log_name((GF_Node *)rai->anim_elt)));
699 : }
700 : return;
701 : }
702 :
703 65 : if (rai->values_count) {
704 : /* Ignore from/to/by*/
705 19 : rai->last_specified_value.fieldType = animp->values->type;
706 19 : rai->last_specified_value.far_ptr = gf_list_last(animp->values->values);
707 46 : } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) {
708 3 : rai->last_specified_value.fieldType = animp->by->type;
709 3 : rai->last_specified_value.far_ptr = animp->by->value;
710 43 : } else if (animp->to) {
711 40 : rai->last_specified_value.fieldType = animp->to->type;
712 40 : rai->last_specified_value.far_ptr = animp->to->value;
713 : }
714 65 : if (gf_svg_is_inherit(&rai->last_specified_value)) {
715 6 : rai->last_specified_value.fieldType = rai->owner->presentation_value.fieldType;
716 6 : rai->last_specified_value.far_ptr = rai->owner->presentation_value.far_ptr;
717 : }
718 65 : if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(rai->last_specified_value.fieldType)) {
719 28 : gf_svg_attributes_resolve_currentColor(&rai->last_specified_value, &rai->owner->current_color_value);
720 28 : gf_svg_attributes_resolve_inherit(&rai->last_specified_value, &rai->owner->parent_presentation_value);
721 : }
722 : }
723 :
724 : /* if the animation behavior is accumulative and this is not the first iteration,
725 : then we modify the interpolation value as follows:
726 : interpolation value += last specified value * number of iterations completed */
727 7646 : static void gf_smil_anim_apply_accumulate(SMIL_Anim_RTI *rai)
728 : {
729 : u32 nb_iterations;
730 :
731 7646 : SMILAnimationAttributesPointers *animp = rai->animp;
732 7646 : SMILTimingAttributesPointers *timingp = rai->timingp;
733 :
734 7646 : nb_iterations = (timingp->runtime->current_interval ? timingp->runtime->current_interval->nb_iterations : 1);
735 :
736 7646 : if (rai->change_detection_mode) {
737 4839 : if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM)
738 105 : && nb_iterations > 0
739 15 : && rai->previous_iteration != (s32) nb_iterations) {
740 : /* if we actually do accumulation and the number of iteration is different,
741 : then we force the result as changed regardless of the result of the interpolation
742 : (TODO: check if this need to be improved)*/
743 1 : rai->interpolated_value_changed = 1;
744 : } else {
745 : /* if we don't accumulate we leave the value of interpolated_value_changed unchanged */
746 : }
747 : } else {
748 2807 : if (nb_iterations > 0 && rai->previous_iteration != (s32) nb_iterations) {
749 2 : rai->previous_iteration = nb_iterations;
750 : }
751 :
752 2807 : if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM) && nb_iterations > 0) {
753 15 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
754 : ("[SMIL Animation] Time %f - Animation %s - applying accumulation (iteration #%d)\n",
755 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), nb_iterations));
756 :
757 15 : gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value,
758 : INT2FIX(nb_iterations), &rai->last_specified_value,
759 : &rai->interpolated_value, 1);
760 :
761 15 : if ((animp->from) && animp->by && (rai->last_specified_value.far_ptr == animp->by->value)) {
762 : /* this is a from-by animation, the last specified value is not the 'by' value but actually 'from'+'by',
763 : we need to add nb_iterations times from to the interpolated_value
764 : see (animate-elem-210-t.svg (upper two circles in the mid column, after 9s/14s */
765 : GF_FieldInfo from_info;
766 0 : from_info.fieldType = rai->animp->from->type;
767 0 : from_info.far_ptr = rai->animp->from->value;
768 0 : gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value,
769 : INT2FIX(nb_iterations), &from_info,
770 : &rai->interpolated_value, 1);
771 : }
772 : }
773 : }
774 7646 : }
775 :
776 7646 : static void gf_smil_apply_additive(SMIL_Anim_RTI *rai)
777 : {
778 7646 : SMILAnimationAttributesPointers *animp = rai->animp;
779 7646 : if (rai->change_detection_mode) return;
780 : else {
781 : /* Apply additive behavior if required
782 : PV = (additive == sum ? PV + animp->IV : animp->IV); */
783 2807 : if (animp->additive && *animp->additive == SMIL_ADDITIVE_SUM) {
784 315 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
785 : ("[SMIL Animation] Time %f - Animation %s - applying additive behavior\n",
786 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
787 :
788 315 : gf_svg_attributes_add((rai->is_first_anim ? &rai->owner->specified_value : &rai->owner->presentation_value),
789 : &rai->interpolated_value,
790 315 : &rai->owner->presentation_value,
791 : 1);
792 :
793 : #ifndef GPAC_DISABLE_LOG
794 315 : if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) {
795 0 : char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value);
796 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n",
797 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt),
798 : gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str)
799 : );
800 0 : if (str) gf_free(str);
801 : }
802 : #endif
803 :
804 : } else {
805 2492 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
806 : ("[SMIL Animation] Time %f - Animation %s - applying non-additive behavior\n",
807 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
808 :
809 : /* FIXME: if we switch pointers to avoid copying values,
810 : we need to modify the address in the DOM node as well,
811 : we also need to take care about change detections. Not easy!!
812 :
813 : void *tmp = rai->owner->presentation_value.far_ptr;
814 : rai->owner->presentation_value.far_ptr = rai->interpolated_value.far_ptr;
815 : rai->interpolated_value.far_ptr = tmp;
816 : */
817 :
818 2492 : gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->interpolated_value, 1);
819 : #ifndef GPAC_DISABLE_LOG
820 2492 : if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) {
821 0 : char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value);
822 :
823 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n",
824 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt),
825 : gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str)
826 : );
827 :
828 0 : if (str) gf_free(str);
829 : }
830 : #endif
831 : }
832 : }
833 : }
834 :
835 7641 : static void gf_smil_anim_animate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time)
836 : {
837 7641 : if (!rti || !rti->rai || !rti->rai->animp) return;
838 : SMIL_Anim_RTI *rai = rti->rai;
839 :
840 7641 : gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time);
841 7641 : gf_smil_anim_apply_accumulate(rai);
842 7641 : gf_smil_apply_additive(rai);
843 : }
844 :
845 : static void gf_smil_anim_animate_with_fraction(SMIL_Timing_RTI *rti, Fixed normalized_simple_time)
846 : {
847 0 : gf_smil_anim_animate(rti, rti->fraction);
848 0 : rti->evaluate_status = SMIL_TIMING_EVAL_NONE;
849 : }
850 :
851 49 : void gf_smil_anim_reset_variables(SMIL_Anim_RTI *rai)
852 : {
853 53 : if (!rai) return;
854 : /* we reset all the animation parameters to force computation of next interpolation value
855 : when the animation restarts */
856 120 : rai->interpolated_value_changed = 0;
857 120 : rai->previous_key_index = -1;
858 120 : rai->previous_coef = -FIX_ONE;
859 120 : rai->previous_iteration = -1;
860 120 : rai->previous_keytime_index = 0;
861 120 : rai->anim_done = 0;
862 : }
863 :
864 : /* copy/paste of the animate function
865 : TODO: check if computations of interpolation value can be avoided.
866 : */
867 216 : static void gf_smil_anim_freeze(SMIL_Timing_RTI *rti, Fixed normalized_simple_time)
868 : {
869 216 : if (!rti || !rti->rai || !rti->rai->animp) return;
870 : SMIL_Anim_RTI *rai = rti->rai;
871 :
872 216 : if (rai->change_detection_mode) {
873 211 : if (rai->anim_done == 0)
874 5 : rai->interpolated_value_changed = 1;
875 : else
876 206 : rai->interpolated_value_changed = 0;
877 : } else {
878 5 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
879 : ("[SMIL Animation] Time %f - Animation %s - applying freeze behavior\n",
880 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
881 :
882 5 : gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time);
883 5 : gf_smil_anim_apply_accumulate(rai);
884 5 : gf_smil_apply_additive(rai);
885 5 : rai->anim_done = 1;
886 : }
887 : }
888 :
889 62 : static void gf_smil_anim_remove(SMIL_Timing_RTI *rti, Fixed normalized_simple_time)
890 : {
891 62 : SMIL_Anim_RTI *rai = rti->rai;
892 62 : if (!rai) return;
893 :
894 62 : if (rai->change_detection_mode) {
895 60 : if (rai->anim_done == 0)
896 2 : rai->interpolated_value_changed = 1;
897 : else
898 58 : rai->interpolated_value_changed = 0;
899 : } else {
900 2 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL,
901 : ("[SMIL Animation] Time %f - Animation %s - applying remove behavior\n",
902 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt)));
903 :
904 : /* TODO: see if we can avoid this copy by switching pointers */
905 2 : gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->owner->specified_value, 0);
906 : /* TODO: check if we need to apply additive behavior even in fill='remove'
907 : maybe (see animate-elem-211-t.svg) */
908 :
909 2 : rai->anim_done = 1;
910 :
911 : #ifndef GPAC_DISABLE_LOG
912 2 : if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) {
913 0 : char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value);
914 :
915 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n",
916 : gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt),
917 : gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str)
918 : );
919 0 : if (str) gf_free(str);
920 : }
921 : #endif
922 :
923 : }
924 : }
925 :
926 7919 : static void gf_smil_anim_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time, GF_SGSMILTimingEvalState state)
927 : {
928 7919 : SMIL_Anim_RTI *rai = rti->rai;
929 7919 : switch (state) {
930 : case SMIL_TIMING_EVAL_REPEAT:
931 : /* we are starting a new cycle of animation, therefore we need to reset the previous state variables
932 : like previous_keytime_index ... */
933 : gf_smil_anim_reset_variables(rai);
934 7641 : case SMIL_TIMING_EVAL_UPDATE:
935 7641 : gf_smil_anim_animate(rti, normalized_simple_time);
936 7641 : break;
937 216 : case SMIL_TIMING_EVAL_FREEZE:
938 216 : gf_smil_anim_freeze(rti, normalized_simple_time);
939 216 : break;
940 62 : case SMIL_TIMING_EVAL_REMOVE:
941 62 : gf_smil_anim_remove(rti, normalized_simple_time);
942 62 : break;
943 : case SMIL_TIMING_EVAL_FRACTION:
944 : gf_smil_anim_animate_with_fraction(rti, normalized_simple_time);
945 : break;
946 : /*
947 : discard should be done before in smil_notify_time
948 : case SMIL_TIMING_EVAL_DISCARD:
949 : break;
950 : */
951 : default:
952 : break;
953 : }
954 7919 : }
955 : /**************************************************************************************
956 : **************************************************************************************/
957 :
958 : GF_EXPORT
959 40361 : void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_props)
960 : {
961 : u32 count_all, i;
962 : u32 active_anim;
963 : #ifndef GPAC_DISABLE_LOG
964 : u32 time=0;
965 :
966 40361 : if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) {
967 0 : time = gf_sys_clock();
968 : }
969 : #endif
970 :
971 : /* Perform all the animations on this node */
972 40361 : count_all = gf_node_animation_count(node);
973 46098 : for (i = 0; i < count_all; i++) {
974 : GF_FieldInfo info;
975 : s32 j;
976 : u32 count;
977 : SMIL_AttributeAnimations *aa;
978 :
979 :
980 5737 : aa = (SMIL_AttributeAnimations *)gf_node_animation_get(node, i);
981 5737 : count = gf_list_count(aa->anims);
982 5737 : if (!count) continue;
983 :
984 5737 : aa->presentation_value_changed = 0;
985 :
986 5737 : if (aa->is_property) {
987 : /* Storing the pointer to the parent presentation value,
988 : i.e. the presentation value produced at the parent level in the tree */
989 2960 : aa->parent_presentation_value = aa->presentation_value;
990 2960 : aa->parent_presentation_value.far_ptr =
991 2960 : gf_svg_get_property_pointer((SVG_Element *)node, aa->orig_dom_ptr, render_svg_props);
992 :
993 : /* Storing also the pointer to the presentation value of the color property
994 : (special handling of the keyword 'currentColor' if used in animation values) */
995 2960 : gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_color, 1, 1, &info);
996 2960 : aa->current_color_value.far_ptr = info.far_ptr;
997 : }
998 :
999 : /* We start with the last animation (TODO in the execution order), then scan in the reverse order
1000 : up to the first animation which is not additive, to determine if the presentation value will change
1001 : We evaluate each animation, but only in the 'change_detection_mode' */
1002 7309 : for (j = count-1; j >= 0; j--) {
1003 6337 : SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j);
1004 6337 : SMIL_Timing_RTI *rti = rai->timingp->runtime;
1005 :
1006 6337 : rai->interpolated_value_changed = 0;
1007 :
1008 : /* The evaluate_status has been updated when notifying the new scene time to this animation,
1009 : i.e. before the scene tree traversal */
1010 6337 : if (rti->evaluate_status) {
1011 5110 : rai->change_detection_mode = 1;
1012 5110 : rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
1013 5110 : aa->presentation_value_changed += rai->interpolated_value_changed;
1014 5110 : if (!rai->animp->additive || *rai->animp->additive == SMIL_ADDITIVE_REPLACE) {
1015 : /* we don't need to check previous animations since this one will overwrite it */
1016 4765 : j--;
1017 4765 : break;
1018 : }
1019 : }
1020 : }
1021 :
1022 : active_anim = 0;
1023 5737 : if (aa->presentation_value_changed) {
1024 : /* If the result of all the combined animations will produce a different result compared to the previous frame,
1025 : we start in the forward order from the j were the previous step stopped (i.e. the first anim in replace mode)
1026 : and evaluate each animation, in the computation mode (change_detection_mode = 0)*/
1027 6069 : for (j++; j<(s32)count; j++) {
1028 3260 : SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j);
1029 3260 : SMIL_Timing_RTI *rti = rai->timingp->runtime;
1030 :
1031 3260 : if (j == 0) rai->is_first_anim = 1;
1032 694 : else rai->is_first_anim = 0;
1033 :
1034 3260 : if (rti->evaluate_status) {
1035 2809 : rai->change_detection_mode = 0;
1036 2809 : rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
1037 2809 : active_anim++;
1038 : }
1039 : }
1040 :
1041 : /* DEBUG: uncomment this line to remove animation effect, and keep animation computation */
1042 : // gf_svg_attributes_copy(&aa->presentation_value, &aa->specified_value, 0);
1043 :
1044 : #ifndef GPAC_DISABLE_LOG
1045 2809 : if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) {
1046 0 : char *str = gf_svg_dump_attribute(node, &aa->presentation_value);
1047 :
1048 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Element %s - Presentation value changed for attribute %s, new value: %s - dirty flags %x\n",
1049 : gf_node_get_scene_time(node), gf_node_get_log_name(node),
1050 : gf_svg_get_attribute_name(node, aa->presentation_value.fieldIndex), str, aa->dirty_flags)
1051 : );
1052 :
1053 0 : if (str) gf_free(str);
1054 : }
1055 : #endif
1056 :
1057 : } else {
1058 : /* DEBUG: uncomment this line to remove animation effect, and keep animation computation */
1059 : // gf_svg_attributes_copy(&aa->presentation_value, &aa->specified_value, 0);
1060 : }
1061 :
1062 : /* we only set dirty flags when a real flag is set to avoid unnecessary computation
1063 : for example, it is not necessary to set it when the anim is an animateTransform
1064 : since there is no associated flag */
1065 5737 : if (aa->dirty_flags) {
1066 4686 : if (aa->presentation_value_changed) {
1067 2532 : gf_node_dirty_set(node, aa->dirty_flags, aa->dirty_parents);
1068 : } else {
1069 : /* WARNING - This does not work for use elements because apply_animations may be called several times */
1070 2154 : if (active_anim) gf_node_dirty_clear(node, aa->dirty_flags);
1071 : }
1072 : }
1073 : }
1074 :
1075 : #ifndef GPAC_DISABLE_LOG
1076 40361 : if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) {
1077 0 : time_spent_in_anim += gf_sys_clock() - time;
1078 : }
1079 : #endif
1080 40361 : }
1081 :
1082 :
1083 0 : GF_Node *gf_smil_anim_get_target(GF_Node *e)
1084 : {
1085 : XLinkAttributesPointers *xlinkp = NULL;
1086 0 : if (!gf_svg_is_animation_tag(e->sgprivate->tag)) return NULL;
1087 0 : xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp;
1088 0 : return (xlinkp && xlinkp->href) ? xlinkp->href->target : NULL;
1089 : }
1090 :
1091 : /* Attributes from the animation elements are not easy to use during runtime,
1092 : the runtime info is a set of easy to use structures.
1093 : This function initializes them (interpolation values ...)
1094 : Needs to be called after gf_smil_timing_init_runtime_info */
1095 68 : void gf_smil_anim_init_runtime_info(GF_Node *e)
1096 : {
1097 : u32 i;
1098 : GF_FieldInfo target_attribute;
1099 : SMIL_AttributeAnimations *aa = NULL;
1100 : SMIL_Anim_RTI *rai;
1101 : XLinkAttributesPointers *xlinkp = NULL;
1102 : SMILAnimationAttributesPointers *animp = NULL;
1103 : SMILTimingAttributesPointers *timingp = NULL;
1104 : GF_Node *target = NULL;
1105 :
1106 69 : if (!e) return;
1107 :
1108 : /* Filling animation structures to be independent of the SVG Element structure */
1109 68 : animp = ((SVGTimedAnimBaseElement *)e)->animp;
1110 68 : timingp = ((SVGTimedAnimBaseElement *)e)->timingp;
1111 68 : if (!animp || !timingp) return;
1112 67 : xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp;
1113 :
1114 67 : target = xlinkp->href->target;
1115 :
1116 : memset(&target_attribute, 0, sizeof(GF_FieldInfo));
1117 67 : if (animp->attributeName && (animp->attributeName->name || animp->attributeName->tag)) {
1118 : /* Filling the target_attribute structure with info on the animated attribute (type, pointer to data, ...)
1119 : NOTE: the animated attribute is created with a default value, if it was not specified on the target element */
1120 64 : if (animp->attributeName->tag) {
1121 64 : gf_node_get_attribute_by_tag(target, animp->attributeName->tag, 1, 1, &target_attribute);
1122 : } else {
1123 0 : gf_node_get_field_by_name(target, animp->attributeName->name, &target_attribute);
1124 : }
1125 : } else {
1126 : /* All animation elements should have a target attribute except for animateMotion
1127 : cf http://www.w3.org/mid/u403c21ajf1sjqtk58g0g38eaep9f9g2ss@hive.bjoern.hoehrmann.de
1128 : "For animateMotion, the attributeName is implied and cannot be specified;
1129 : animateTransform requires specification of the attribute name and any attribute that is
1130 : a transform-like attribute can be a target, e.g. gradientTransform."*/
1131 :
1132 3 : switch (e->sgprivate->tag) {
1133 3 : case TAG_SVG_animateMotion:
1134 : /* Explicit creation of the pseudo 'motionTransform' attribute since it cannot be specified */
1135 3 : gf_node_get_attribute_by_tag(target, TAG_SVG_ATT_motionTransform, 1, 0, &target_attribute);
1136 6 : gf_mx2d_init(*(GF_Matrix2D *)target_attribute.far_ptr);
1137 3 : break;
1138 0 : default:
1139 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL,
1140 : ("[SMIL Animation] Missing attributeName attribute on element %s\n",
1141 : gf_node_get_log_name((GF_Node*)e) ));
1142 : return;
1143 : }
1144 : }
1145 :
1146 67 : if (animp->attributeType && *animp->attributeType == SMIL_ATTRIBUTETYPE_CSS) {
1147 : /* see example animate-elem-219-t.svg from the SVG test suite, upper row */
1148 0 : if (!gf_svg_is_property(target, &target_attribute)) {
1149 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL,
1150 : ("[SMIL Animation] Using CSS attributeType for an animation on an attribute which is not a property %s\n",
1151 : gf_node_get_log_name((GF_Node*)e) ));
1152 : return;
1153 : }
1154 : }
1155 :
1156 : /* Creation and setup of the runtime structure for animation */
1157 67 : GF_SAFEALLOC(rai, SMIL_Anim_RTI)
1158 67 : if (!rai) {
1159 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Animation] Failed to allocated SMIL anim RTI\n"));
1160 : return;
1161 : }
1162 :
1163 67 : rai->anim_elt = e;
1164 67 : rai->animp = animp;
1165 67 : rai->timingp = timingp;
1166 67 : rai->xlinkp = xlinkp;
1167 :
1168 134 : gf_mx2d_init(rai->identity);
1169 67 : rai->default_transform_value.far_ptr = &rai->identity;
1170 67 : rai->default_transform_value.fieldType = SVG_Transform_datatype;
1171 :
1172 : /* the interpolated value has the same type as the target attribute,
1173 : but we need to create a new pointer to hold its value */
1174 67 : rai->interpolated_value = target_attribute;
1175 67 : rai->interpolated_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType);
1176 :
1177 : /* there has not been any interpolation yet, so the previous key index and interpolation coefficient
1178 : shall not be set*/
1179 : gf_smil_anim_reset_variables(rai);
1180 :
1181 67 : rai->values_count = (animp->values ? gf_list_count(animp->values->values) : 0);
1182 67 : rai->key_times_count = (animp->keyTimes ? gf_list_count(*animp->keyTimes) : 0);
1183 67 : rai->key_points_count = (animp->keyPoints ? gf_list_count(*animp->keyPoints) : 0);
1184 67 : rai->key_splines_count = (animp->keySplines ? gf_list_count(*animp->keySplines) : 0);
1185 :
1186 :
1187 115 : if (!rai->values_count && /* 'values' attribute not specified */
1188 96 : (!animp->to || animp->to->type == 0) && /* 'to' attribute not specified */
1189 16 : (!animp->from || animp->from->type == 0) && /* 'from' attribute not specified */
1190 11 : (animp->by && animp->by->type != 0)) { /* 'by' attribute specified */
1191 : /* if this is a 'by' animation without from the animation is defined to be additive
1192 : see http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#AnimationNS-FromToBy
1193 : we override the additive attribute */
1194 3 : if (!animp->additive) {
1195 : /* this case can only happen with dynamic allocation of attributes */
1196 : GF_FieldInfo info;
1197 3 : gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_additive, 1, 0, &info);
1198 3 : animp->additive = info.far_ptr;
1199 : }
1200 3 : if (*animp->additive == SMIL_ADDITIVE_REPLACE) {
1201 3 : GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL, ("[SMIL Animation] Warning: by-animations cannot use additive=\"replace\"\n"));
1202 : }
1203 3 : *animp->additive = SMIL_ADDITIVE_SUM;
1204 : }
1205 :
1206 : /*TODO
1207 : http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation
1208 : To animation defines its own kind of additive semantics, so the additive attribute is ignored.
1209 : */
1210 :
1211 : /*TODO
1212 : http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation
1213 : Because to animation is defined in terms of absolute values of the target attribute,
1214 : cumulative animation is not defined:
1215 : */
1216 :
1217 : /* TODO
1218 : http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-setElement
1219 : The set element is non-additive. The additive and accumulate attributes are not allowed,
1220 : and will be ignored if specified.
1221 : */
1222 :
1223 : /* For animateMotion, we need to retrieve the value of the rotate attribute, retrieve the path either
1224 : from the 'path' attribute or from the 'mpath' element, and then initialize the path iterator*/
1225 67 : if (e->sgprivate->tag == TAG_SVG_animateMotion) {
1226 : GF_Path *the_path = NULL;
1227 : GF_ChildNodeItem *child = NULL;
1228 :
1229 : GF_FieldInfo info;
1230 3 : if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_rotate, 0, 0, &info) == GF_OK) {
1231 3 : rai->rotate = ((SVG_Rotate *)info.far_ptr)->type;
1232 : } else {
1233 0 : rai->rotate = SVG_NUMBER_VALUE;
1234 : }
1235 3 : if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_path, 0, 0, &info) == GF_OK) {
1236 2 : the_path = ((SVG_PathData *)info.far_ptr);
1237 : }
1238 3 : child = ((SVG_Element *)e)->children;
1239 :
1240 6 : if ((!animp->to || animp->to->type == 0) &&
1241 6 : (!animp->by || animp->by->type == 0) &&
1242 3 : (!animp->values || animp->values->type == 0)) {
1243 : #if USE_GF_PATH
1244 3 : if (!gf_path_is_empty(the_path)) {
1245 1 : rai->path = the_path;
1246 1 : rai->path_iterator = gf_path_iterator_new(rai->path);
1247 1 : rai->length = gf_path_iterator_get_length(rai->path_iterator);
1248 : }
1249 : #else
1250 : rai->path = gf_path_new();
1251 : if (gf_list_count(the_path->points)) {
1252 : gf_svg_path_build(rai->path, the_path->commands, the_path->points);
1253 : rai->path_iterator = gf_path_iterator_new(rai->path);
1254 : rai->length = gf_path_iterator_get_length(rai->path_iterator);
1255 : }
1256 : #endif
1257 : else {
1258 2 : while (child) {
1259 : GF_Node *used_path = NULL;
1260 1 : u32 child_tag = gf_node_get_tag(child->node);
1261 1 : if (child_tag == TAG_SVG_mpath) {
1262 1 : if (gf_node_get_attribute_by_tag(child->node, TAG_XLINK_ATT_href, 0, 0, &info) == GF_OK) {
1263 1 : XMLRI *iri = (XMLRI *)info.far_ptr;
1264 1 : if (iri->target) used_path = iri->target;
1265 0 : else if (iri->string) used_path =
1266 0 : (GF_Node *)gf_sg_find_node_by_name(gf_node_get_graph(child->node), iri->string);
1267 1 : if (used_path && gf_node_get_tag(used_path) == TAG_SVG_path) {
1268 1 : gf_node_get_attribute_by_tag(used_path, TAG_SVG_ATT_d, 1, 0, &info);
1269 : #if USE_GF_PATH
1270 1 : rai->path = (SVG_PathData *)info.far_ptr;
1271 : #else
1272 : gf_svg_path_build(rai->path,
1273 : ((SVG_PathData *)info.far_ptr)->commands,
1274 : ((SVG_PathData *)info.far_ptr)->points);
1275 : #endif
1276 1 : rai->path_iterator = gf_path_iterator_new(rai->path);
1277 1 : rai->length = gf_path_iterator_get_length(rai->path_iterator);
1278 : }
1279 : }
1280 : break;
1281 : }
1282 0 : child = child->next;
1283 : }
1284 : }
1285 : }
1286 : }
1287 :
1288 : /* for all animations, check if there is already one animation on this attribute,
1289 : if yes, get the list and append the new animation runtime info
1290 : if no, create a list and add the new animation runtime info. */
1291 22 : for (i = 0; i < gf_node_animation_count(target); i++) {
1292 31 : aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i);
1293 31 : if (aa->presentation_value.fieldIndex == target_attribute.fieldIndex) {
1294 9 : gf_list_add(aa->anims, rai);
1295 9 : break;
1296 : }
1297 : aa = NULL;
1298 : }
1299 67 : if (!aa) {
1300 58 : GF_SAFEALLOC(aa, SMIL_AttributeAnimations)
1301 58 : if (!aa) {
1302 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Animation] Failed to allocated SMIL attribue ani\n"));
1303 : return;
1304 : }
1305 :
1306 : /* We determine if the animated attribute is a property since this changes quite a lot the animation model */
1307 58 : aa->is_property = gf_svg_is_property(target, &target_attribute);
1308 58 : aa->current_color_value.fieldType = SVG_Paint_datatype;
1309 :
1310 : /* We copy (one copy for all animations on the same attribute) the DOM specified
1311 : value before any animation starts (because the animation will override it),
1312 : we also save the initial memory address of the specified value (orig_dom_ptr)
1313 : for inheritance hack */
1314 58 : aa->specified_value = target_attribute;
1315 58 : aa->orig_dom_ptr = aa->specified_value.far_ptr;
1316 58 : aa->specified_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType);
1317 58 : gf_svg_attributes_copy(&aa->specified_value, &target_attribute, 0);
1318 :
1319 : /* Now, the initial memory address of the specified value holds the presentation value,
1320 : and the presentation value is initialized */
1321 58 : aa->presentation_value = target_attribute;
1322 :
1323 58 : aa->anims = gf_list_new();
1324 58 : gf_list_add(aa->anims, rai);
1325 58 : gf_node_animation_add(target, aa);
1326 :
1327 : /* determine what the rendering will need to do when the animation runs */
1328 58 : aa->dirty_flags = gf_svg_get_modification_flags((SVG_Element *)target, &target_attribute);
1329 :
1330 : /* If the animation will result in a change of geometry or of the display property,
1331 : this animation will require traversing the tree, we need to inform the parents of the target node */
1332 58 : aa->dirty_parents = 0;
1333 58 : if (aa->dirty_flags & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_SVG_DISPLAY_DIRTY)) aa->dirty_parents = 1;
1334 : }
1335 :
1336 67 : rai->owner = aa;
1337 67 : gf_smil_anim_get_last_specified_value(rai);
1338 :
1339 : /* for animation (unlike other timed elements like video), the evaluation (i.e. interpolation) cannot be done
1340 : during timing evaluation, because due to inheritance, interpolation can only be computed
1341 : during scene tree traversal, therefore we need to postpone evaluation of the timed element */
1342 67 : timingp->runtime->postpone = 1;
1343 :
1344 67 : timingp->runtime->evaluate = gf_smil_anim_evaluate;
1345 : }
1346 :
1347 67 : void gf_smil_anim_delete_runtime_info(SMIL_Anim_RTI *rai)
1348 : {
1349 67 : gf_svg_delete_attribute_value(rai->interpolated_value.fieldType,
1350 : rai->interpolated_value.far_ptr,
1351 67 : rai->anim_elt->sgprivate->scenegraph);
1352 67 : if (rai->path) {
1353 2 : gf_svg_delete_attribute_value(rai->last_specified_value.fieldType,
1354 : rai->last_specified_value.far_ptr,
1355 2 : rai->anim_elt->sgprivate->scenegraph);
1356 : #if USE_GF_PATH
1357 : #else
1358 : if (rai->path) gf_path_del(rai->path);
1359 : #endif
1360 :
1361 : }
1362 :
1363 67 : if (rai->path_iterator) gf_path_iterator_del(rai->path_iterator);
1364 67 : gf_free(rai);
1365 67 : }
1366 :
1367 67 : void gf_smil_anim_remove_from_target(GF_Node *anim, GF_Node *target)
1368 : {
1369 : u32 i, j;
1370 125 : if (!target) return;
1371 21 : for (i = 0; i < gf_node_animation_count((GF_Node *)target); i ++) {
1372 : SMIL_Anim_RTI *rai;
1373 21 : SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get((GF_Node *)target, i);
1374 21 : j=0;
1375 56 : while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) {
1376 23 : if ((GF_Node *)rai->anim_elt == anim) {
1377 9 : gf_list_rem(aa->anims, j-1);
1378 9 : gf_smil_anim_delete_runtime_info(rai);
1379 9 : break;
1380 : }
1381 : }
1382 21 : if (gf_list_count(aa->anims) == 0) {
1383 7 : gf_list_del(aa->anims);
1384 7 : gf_svg_delete_attribute_value(aa->specified_value.fieldType,
1385 : aa->specified_value.far_ptr,
1386 7 : target->sgprivate->scenegraph);
1387 7 : aa->presentation_value.far_ptr = aa->orig_dom_ptr;
1388 7 : gf_node_animation_rem((GF_Node *)target, i);
1389 7 : gf_free(aa);
1390 : }
1391 : }
1392 : }
1393 :
1394 49 : void gf_smil_anim_delete_animations(GF_Node *e)
1395 : {
1396 : u32 i, j;
1397 :
1398 100 : for (i = 0; i < gf_node_animation_count(e); i ++) {
1399 : SMIL_Anim_RTI *rai;
1400 51 : SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(e, i);
1401 51 : gf_svg_delete_attribute_value(aa->specified_value.fieldType,
1402 : aa->specified_value.far_ptr,
1403 51 : e->sgprivate->scenegraph);
1404 51 : j=0;
1405 160 : while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) {
1406 58 : rai->xlinkp->href->target = NULL;
1407 58 : gf_smil_anim_delete_runtime_info(rai);
1408 : }
1409 51 : gf_list_del(aa->anims);
1410 51 : gf_free(aa);
1411 : }
1412 49 : gf_node_animation_del(e);
1413 49 : }
1414 :
1415 0 : void gf_smil_anim_init_discard(GF_Node *node)
1416 : {
1417 : SVGAllAttributes all_atts;
1418 : XLinkAttributesPointers *xlinkp = NULL;
1419 : SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node;
1420 0 : gf_smil_timing_init_runtime_info(node);
1421 :
1422 0 : gf_svg_flatten_attributes((SVG_Element *)e, &all_atts);
1423 0 : GF_SAFEALLOC(e->xlinkp, XLinkAttributesPointers);
1424 0 : if (!e->xlinkp) {
1425 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,("[SMIL] Error creating anim xlink attrib\n"));
1426 0 : return;
1427 : }
1428 : xlinkp = e->xlinkp;
1429 0 : xlinkp->href = all_atts.xlink_href;
1430 0 : xlinkp->type = all_atts.xlink_type;
1431 :
1432 0 : e->timingp->runtime->evaluate_status = SMIL_TIMING_EVAL_DISCARD;
1433 : }
1434 :
1435 68 : void gf_smil_anim_init_node(GF_Node *node)
1436 : {
1437 : XLinkAttributesPointers *xlinkp;
1438 : SMILAnimationAttributesPointers *animp = NULL;
1439 : SVGAllAttributes all_atts;
1440 : SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node;
1441 :
1442 68 : gf_svg_flatten_attributes((SVG_Element *)e, &all_atts);
1443 68 : e->xlinkp = gf_malloc(sizeof(XLinkAttributesPointers));
1444 : xlinkp = e->xlinkp;
1445 68 : xlinkp->href = all_atts.xlink_href;
1446 68 : xlinkp->type = all_atts.xlink_type;
1447 :
1448 : /*perform init of default values
1449 : When the xlink:href attribute of animation is not set, the target defaults to the parent element */
1450 68 : if (!xlinkp->href) {
1451 : GF_FieldInfo info;
1452 0 : gf_node_get_attribute_by_tag((GF_Node *)node, TAG_XLINK_ATT_href, 1, 0, &info);
1453 0 : xlinkp->href = info.far_ptr;
1454 0 : xlinkp->href->type = XMLRI_ELEMENTID;
1455 0 : xlinkp->href->target = gf_node_get_parent(node, 0);
1456 : }
1457 68 : if (xlinkp->href->type == XMLRI_STRING) {
1458 0 : if (!xlinkp->href->string) {
1459 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,("Error: IRI not initialized\n"));
1460 0 : return;
1461 : } else {
1462 : GF_Node *n;
1463 :
1464 0 : n = (GF_Node*)gf_sg_find_node_by_name(gf_node_get_graph(node), xlinkp->href->string);
1465 0 : if (n) {
1466 0 : xlinkp->href->type = XMLRI_ELEMENTID;
1467 0 : xlinkp->href->target = n;
1468 0 : gf_node_register_iri(node->sgprivate->scenegraph, xlinkp->href);
1469 : } else {
1470 : return;
1471 : }
1472 : }
1473 : }
1474 68 : if (!xlinkp->href->target) {
1475 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL,("Trying to initialize an animation when the target is not known\n"));
1476 : return;
1477 : }
1478 :
1479 : // We may not have an attribute name, when using an animateMotion element
1480 68 : if (node->sgprivate->tag != TAG_SVG_animateMotion && !all_atts.attributeName) {
1481 : goto end_init;
1482 : }
1483 :
1484 : /* if an attribute (to, from or by) is present but its type is not set
1485 : (e.g. it could not be determined before, the target was not known), we try to get the type from the target */
1486 67 : if ( (all_atts.to && (all_atts.to->type==0))
1487 67 : || (all_atts.from && (all_atts.from->type==0))
1488 67 : || (all_atts.by && (all_atts.by->type==0))
1489 : ) {
1490 : GF_FieldInfo info;
1491 0 : if (gf_node_get_attribute_by_name((GF_Node *)xlinkp->href->target, all_atts.attributeName->name, 0, 1, 1, &info)==GF_OK) {
1492 0 : u32 anim_value_type = info.fieldType;
1493 : u32 i;
1494 0 : for (i=0; i<3; i++) {
1495 : u32 tag = 0;
1496 : switch (i) {
1497 : case 0:
1498 : tag=TAG_SVG_ATT_to;
1499 : break;
1500 : case 1:
1501 : tag=TAG_SVG_ATT_from;
1502 : break;
1503 : case 2:
1504 : tag=TAG_SVG_ATT_by;
1505 : break;
1506 : }
1507 0 : if (gf_node_get_attribute_by_tag((GF_Node *)node, tag, 0, 0, &info)==GF_OK) {
1508 0 : SMIL_AnimateValue *attval = info.far_ptr;
1509 0 : if (attval->type==0) {
1510 0 : SVG_String string = attval->value;
1511 0 : attval->value = NULL;
1512 0 : if (string) {
1513 0 : gf_svg_parse_attribute((GF_Node *)node, &info, string, anim_value_type);
1514 0 : gf_free(string);
1515 : }
1516 : }
1517 : }
1518 : }
1519 : }
1520 : }
1521 :
1522 67 : e->animp = gf_malloc(sizeof(SMILAnimationAttributesPointers));
1523 : animp = e->animp;
1524 67 : animp->accumulate = all_atts.accumulate;
1525 67 : animp->additive = all_atts.additive;
1526 67 : animp->attributeName = all_atts.attributeName;
1527 67 : animp->attributeType = all_atts.attributeType;
1528 67 : animp->by = all_atts.by;
1529 67 : animp->calcMode = all_atts.calcMode;
1530 67 : animp->from = all_atts.from;
1531 67 : animp->keySplines = all_atts.keySplines;
1532 67 : animp->keyTimes = all_atts.keyTimes;
1533 67 : animp->lsr_enabled = all_atts.lsr_enabled;
1534 67 : animp->to = all_atts.to;
1535 67 : animp->type = all_atts.transform_type;
1536 67 : animp->values = all_atts.values;
1537 67 : if (node->sgprivate->tag == TAG_SVG_animateMotion) {
1538 3 : e->animp->keyPoints = all_atts.keyPoints;
1539 3 : e->animp->origin = all_atts.origin;
1540 3 : e->animp->path = all_atts.path;
1541 3 : e->animp->rotate = all_atts.rotate;
1542 : } else {
1543 64 : e->animp->keyPoints = NULL;
1544 64 : e->animp->origin = NULL;
1545 64 : e->animp->path = NULL;
1546 64 : e->animp->rotate = NULL;
1547 : }
1548 :
1549 69 : end_init:
1550 68 : gf_smil_timing_init_runtime_info(node);
1551 68 : gf_smil_anim_init_runtime_info(node);
1552 68 : gf_smil_anim_set_anim_runtime_in_timing(node);
1553 : }
1554 :
1555 :
1556 :
1557 : #endif /*GPAC_DISABLE_SVG*/
|