LCOV - code coverage report
Current view: top level - scenegraph - smil_anim.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 530 671 79.0 %
Date: 2021-04-29 23:48:07 Functions: 28 33 84.8 %

          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*/

Generated by: LCOV version 1.13