Coverage Report

Created: 2026-02-14 07:22

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