| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Cyril Concolato | |
| * Copyright (c) Telecom ParisTech 2004-2022 | |
| * | |
| * This file is part of GPAC / SVG Scene Graph sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/internal/scenegraph_dev.h> | |
| #include <gpac/nodes_svg.h> | |
| #include <gpac/utf.h> | |
| #ifndef GPAC_DISABLE_LOG | |
| u32 time_spent_in_anim = 0; | |
| #endif | |
| #ifndef GPAC_DISABLE_SVG | |
| /************************************************************************************** | |
| * Each GF_Node holds the (SVG/SMIL) animation elements which target itself in a list * | |
| * The following are the generic functions to manipulate this list: * | |
| * - add a new animation to the list, * | |
| * - get an animation from the list, * | |
| * - remove an animation from the list, * | |
| * - count the animations in the list, * | |
| * - delete the list * | |
| **************************************************************************************/ | |
| GF_Err gf_node_animation_add(GF_Node *node, void *animation) | |
| { | |
| if (!node || !animation) return GF_BAD_PARAM; | |
| if (!node->sgprivate->interact) { | |
| GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); | |
| if (!node->sgprivate->interact) return GF_OUT_OF_MEM; | |
| } | |
| if (!node->sgprivate->interact->animations) node->sgprivate->interact->animations = gf_list_new(); | |
| return gf_list_add(node->sgprivate->interact->animations, animation); | |
| } | |
| GF_Err gf_node_animation_del(GF_Node *node) | |
| { | |
| if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_BAD_PARAM; | |
| gf_list_del(node->sgprivate->interact->animations); | |
| node->sgprivate->interact->animations = NULL; | |
| return GF_OK; | |
| } | |
| u32 gf_node_animation_count(GF_Node *node) | |
| { | |
| if (!node || !node->sgprivate->interact|| !node->sgprivate->interact->animations) return 0; | |
| return gf_list_count(node->sgprivate->interact->animations); | |
| } | |
| void *gf_node_animation_get(GF_Node *node, u32 i) | |
| { | |
| if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return 0; | |
| return gf_list_get(node->sgprivate->interact->animations, i); | |
| } | |
| GF_Err gf_node_animation_rem(GF_Node *node, u32 i) | |
| { | |
| if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_OK; | |
| return gf_list_rem(node->sgprivate->interact->animations, i); | |
| } | |
| /************************************************************************************** | |
| * End of Generic GF_Node animations list * | |
| **************************************************************************************/ | |
| /************************************************************************************** | |
| * Helping functions for animation * | |
| **************************************************************************************/ | |
| /* Sets the pointer to the attribute value with the pointer | |
| to the value which passed (if unspecified) */ | |
| void gf_svg_attributes_resolve_unspecified(GF_FieldInfo *in, GF_FieldInfo *p, GF_FieldInfo *t) | |
| { | |
| if (in->fieldType == 0) { | |
| if (p->fieldType == SVG_Transform_datatype) { | |
| /* if the input value is not specified, and the presentation value is of type Transform, | |
| then we should use the default identity transform instead of the presentation value */ | |
| *in = *t; | |
| } else { | |
| *in = *p; | |
| } | |
| } | |
| } | |
| /* Replaces the pointer to the attribute value with the pointer | |
| to the value which is inherited (if inherited) */ | |
| void gf_svg_attributes_resolve_inherit(GF_FieldInfo *in, GF_FieldInfo *prop) | |
| { | |
| if (gf_svg_is_inherit(in)) *in = *prop; | |
| } | |
| /* Replaces the pointer to the attribute value with the pointer | |
| to the value of the color attribute (if the current value is set to currentColor) */ | |
| void gf_svg_attributes_resolve_currentColor(GF_FieldInfo *in, GF_FieldInfo *current_color) | |
| { | |
| if ((in->fieldType == SVG_Paint_datatype) && gf_svg_is_current_color(in)) { | |
| *in = *current_color; | |
| } | |
| } | |
| /************************************************************************************** | |
| * The main function doing evaluation of the animation is: gf_smil_anim_evaluate * | |
| * Depending on the timing status of the animation it calls: * | |
| * - gf_smil_anim_animate * | |
| * - gf_smil_anim_animate_with_fraction * | |
| * - gf_smil_anim_freeze * | |
| * - gf_smil_anim_remove * | |
| * * | |
| * The gf_smil_anim_animate consists in * | |
| * - interpolating using gf_smil_anim_compute_interpolation_value * | |
| * - accumulating using gf_smil_anim_apply_accumulate * | |
| * - applying additive behavior * | |
| * * | |
| * Depending on the animation attributes, one of the following functions is called * | |
| * by the function gf_smil_anim_compute_interpolation_value * | |
| * - gf_smil_anim_set * | |
| * - gf_smil_anim_animate_using_values * | |
| * - gf_smil_anim_animate_from_to * | |
| * - gf_smil_anim_animate_from_by * | |
| * - gf_smil_anim_animate_using_path * | |
| * * | |
| * In most animation methods, the important step in the animation is to resolve * | |
| * the inherit and currentColor values to perform further interpolation, i.e. calls: * | |
| * gf_svg_attributes_resolve_currentColor(&info, &rai->owner->current_color_value); * | |
| * gf_svg_attributes_resolve_inherit(&info, &rai->owner->parent_presentation_value); * | |
| * * | |
| **************************************************************************************/ | |
| static void gf_smil_anim_set(SMIL_Anim_RTI *rai) | |
| { | |
| GF_FieldInfo to_info; | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| if (!animp->to) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Animation %s - set element without to attribute\n", | |
| gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| return; | |
| } | |
| if (!animp->to->type) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Animation %s - set element with an unparsed to attribute\n", | |
| gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| return; | |
| } | |
| if (rai->change_detection_mode) { | |
| /* if the set has been applied, unless next animations are additive we don't need | |
| to apply it again */ | |
| if (rai->previous_coef > 0) rai->interpolated_value_changed = 0; | |
| else rai->interpolated_value_changed = 1; | |
| return; | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying set animation\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), | |
| gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| to_info.fieldType = animp->to->type; | |
| to_info.far_ptr = animp->to->value; | |
| /* we do not need to resolve currentColor values or inherit values here, | |
| because no further interpolation is required for the animation and | |
| because inheritance is applied after animations in the compositor. */ | |
| gf_svg_attributes_copy(&rai->interpolated_value, &to_info, 0); | |
| rai->previous_coef = FIX_ONE; | |
| } | |
| } | |
| static void gf_smil_anim_use_keypoints_keytimes(SMIL_Anim_RTI *rai, Fixed normalized_simple_time, | |
| Fixed *interpolation_coefficient, u32 *keyValueIndex) | |
| { | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| u32 keyTimeIndex = 0; | |
| Fixed interval_duration; | |
| *interpolation_coefficient = normalized_simple_time; | |
| /* Computing new interpolation coefficient */ | |
| if (rai->key_times_count) { | |
| Fixed keyTimeBefore = 0, keyTimeAfter=0; | |
| for (keyTimeIndex = rai->previous_keytime_index; keyTimeIndex< rai->key_times_count; keyTimeIndex++) { | |
| Fixed *t = (Fixed *)gf_list_get(*animp->keyTimes, keyTimeIndex); | |
| if (normalized_simple_time < *t) { | |
| Fixed *tm1; | |
| rai->previous_keytime_index = keyTimeIndex; | |
| tm1 = (Fixed *) gf_list_get(*animp->keyTimes, keyTimeIndex-1); | |
| if (tm1) keyTimeBefore = *tm1; | |
| else keyTimeBefore = 0; | |
| keyTimeAfter = *t; | |
| break; | |
| } | |
| } | |
| keyTimeIndex--; | |
| interval_duration = keyTimeAfter - keyTimeBefore; | |
| if (keyValueIndex) *keyValueIndex = keyTimeIndex; | |
| if (interval_duration) | |
| *interpolation_coefficient = gf_divfix(normalized_simple_time - keyTimeBefore, interval_duration); | |
| else | |
| *interpolation_coefficient = FIX_ONE; | |
| if (!rai->change_detection_mode) | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - Using Key Times: index %d, interval duration %.2f, coeff: %.2f\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), | |
| gf_node_get_log_name((GF_Node *)rai->anim_elt), | |
| keyTimeIndex, | |
| interval_duration, | |
| interpolation_coefficient)); | |
| } | |
| if (rai->anim_elt->sgprivate->tag == TAG_SVG_animateMotion && rai->key_points_count) { | |
| Fixed *p1; | |
| p1 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex); | |
| if (animp->calcMode && *animp->calcMode == SMIL_CALCMODE_DISCRETE) { | |
| *interpolation_coefficient = *p1; | |
| } else { | |
| Fixed *p2 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex+1); | |
| *interpolation_coefficient = gf_mulfix(FIX_ONE - *interpolation_coefficient, *p1) | |
| + gf_mulfix(*interpolation_coefficient, (p2 ? *p2 : *p1)); | |
| } | |
| if (keyValueIndex) *keyValueIndex = 0; | |
| if (!rai->change_detection_mode) | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - Using Key Points: key Point 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), keyTimeIndex, *interpolation_coefficient)); | |
| } | |
| } | |
| static void gf_smil_anim_animate_using_values(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) | |
| { | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| GF_List *values; | |
| GF_FieldInfo value_info, value_info_next; | |
| u32 keyValueIndex; | |
| Fixed interpolation_coefficient; | |
| u32 real_calcMode; | |
| values = animp->values->values; | |
| memset(&value_info, 0, sizeof(GF_FieldInfo)); | |
| value_info.fieldType = animp->values->type; | |
| value_info_next = value_info; | |
| real_calcMode = (gf_svg_attribute_is_interpolatable(animp->values->type)? | |
| (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR): | |
| SMIL_CALCMODE_DISCRETE | |
| ); | |
| if (rai->values_count == 1) { | |
| if (rai->change_detection_mode) { | |
| /* Since we have only 1 value, the previous key index should always be 0, | |
| unless the animation has not started or is reset (-1) */ | |
| if (rai->previous_key_index == 0) rai->interpolated_value_changed = 0; | |
| else rai->interpolated_value_changed = 1; | |
| return; | |
| } else { | |
| value_info.far_ptr = gf_list_get(values, 0); | |
| /* no further interpolation needed | |
| therefore no need to resolve inherit and currentColor */ | |
| gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); | |
| rai->previous_key_index = 0; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - Using values[0] as interpolation value\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| return; | |
| } | |
| } | |
| /* Computing new key value index and interpolation coefficient */ | |
| if (!rai->key_times_count) { | |
| if (real_calcMode == SMIL_CALCMODE_DISCRETE) { | |
| if (normalized_simple_time == FIX_ONE) { | |
| keyValueIndex = rai->values_count-1; | |
| interpolation_coefficient = FIX_ONE; | |
| } else { | |
| Fixed tmp = normalized_simple_time*rai->values_count; | |
| Fixed tmp_floor = gf_floor(tmp); | |
| if ((tmp - tmp_floor) == 0 && tmp) { | |
| keyValueIndex = FIX2INT(tmp_floor) - 1; | |
| } else { | |
| keyValueIndex = FIX2INT(tmp_floor); | |
| } | |
| interpolation_coefficient = tmp - INT2FIX(keyValueIndex); | |
| } | |
| } else { | |
| Fixed tmp = normalized_simple_time*(rai->values_count-1); | |
| if (normalized_simple_time == FIX_ONE) { | |
| keyValueIndex = rai->values_count-2; | |
| } else { | |
| keyValueIndex = FIX2INT(gf_floor(tmp)); | |
| } | |
| interpolation_coefficient = tmp - INT2FIX(keyValueIndex); | |
| } | |
| //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Animation %s - No KeyTimes: key index %d, coeff: %.2f\n", gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex, FIX2FLT(interpolation_coefficient))); | |
| } else { | |
| gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, &keyValueIndex); | |
| } | |
| if (rai->change_detection_mode) { | |
| if (real_calcMode == SMIL_CALCMODE_DISCRETE && rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef != -FIX_ONE) { | |
| rai->interpolated_value_changed = 0; | |
| } else if (rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef == interpolation_coefficient) | |
| rai->interpolated_value_changed = 0; | |
| else | |
| rai->interpolated_value_changed = 1; | |
| } else { | |
| rai->previous_key_index = keyValueIndex; | |
| rai->previous_coef = interpolation_coefficient; | |
| switch (real_calcMode) { | |
| case SMIL_CALCMODE_DISCRETE: | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying discrete animation using values (key value index: %d)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex)); | |
| value_info.far_ptr = gf_list_get(values, keyValueIndex); | |
| /* no further interpolation needed | |
| therefore no need to resolve inherit and currentColor */ | |
| gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); | |
| break; | |
| case SMIL_CALCMODE_PACED: | |
| /* TODO: at the moment assume it is linear */ | |
| case SMIL_CALCMODE_SPLINE: | |
| /* TODO: at the moment assume it is linear */ | |
| case SMIL_CALCMODE_LINEAR: | |
| if (keyValueIndex == rai->values_count - 1) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (setting last key value: %d)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex)); | |
| value_info.far_ptr = gf_list_get(values, rai->values_count - 1); | |
| /* no further interpolation needed | |
| therefore no need to resolve inherit and currentColor */ | |
| gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (key value indices: %d, %d / coeff: %f)\n", | |
| 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)); | |
| value_info.far_ptr = gf_list_get(values, keyValueIndex); | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) { | |
| gf_svg_attributes_resolve_currentColor(&value_info, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&value_info, &rai->owner->parent_presentation_value); | |
| } | |
| value_info_next.far_ptr = gf_list_get(values, keyValueIndex+1); | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) { | |
| gf_svg_attributes_resolve_currentColor(&value_info_next, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&value_info_next, &rai->owner->parent_presentation_value); | |
| } | |
| gf_svg_attributes_interpolate(&value_info, | |
| &value_info_next, | |
| &rai->interpolated_value, | |
| interpolation_coefficient, 1); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| static void gf_smil_anim_animate_from_to(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) | |
| { | |
| GF_FieldInfo from_info, to_info; | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| Fixed interpolation_coefficient; | |
| s32 useFrom = (normalized_simple_time<=FIX_ONE/2); | |
| u32 real_calcMode; | |
| real_calcMode = (animp->to && gf_svg_attribute_is_interpolatable(animp->to->type)? | |
| (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR): | |
| SMIL_CALCMODE_DISCRETE | |
| ); | |
| if (rai->change_detection_mode) { | |
| if (rai->previous_coef == normalized_simple_time) | |
| rai->interpolated_value_changed = 0; | |
| else { | |
| if (real_calcMode == SMIL_CALCMODE_DISCRETE && | |
| useFrom == rai->previous_key_index) { | |
| rai->interpolated_value_changed = 0; | |
| } else { | |
| rai->interpolated_value_changed = 1; | |
| } | |
| } | |
| } else { | |
| if (animp->from && animp->from->value) { | |
| from_info.fieldType = animp->from->type; | |
| from_info.far_ptr = animp->from->value; | |
| } else { | |
| from_info.fieldType = 0; | |
| from_info.far_ptr = NULL; | |
| } | |
| if (rai->is_first_anim) | |
| gf_svg_attributes_resolve_unspecified(&from_info, | |
| &rai->owner->specified_value, | |
| &rai->default_transform_value); | |
| else | |
| gf_svg_attributes_resolve_unspecified(&from_info, | |
| &rai->owner->presentation_value, | |
| &rai->default_transform_value); | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { | |
| gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value); | |
| } | |
| if (animp->to && animp->to->value) { | |
| to_info.fieldType = animp->to->type; | |
| to_info.far_ptr = animp->to->value; | |
| } else { | |
| to_info.fieldType = 0; | |
| to_info.far_ptr = NULL; | |
| } | |
| if (rai->is_first_anim) | |
| gf_svg_attributes_resolve_unspecified(&to_info, | |
| &rai->owner->specified_value, | |
| &rai->default_transform_value); | |
| else | |
| gf_svg_attributes_resolve_unspecified(&to_info, | |
| &rai->owner->presentation_value, | |
| &rai->default_transform_value); | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(to_info.fieldType)) { | |
| gf_svg_attributes_resolve_currentColor(&to_info, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&to_info, &rai->owner->parent_presentation_value); | |
| } | |
| gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL); | |
| rai->previous_coef = interpolation_coefficient; | |
| switch (real_calcMode) { | |
| case SMIL_CALCMODE_DISCRETE: | |
| { | |
| /* before half of the duration stay at 'from' and then switch to 'to' */ | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (using %s value)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), (useFrom?"from":"to"))); | |
| gf_svg_attributes_copy(&rai->interpolated_value, (useFrom?&from_info:&to_info), 0); | |
| rai->previous_key_index = useFrom; | |
| } | |
| break; | |
| case SMIL_CALCMODE_SPLINE: | |
| case SMIL_CALCMODE_PACED: | |
| case SMIL_CALCMODE_LINEAR: | |
| default: | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (linear interpolation, using coefficient %f)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), interpolation_coefficient)); | |
| gf_svg_attributes_interpolate(&from_info, &to_info, &rai->interpolated_value, interpolation_coefficient, 1); | |
| break; | |
| } | |
| } | |
| } | |
| static void gf_smil_anim_animate_from_by(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) | |
| { | |
| Fixed from_coef; | |
| GF_FieldInfo from_info, by_info; | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| s32 useFrom = (normalized_simple_time<=FIX_ONE/2); | |
| if (rai->change_detection_mode) { | |
| if (rai->previous_coef == normalized_simple_time) | |
| rai->interpolated_value_changed = 0; | |
| else { | |
| if (animp->calcMode && | |
| *animp->calcMode == SMIL_CALCMODE_DISCRETE && | |
| useFrom == rai->previous_key_index) { | |
| rai->interpolated_value_changed = 0; | |
| } else { | |
| rai->interpolated_value_changed = 1; | |
| } | |
| } | |
| } else { | |
| rai->previous_coef = normalized_simple_time; | |
| if (animp->from) { | |
| from_info.fieldType = animp->from->type; | |
| from_info.far_ptr = animp->from->value; | |
| from_coef = FIX_ONE; | |
| } else { | |
| from_info.fieldType = 0; | |
| from_info.far_ptr = NULL; | |
| /* this is a by animation only, then, it is always additive, | |
| we don't need the from value*/ | |
| from_coef = 0; | |
| } | |
| if (rai->is_first_anim) | |
| gf_svg_attributes_resolve_unspecified(&from_info, | |
| &rai->owner->specified_value, | |
| &rai->default_transform_value); | |
| else | |
| gf_svg_attributes_resolve_unspecified(&from_info, | |
| &rai->owner->presentation_value, | |
| &rai->default_transform_value); | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { | |
| gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value); | |
| } | |
| if (animp->by) { | |
| by_info.fieldType = animp->by->type; | |
| by_info.far_ptr = animp->by->value; | |
| } else { | |
| by_info.fieldType = 0; | |
| by_info.far_ptr = NULL; | |
| } | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { | |
| gf_svg_attributes_resolve_currentColor(&by_info, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&by_info, &rai->owner->parent_presentation_value); | |
| } | |
| switch ((animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR)) { | |
| case SMIL_CALCMODE_DISCRETE: | |
| { | |
| /* before half of the duration stay at 'from' and then switch to 'to' */ | |
| if (useFrom) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from)", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| gf_svg_attributes_muladd(from_coef, &from_info, 0, &by_info, &rai->interpolated_value, 0); | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from+by)", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| gf_svg_attributes_muladd(from_coef, &from_info, FIX_ONE, &by_info, &rai->interpolated_value, 0); | |
| } | |
| rai->previous_key_index = useFrom; | |
| } | |
| break; | |
| case SMIL_CALCMODE_SPLINE: | |
| case SMIL_CALCMODE_PACED: | |
| case SMIL_CALCMODE_LINEAR: | |
| default: | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (linear interpolation between from and from+by, coef: %f)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time)); | |
| gf_svg_attributes_muladd(from_coef, &from_info, normalized_simple_time, &by_info, &rai->interpolated_value, 0); | |
| break; | |
| } | |
| } | |
| } | |
| static void gf_svg_compute_path_anim(SMIL_Anim_RTI *rai, GF_Matrix2D *m, Fixed normalized_simple_time) | |
| { | |
| Fixed offset; | |
| offset = gf_mulfix(normalized_simple_time, rai->length); | |
| gf_mx2d_init(*m); | |
| gf_path_iterator_get_transform(rai->path_iterator, offset, 1, m, 1, 0); | |
| //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("offset: %f, position: (%f, %f)", offset, ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[2], ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[5])); | |
| switch (rai->rotate) { | |
| case SVG_NUMBER_AUTO: | |
| break; | |
| case SVG_NUMBER_AUTO_REVERSE: | |
| gf_mx2d_add_rotation(m, m->m[2], m->m[5], GF_PI); | |
| break; | |
| default: | |
| m->m[0] = FIX_ONE; | |
| m->m[1] = 0; | |
| m->m[3] = 0; | |
| m->m[4] = FIX_ONE; | |
| } | |
| } | |
| static void gf_smil_anim_animate_using_path(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) | |
| { | |
| Fixed interpolation_coefficient; | |
| gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL); | |
| if (rai->change_detection_mode) { | |
| if (rai->previous_coef == interpolation_coefficient) | |
| rai->interpolated_value_changed = 0; | |
| else { | |
| rai->interpolated_value_changed = 1; | |
| } | |
| } else { | |
| rai->previous_coef = interpolation_coefficient; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying path animation (coef: %f)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time)); | |
| gf_svg_compute_path_anim(rai, (GF_Matrix2D*)rai->interpolated_value.far_ptr, interpolation_coefficient); | |
| } | |
| } | |
| static void gf_smil_anim_compute_interpolation_value(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) | |
| { | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| if (rai->path) { | |
| gf_smil_anim_animate_using_path(rai, normalized_simple_time); | |
| } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) { | |
| gf_smil_anim_set(rai); | |
| } else if (rai->values_count) { | |
| /* Ignore 'from'/'to'/'by'*/ | |
| gf_smil_anim_animate_using_values(rai, normalized_simple_time); | |
| } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) { | |
| /* 'to' is not specified but 'by' is, so this is a 'by' animation or a 'from'-'by' animation */ | |
| gf_smil_anim_animate_from_by(rai, normalized_simple_time); | |
| } else { | |
| /* Ignore 'by' if specified */ | |
| gf_smil_anim_animate_from_to(rai, normalized_simple_time); | |
| } | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_COMPTIME, GF_LOG_DEBUG)) { | |
| char *str = gf_svg_dump_attribute(rai->anim_elt, &rai->interpolated_value); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Animation %s - Interpolation value changed for attribute %s, new value: %s \n", | |
| gf_node_get_scene_time(rai->anim_elt), gf_node_get_log_name(rai->anim_elt), | |
| gf_svg_get_attribute_name(rai->anim_elt, rai->owner->presentation_value.fieldIndex), str) | |
| ); | |
| if (str) gf_free(str); | |
| } | |
| #endif | |
| } | |
| void gf_smil_anim_set_anim_runtime_in_timing(GF_Node *n) | |
| { | |
| u32 i, j; | |
| SVGTimedAnimBaseElement *timed_elt = NULL; | |
| SMIL_Timing_RTI *rti = NULL; | |
| GF_Node *target = NULL; | |
| if (!n) return; | |
| timed_elt = (SVGTimedAnimBaseElement *)n; | |
| if (!gf_svg_is_animation_tag(n->sgprivate->tag)) return; | |
| target = timed_elt->xlinkp->href->target; | |
| if (!target) return; | |
| if (timed_elt->timingp) rti = timed_elt->timingp->runtime; | |
| if (!rti) return; | |
| rti->rai = NULL; | |
| for (i = 0; i < gf_node_animation_count(target); i++) { | |
| SMIL_Anim_RTI *rai_tmp; | |
| SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i); | |
| j=0; | |
| while ((rai_tmp = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { | |
| if (rai_tmp->timingp->runtime == rti) { | |
| rti->rai = rai_tmp; | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| static void gf_smil_anim_get_last_specified_value(SMIL_Anim_RTI *rai) | |
| { | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| if (!animp) return; | |
| if (rai->path) { | |
| if (!rai->last_specified_value.far_ptr) { | |
| rai->last_specified_value.far_ptr = gf_malloc(sizeof(GF_Matrix2D)); | |
| rai->last_specified_value.fieldType = SVG_Matrix2D_datatype; | |
| } | |
| gf_svg_compute_path_anim(rai, rai->last_specified_value.far_ptr, FIX_ONE); | |
| return; | |
| } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) { | |
| if (animp->to) { | |
| rai->last_specified_value.fieldType = animp->to->type; | |
| rai->last_specified_value.far_ptr = animp->to->value; | |
| } else { | |
| /* TODO ??? */ | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Animation %s - set element without to attribute\n", | |
| gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| } | |
| return; | |
| } | |
| if (rai->values_count) { | |
| /* Ignore from/to/by*/ | |
| rai->last_specified_value.fieldType = animp->values->type; | |
| rai->last_specified_value.far_ptr = gf_list_last(animp->values->values); | |
| } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) { | |
| rai->last_specified_value.fieldType = animp->by->type; | |
| rai->last_specified_value.far_ptr = animp->by->value; | |
| } else if (animp->to) { | |
| rai->last_specified_value.fieldType = animp->to->type; | |
| rai->last_specified_value.far_ptr = animp->to->value; | |
| } | |
| if (gf_svg_is_inherit(&rai->last_specified_value)) { | |
| rai->last_specified_value.fieldType = rai->owner->presentation_value.fieldType; | |
| rai->last_specified_value.far_ptr = rai->owner->presentation_value.far_ptr; | |
| } | |
| if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(rai->last_specified_value.fieldType)) { | |
| gf_svg_attributes_resolve_currentColor(&rai->last_specified_value, &rai->owner->current_color_value); | |
| gf_svg_attributes_resolve_inherit(&rai->last_specified_value, &rai->owner->parent_presentation_value); | |
| } | |
| } | |
| /* if the animation behavior is accumulative and this is not the first iteration, | |
| then we modify the interpolation value as follows: | |
| interpolation value += last specified value * number of iterations completed */ | |
| static void gf_smil_anim_apply_accumulate(SMIL_Anim_RTI *rai) | |
| { | |
| u32 nb_iterations; | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| SMILTimingAttributesPointers *timingp = rai->timingp; | |
| nb_iterations = (timingp->runtime->current_interval ? timingp->runtime->current_interval->nb_iterations : 1); | |
| if (rai->change_detection_mode) { | |
| if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM) | |
| && nb_iterations > 0 | |
| && rai->previous_iteration != (s32) nb_iterations) { | |
| /* if we actually do accumulation and the number of iteration is different, | |
| then we force the result as changed regardless of the result of the interpolation | |
| (TODO: check if this need to be improved)*/ | |
| rai->interpolated_value_changed = 1; | |
| } else { | |
| /* if we don't accumulate we leave the value of interpolated_value_changed unchanged */ | |
| } | |
| } else { | |
| if (nb_iterations > 0 && rai->previous_iteration != (s32) nb_iterations) { | |
| rai->previous_iteration = nb_iterations; | |
| } | |
| if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM) && nb_iterations > 0) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying accumulation (iteration #%d)\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), nb_iterations)); | |
| gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value, | |
| INT2FIX(nb_iterations), &rai->last_specified_value, | |
| &rai->interpolated_value, 1); | |
| if ((animp->from) && animp->by && (rai->last_specified_value.far_ptr == animp->by->value)) { | |
| /* this is a from-by animation, the last specified value is not the 'by' value but actually 'from'+'by', | |
| we need to add nb_iterations times from to the interpolated_value | |
| see (animate-elem-210-t.svg (upper two circles in the mid column, after 9s/14s */ | |
| GF_FieldInfo from_info; | |
| from_info.fieldType = rai->animp->from->type; | |
| from_info.far_ptr = rai->animp->from->value; | |
| gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value, | |
| INT2FIX(nb_iterations), &from_info, | |
| &rai->interpolated_value, 1); | |
| } | |
| } | |
| } | |
| } | |
| static void gf_smil_apply_additive(SMIL_Anim_RTI *rai) | |
| { | |
| SMILAnimationAttributesPointers *animp = rai->animp; | |
| if (rai->change_detection_mode) return; | |
| else { | |
| /* Apply additive behavior if required | |
| PV = (additive == sum ? PV + animp->IV : animp->IV); */ | |
| if (animp->additive && *animp->additive == SMIL_ADDITIVE_SUM) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying additive behavior\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| gf_svg_attributes_add((rai->is_first_anim ? &rai->owner->specified_value : &rai->owner->presentation_value), | |
| &rai->interpolated_value, | |
| &rai->owner->presentation_value, | |
| 1); | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_COMPTIME, GF_LOG_DEBUG)) { | |
| char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), | |
| gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str) | |
| ); | |
| if (str) gf_free(str); | |
| } | |
| #endif | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying non-additive behavior\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| /* FIXME: if we switch pointers to avoid copying values, | |
| we need to modify the address in the DOM node as well, | |
| we also need to take care about change detections. Not easy!! | |
| void *tmp = rai->owner->presentation_value.far_ptr; | |
| rai->owner->presentation_value.far_ptr = rai->interpolated_value.far_ptr; | |
| rai->interpolated_value.far_ptr = tmp; | |
| */ | |
| gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->interpolated_value, 1); | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_COMPTIME, GF_LOG_DEBUG)) { | |
| char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), | |
| gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str) | |
| ); | |
| if (str) gf_free(str); | |
| } | |
| #endif | |
| } | |
| } | |
| } | |
| static void gf_smil_anim_animate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) | |
| { | |
| if (!rti || !rti->rai || !rti->rai->animp) return; | |
| SMIL_Anim_RTI *rai = rti->rai; | |
| gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time); | |
| gf_smil_anim_apply_accumulate(rai); | |
| gf_smil_apply_additive(rai); | |
| } | |
| void gf_smil_anim_reset_variables(SMIL_Anim_RTI *rai) | |
| { | |
| if (!rai) return; | |
| /* we reset all the animation parameters to force computation of next interpolation value | |
| when the animation restarts */ | |
| rai->interpolated_value_changed = 0; | |
| rai->previous_key_index = -1; | |
| rai->previous_coef = -FIX_ONE; | |
| rai->previous_iteration = -1; | |
| rai->previous_keytime_index = 0; | |
| rai->anim_done = 0; | |
| } | |
| /* copy/paste of the animate function | |
| TODO: check if computations of interpolation value can be avoided. | |
| */ | |
| static void gf_smil_anim_freeze(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) | |
| { | |
| if (!rti || !rti->rai || !rti->rai->animp) return; | |
| SMIL_Anim_RTI *rai = rti->rai; | |
| if (rai->change_detection_mode) { | |
| if (rai->anim_done == 0) | |
| rai->interpolated_value_changed = 1; | |
| else | |
| rai->interpolated_value_changed = 0; | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying freeze behavior\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time); | |
| gf_smil_anim_apply_accumulate(rai); | |
| gf_smil_apply_additive(rai); | |
| rai->anim_done = 1; | |
| } | |
| } | |
| static void gf_smil_anim_remove(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) | |
| { | |
| SMIL_Anim_RTI *rai = rti->rai; | |
| if (!rai) return; | |
| if (rai->change_detection_mode) { | |
| if (rai->anim_done == 0) | |
| rai->interpolated_value_changed = 1; | |
| else | |
| rai->interpolated_value_changed = 0; | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Time %f - Animation %s - applying remove behavior\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); | |
| /* TODO: see if we can avoid this copy by switching pointers */ | |
| gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->owner->specified_value, 0); | |
| /* TODO: check if we need to apply additive behavior even in fill='remove' | |
| maybe (see animate-elem-211-t.svg) */ | |
| rai->anim_done = 1; | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_COMPTIME, GF_LOG_DEBUG)) { | |
| char *str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", | |
| gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), | |
| gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str) | |
| ); | |
| if (str) gf_free(str); | |
| } | |
| #endif | |
| } | |
| } | |
| static void gf_smil_anim_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time, GF_SGSMILTimingEvalState state) | |
| { | |
| SMIL_Anim_RTI *rai = rti->rai; | |
| switch (state) { | |
| case SMIL_TIMING_EVAL_REPEAT: | |
| /* we are starting a new cycle of animation, therefore we need to reset the previous state variables | |
| like previous_keytime_index ... */ | |
| gf_smil_anim_reset_variables(rai); | |
| case SMIL_TIMING_EVAL_UPDATE: | |
| gf_smil_anim_animate(rti, normalized_simple_time); | |
| break; | |
| case SMIL_TIMING_EVAL_FREEZE: | |
| gf_smil_anim_freeze(rti, normalized_simple_time); | |
| break; | |
| case SMIL_TIMING_EVAL_REMOVE: | |
| gf_smil_anim_remove(rti, normalized_simple_time); | |
| break; | |
| case SMIL_TIMING_EVAL_FRACTION: | |
| gf_smil_anim_animate(rti, normalized_simple_time); | |
| rti->evaluate_status = SMIL_TIMING_EVAL_NONE; | |
| break; | |
| /* | |
| discard should be done before in smil_notify_time | |
| case SMIL_TIMING_EVAL_DISCARD: | |
| break; | |
| */ | |
| default: | |
| break; | |
| } | |
| } | |
| /************************************************************************************** | |
| **************************************************************************************/ | |
| GF_EXPORT | |
| void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_props) | |
| { | |
| u32 count_all, i; | |
| u32 active_anim; | |
| #ifndef GPAC_DISABLE_LOG | |
| u32 time=0; | |
| if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { | |
| time = gf_sys_clock(); | |
| } | |
| #endif | |
| /* Perform all the animations on this node */ | |
| count_all = gf_node_animation_count(node); | |
| for (i = 0; i < count_all; i++) { | |
| GF_FieldInfo info; | |
| s32 j; | |
| u32 count; | |
| SMIL_AttributeAnimations *aa; | |
| aa = (SMIL_AttributeAnimations *)gf_node_animation_get(node, i); | |
| count = gf_list_count(aa->anims); | |
| if (!count) continue; | |
| aa->presentation_value_changed = 0; | |
| if (aa->is_property) { | |
| /* Storing the pointer to the parent presentation value, | |
| i.e. the presentation value produced at the parent level in the tree */ | |
| aa->parent_presentation_value = aa->presentation_value; | |
| aa->parent_presentation_value.far_ptr = | |
| gf_svg_get_property_pointer((SVG_Element *)node, aa->orig_dom_ptr, render_svg_props); | |
| /* Storing also the pointer to the presentation value of the color property | |
| (special handling of the keyword 'currentColor' if used in animation values) */ | |
| gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_color, 1, 1, &info); | |
| aa->current_color_value.far_ptr = info.far_ptr; | |
| } | |
| /* We start with the last animation (TODO in the execution order), then scan in the reverse order | |
| up to the first animation which is not additive, to determine if the presentation value will change | |
| We evaluate each animation, but only in the 'change_detection_mode' */ | |
| for (j = count-1; j >= 0; j--) { | |
| SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j); | |
| SMIL_Timing_RTI *rti = rai->timingp->runtime; | |
| rai->interpolated_value_changed = 0; | |
| /* The evaluate_status has been updated when notifying the new scene time to this animation, | |
| i.e. before the scene tree traversal */ | |
| if (rti->evaluate_status) { | |
| rai->change_detection_mode = 1; | |
| rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); | |
| aa->presentation_value_changed += rai->interpolated_value_changed; | |
| if (!rai->animp->additive || *rai->animp->additive == SMIL_ADDITIVE_REPLACE) { | |
| /* we don't need to check previous animations since this one will overwrite it */ | |
| j--; | |
| break; | |
| } | |
| } | |
| } | |
| active_anim = 0; | |
| if (aa->presentation_value_changed) { | |
| /* If the result of all the combined animations will produce a different result compared to the previous frame, | |
| we start in the forward order from the j were the previous step stopped (i.e. the first anim in replace mode) | |
| and evaluate each animation, in the computation mode (change_detection_mode = 0)*/ | |
| for (j++; j<(s32)count; j++) { | |
| SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j); | |
| SMIL_Timing_RTI *rti = rai->timingp->runtime; | |
| if (j == 0) rai->is_first_anim = 1; | |
| else rai->is_first_anim = 0; | |
| if (rti->evaluate_status) { | |
| rai->change_detection_mode = 0; | |
| rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); | |
| active_anim++; | |
| } | |
| } | |
| /* DEBUG: uncomment this line to remove animation effect, and keep animation computation */ | |
| // gf_svg_attributes_copy(&aa->presentation_value, &aa->specified_value, 0); | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_COMPTIME, GF_LOG_DEBUG)) { | |
| char *str = gf_svg_dump_attribute(node, &aa->presentation_value); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[SMIL Animation] Time %f - Element %s - Presentation value changed for attribute %s, new value: %s - dirty flags %x\n", | |
| gf_node_get_scene_time(node), gf_node_get_log_name(node), | |
| gf_svg_get_attribute_name(node, aa->presentation_value.fieldIndex), str, aa->dirty_flags) | |
| ); | |
| if (str) gf_free(str); | |
| } | |
| #endif | |
| } else { | |
| /* DEBUG: uncomment this line to remove animation effect, and keep animation computation */ | |
| // gf_svg_attributes_copy(&aa->presentation_value, &aa->specified_value, 0); | |
| } | |
| /* we only set dirty flags when a real flag is set to avoid unnecessary computation | |
| for example, it is not necessary to set it when the anim is an animateTransform | |
| since there is no associated flag */ | |
| if (aa->dirty_flags) { | |
| if (aa->presentation_value_changed) { | |
| gf_node_dirty_set(node, aa->dirty_flags, aa->dirty_parents); | |
| } else { | |
| /* WARNING - This does not work for use elements because apply_animations may be called several times */ | |
| if (active_anim) gf_node_dirty_clear(node, aa->dirty_flags); | |
| } | |
| } | |
| } | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { | |
| time_spent_in_anim += gf_sys_clock() - time; | |
| } | |
| #endif | |
| } | |
| GF_Node *gf_smil_anim_get_target(GF_Node *e) | |
| { | |
| XLinkAttributesPointers *xlinkp = NULL; | |
| if (!gf_svg_is_animation_tag(e->sgprivate->tag)) return NULL; | |
| xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp; | |
| return (xlinkp && xlinkp->href) ? xlinkp->href->target : NULL; | |
| } | |
| /* Attributes from the animation elements are not easy to use during runtime, | |
| the runtime info is a set of easy to use structures. | |
| This function initializes them (interpolation values ...) | |
| Needs to be called after gf_smil_timing_init_runtime_info */ | |
| void gf_smil_anim_init_runtime_info(GF_Node *e) | |
| { | |
| u32 i; | |
| GF_FieldInfo target_attribute; | |
| SMIL_AttributeAnimations *aa = NULL; | |
| SMIL_Anim_RTI *rai; | |
| XLinkAttributesPointers *xlinkp = NULL; | |
| SMILAnimationAttributesPointers *animp = NULL; | |
| SMILTimingAttributesPointers *timingp = NULL; | |
| GF_Node *target = NULL; | |
| if (!e) return; | |
| /* Filling animation structures to be independent of the SVG Element structure */ | |
| animp = ((SVGTimedAnimBaseElement *)e)->animp; | |
| timingp = ((SVGTimedAnimBaseElement *)e)->timingp; | |
| if (!animp || !timingp) return; | |
| xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp; | |
| target = xlinkp->href->target; | |
| memset(&target_attribute, 0, sizeof(GF_FieldInfo)); | |
| if (animp->attributeName && (animp->attributeName->name || animp->attributeName->tag)) { | |
| /* Filling the target_attribute structure with info on the animated attribute (type, pointer to data, ...) | |
| NOTE: the animated attribute is created with a default value, if it was not specified on the target element */ | |
| if (animp->attributeName->tag) { | |
| gf_node_get_attribute_by_tag(target, animp->attributeName->tag, 1, 1, &target_attribute); | |
| } else { | |
| gf_node_get_field_by_name(target, animp->attributeName->name, &target_attribute); | |
| } | |
| } else { | |
| /* All animation elements should have a target attribute except for animateMotion | |
| cf http://www.w3.org/mid/u403c21ajf1sjqtk58g0g38eaep9f9g2ss@hive.bjoern.hoehrmann.de | |
| "For animateMotion, the attributeName is implied and cannot be specified; | |
| animateTransform requires specification of the attribute name and any attribute that is | |
| a transform-like attribute can be a target, e.g. gradientTransform."*/ | |
| switch (e->sgprivate->tag) { | |
| case TAG_SVG_animateMotion: | |
| /* Explicit creation of the pseudo 'motionTransform' attribute since it cannot be specified */ | |
| gf_node_get_attribute_by_tag(target, TAG_SVG_ATT_motionTransform, 1, 0, &target_attribute); | |
| gf_mx2d_init(*(GF_Matrix2D *)target_attribute.far_ptr); | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Missing attributeName attribute on element %s\n", | |
| gf_node_get_log_name((GF_Node*)e) )); | |
| return; | |
| } | |
| } | |
| if (animp->attributeType && *animp->attributeType == SMIL_ATTRIBUTETYPE_CSS) { | |
| /* see example animate-elem-219-t.svg from the SVG test suite, upper row */ | |
| if (!gf_svg_is_property(target, &target_attribute)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_COMPTIME, | |
| ("[SMIL Animation] Using CSS attributeType for an animation on an attribute which is not a property %s\n", | |
| gf_node_get_log_name((GF_Node*)e) )); | |
| return; | |
| } | |
| } | |
| /* Creation and setup of the runtime structure for animation */ | |
| GF_SAFEALLOC(rai, SMIL_Anim_RTI) | |
| if (!rai) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Animation] Failed to allocated SMIL anim RTI\n")); | |
| return; | |
| } | |
| rai->anim_elt = e; | |
| rai->animp = animp; | |
| rai->timingp = timingp; | |
| rai->xlinkp = xlinkp; | |
| gf_mx2d_init(rai->identity); | |
| rai->default_transform_value.far_ptr = &rai->identity; | |
| rai->default_transform_value.fieldType = SVG_Transform_datatype; | |
| /* the interpolated value has the same type as the target attribute, | |
| but we need to create a new pointer to hold its value */ | |
| rai->interpolated_value = target_attribute; | |
| rai->interpolated_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType); | |
| /* there has not been any interpolation yet, so the previous key index and interpolation coefficient | |
| shall not be set*/ | |
| gf_smil_anim_reset_variables(rai); | |
| rai->values_count = (animp->values ? gf_list_count(animp->values->values) : 0); | |
| rai->key_times_count = (animp->keyTimes ? gf_list_count(*animp->keyTimes) : 0); | |
| rai->key_points_count = (animp->keyPoints ? gf_list_count(*animp->keyPoints) : 0); | |
| rai->key_splines_count = (animp->keySplines ? gf_list_count(*animp->keySplines) : 0); | |
| if (!rai->values_count && /* 'values' attribute not specified */ | |
| (!animp->to || animp->to->type == 0) && /* 'to' attribute not specified */ | |
| (!animp->from || animp->from->type == 0) && /* 'from' attribute not specified */ | |
| (animp->by && animp->by->type != 0)) { /* 'by' attribute specified */ | |
| /* if this is a 'by' animation without from the animation is defined to be additive | |
| see http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#AnimationNS-FromToBy | |
| we override the additive attribute */ | |
| if (!animp->additive) { | |
| /* this case can only happen with dynamic allocation of attributes */ | |
| GF_FieldInfo info; | |
| gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_additive, 1, 0, &info); | |
| animp->additive = info.far_ptr; | |
| } | |
| if (*animp->additive == SMIL_ADDITIVE_REPLACE) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_COMPTIME, ("[SMIL Animation] Warning: by-animations cannot use additive=\"replace\"\n")); | |
| } | |
| *animp->additive = SMIL_ADDITIVE_SUM; | |
| } | |
| /*TODO | |
| http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation | |
| To animation defines its own kind of additive semantics, so the additive attribute is ignored. | |
| */ | |
| /*TODO | |
| http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation | |
| Because to animation is defined in terms of absolute values of the target attribute, | |
| cumulative animation is not defined: | |
| */ | |
| /* TODO | |
| http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-setElement | |
| The set element is non-additive. The additive and accumulate attributes are not allowed, | |
| and will be ignored if specified. | |
| */ | |
| /* For animateMotion, we need to retrieve the value of the rotate attribute, retrieve the path either | |
| from the 'path' attribute or from the 'mpath' element, and then initialize the path iterator*/ | |
| if (e->sgprivate->tag == TAG_SVG_animateMotion) { | |
| GF_Path *the_path = NULL; | |
| GF_ChildNodeItem *child = NULL; | |
| GF_FieldInfo info; | |
| if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_rotate, 0, 0, &info) == GF_OK) { | |
| rai->rotate = ((SVG_Rotate *)info.far_ptr)->type; | |
| } else { | |
| rai->rotate = SVG_NUMBER_VALUE; | |
| } | |
| if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_path, 0, 0, &info) == GF_OK) { | |
| the_path = ((SVG_PathData *)info.far_ptr); | |
| } | |
| child = ((SVG_Element *)e)->children; | |
| if ((!animp->to || animp->to->type == 0) && | |
| (!animp->by || animp->by->type == 0) && | |
| (!animp->values || animp->values->type == 0)) { | |
| #if USE_GF_PATH | |
| if (!gf_path_is_empty(the_path)) { | |
| rai->path = the_path; | |
| rai->path_iterator = gf_path_iterator_new(rai->path); | |
| rai->length = gf_path_iterator_get_length(rai->path_iterator); | |
| } | |
| #else | |
| rai->path = gf_path_new(); | |
| if (gf_list_count(the_path->points)) { | |
| gf_svg_path_build(rai->path, the_path->commands, the_path->points); | |
| rai->path_iterator = gf_path_iterator_new(rai->path); | |
| rai->length = gf_path_iterator_get_length(rai->path_iterator); | |
| } | |
| #endif | |
| else { | |
| while (child) { | |
| GF_Node *used_path = NULL; | |
| u32 child_tag = gf_node_get_tag(child->node); | |
| if (child_tag == TAG_SVG_mpath) { | |
| if (gf_node_get_attribute_by_tag(child->node, TAG_XLINK_ATT_href, 0, 0, &info) == GF_OK) { | |
| XMLRI *iri = (XMLRI *)info.far_ptr; | |
| if (iri->target) used_path = iri->target; | |
| else if (iri->string) used_path = | |
| (GF_Node *)gf_sg_find_node_by_name(gf_node_get_graph(child->node), iri->string); | |
| if (used_path && gf_node_get_tag(used_path) == TAG_SVG_path) { | |
| gf_node_get_attribute_by_tag(used_path, TAG_SVG_ATT_d, 1, 0, &info); | |
| #if USE_GF_PATH | |
| rai->path = (SVG_PathData *)info.far_ptr; | |
| #else | |
| gf_svg_path_build(rai->path, | |
| ((SVG_PathData *)info.far_ptr)->commands, | |
| ((SVG_PathData *)info.far_ptr)->points); | |
| #endif | |
| rai->path_iterator = gf_path_iterator_new(rai->path); | |
| rai->length = gf_path_iterator_get_length(rai->path_iterator); | |
| } | |
| } | |
| break; | |
| } | |
| child = child->next; | |
| } | |
| } | |
| } | |
| } | |
| /* for all animations, check if there is already one animation on this attribute, | |
| if yes, get the list and append the new animation runtime info | |
| if no, create a list and add the new animation runtime info. */ | |
| for (i = 0; i < gf_node_animation_count(target); i++) { | |
| aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i); | |
| if (aa->presentation_value.fieldIndex == target_attribute.fieldIndex) { | |
| gf_list_add(aa->anims, rai); | |
| break; | |
| } | |
| aa = NULL; | |
| } | |
| if (!aa) { | |
| GF_SAFEALLOC(aa, SMIL_AttributeAnimations) | |
| if (!aa) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Animation] Failed to allocated SMIL attribue ani\n")); | |
| return; | |
| } | |
| /* We determine if the animated attribute is a property since this changes quite a lot the animation model */ | |
| aa->is_property = gf_svg_is_property(target, &target_attribute); | |
| aa->current_color_value.fieldType = SVG_Paint_datatype; | |
| /* We copy (one copy for all animations on the same attribute) the DOM specified | |
| value before any animation starts (because the animation will override it), | |
| we also save the initial memory address of the specified value (orig_dom_ptr) | |
| for inheritance hack */ | |
| aa->specified_value = target_attribute; | |
| aa->orig_dom_ptr = aa->specified_value.far_ptr; | |
| aa->specified_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType); | |
| gf_svg_attributes_copy(&aa->specified_value, &target_attribute, 0); | |
| /* Now, the initial memory address of the specified value holds the presentation value, | |
| and the presentation value is initialized */ | |
| aa->presentation_value = target_attribute; | |
| aa->anims = gf_list_new(); | |
| gf_list_add(aa->anims, rai); | |
| gf_node_animation_add(target, aa); | |
| /* determine what the rendering will need to do when the animation runs */ | |
| aa->dirty_flags = gf_svg_get_modification_flags((SVG_Element *)target, &target_attribute); | |
| /* If the animation will result in a change of geometry or of the display property, | |
| this animation will require traversing the tree, we need to inform the parents of the target node */ | |
| aa->dirty_parents = 0; | |
| if (aa->dirty_flags & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_SVG_DISPLAY_DIRTY)) aa->dirty_parents = 1; | |
| } | |
| rai->owner = aa; | |
| gf_smil_anim_get_last_specified_value(rai); | |
| /* for animation (unlike other timed elements like video), the evaluation (i.e. interpolation) cannot be done | |
| during timing evaluation, because due to inheritance, interpolation can only be computed | |
| during scene tree traversal, therefore we need to postpone evaluation of the timed element */ | |
| timingp->runtime->postpone = 1; | |
| timingp->runtime->evaluate = gf_smil_anim_evaluate; | |
| } | |
| void gf_smil_anim_delete_runtime_info(SMIL_Anim_RTI *rai) | |
| { | |
| gf_svg_delete_attribute_value(rai->interpolated_value.fieldType, | |
| rai->interpolated_value.far_ptr, | |
| rai->anim_elt->sgprivate->scenegraph); | |
| if (rai->path) { | |
| gf_svg_delete_attribute_value(rai->last_specified_value.fieldType, | |
| rai->last_specified_value.far_ptr, | |
| rai->anim_elt->sgprivate->scenegraph); | |
| #if USE_GF_PATH | |
| #else | |
| if (rai->path) gf_path_del(rai->path); | |
| #endif | |
| } | |
| if (rai->path_iterator) gf_path_iterator_del(rai->path_iterator); | |
| gf_free(rai); | |
| } | |
| void gf_smil_anim_remove_from_target(GF_Node *anim, GF_Node *target) | |
| { | |
| u32 i, j; | |
| if (!target) return; | |
| for (i = 0; i < gf_node_animation_count((GF_Node *)target); i ++) { | |
| SMIL_Anim_RTI *rai; | |
| SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get((GF_Node *)target, i); | |
| j=0; | |
| while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { | |
| if ((GF_Node *)rai->anim_elt == anim) { | |
| gf_list_rem(aa->anims, j-1); | |
| gf_smil_anim_delete_runtime_info(rai); | |
| break; | |
| } | |
| } | |
| if (gf_list_count(aa->anims) == 0) { | |
| gf_list_del(aa->anims); | |
| gf_svg_delete_attribute_value(aa->specified_value.fieldType, | |
| aa->specified_value.far_ptr, | |
| target->sgprivate->scenegraph); | |
| aa->presentation_value.far_ptr = aa->orig_dom_ptr; | |
| gf_node_animation_rem((GF_Node *)target, i); | |
| gf_free(aa); | |
| } | |
| } | |
| } | |
| void gf_smil_anim_delete_animations(GF_Node *e) | |
| { | |
| u32 i, j; | |
| for (i = 0; i < gf_node_animation_count(e); i ++) { | |
| SMIL_Anim_RTI *rai; | |
| SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(e, i); | |
| gf_svg_delete_attribute_value(aa->specified_value.fieldType, | |
| aa->specified_value.far_ptr, | |
| e->sgprivate->scenegraph); | |
| j=0; | |
| while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { | |
| rai->xlinkp->href->target = NULL; | |
| gf_smil_anim_delete_runtime_info(rai); | |
| } | |
| gf_list_del(aa->anims); | |
| gf_free(aa); | |
| } | |
| gf_node_animation_del(e); | |
| } | |
| void gf_smil_anim_init_discard(GF_Node *node) | |
| { | |
| SVGAllAttributes all_atts; | |
| XLinkAttributesPointers *xlinkp = NULL; | |
| SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node; | |
| gf_smil_timing_init_runtime_info(node); | |
| gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); | |
| GF_SAFEALLOC(e->xlinkp, XLinkAttributesPointers); | |
| if (!e->xlinkp) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME,("[SMIL] Error creating anim xlink attrib\n")); | |
| return; | |
| } | |
| xlinkp = e->xlinkp; | |
| xlinkp->href = all_atts.xlink_href; | |
| xlinkp->type = all_atts.xlink_type; | |
| e->timingp->runtime->evaluate_status = SMIL_TIMING_EVAL_DISCARD; | |
| } | |
| void gf_smil_anim_init_node(GF_Node *node) | |
| { | |
| XLinkAttributesPointers *xlinkp; | |
| SMILAnimationAttributesPointers *animp = NULL; | |
| SVGAllAttributes all_atts; | |
| SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node; | |
| gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); | |
| e->xlinkp = gf_malloc(sizeof(XLinkAttributesPointers)); | |
| xlinkp = e->xlinkp; | |
| xlinkp->href = all_atts.xlink_href; | |
| xlinkp->type = all_atts.xlink_type; | |
| /*perform init of default values | |
| When the xlink:href attribute of animation is not set, the target defaults to the parent element */ | |
| if (!xlinkp->href) { | |
| GF_FieldInfo info; | |
| gf_node_get_attribute_by_tag((GF_Node *)node, TAG_XLINK_ATT_href, 1, 0, &info); | |
| xlinkp->href = info.far_ptr; | |
| xlinkp->href->type = XMLRI_ELEMENTID; | |
| xlinkp->href->target = gf_node_get_parent(node, 0); | |
| } | |
| if (xlinkp->href->type == XMLRI_STRING) { | |
| if (!xlinkp->href->string) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME,("Error: IRI not initialized\n")); | |
| return; | |
| } else { | |
| GF_Node *n; | |
| n = (GF_Node*)gf_sg_find_node_by_name(gf_node_get_graph(node), xlinkp->href->string); | |
| if (n) { | |
| xlinkp->href->type = XMLRI_ELEMENTID; | |
| xlinkp->href->target = n; | |
| gf_node_register_iri(node->sgprivate->scenegraph, xlinkp->href); | |
| } else { | |
| return; | |
| } | |
| } | |
| } | |
| if (!xlinkp->href->target) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_COMPTIME,("Trying to initialize an animation when the target is not known\n")); | |
| return; | |
| } | |
| // We may not have an attribute name, when using an animateMotion element | |
| if (node->sgprivate->tag != TAG_SVG_animateMotion && !all_atts.attributeName) { | |
| goto end_init; | |
| } | |
| /* if an attribute (to, from or by) is present but its type is not set | |
| (e.g. it could not be determined before, the target was not known), we try to get the type from the target */ | |
| if ( (all_atts.to && (all_atts.to->type==0)) | |
| || (all_atts.from && (all_atts.from->type==0)) | |
| || (all_atts.by && (all_atts.by->type==0)) | |
| ) { | |
| GF_FieldInfo info; | |
| if (gf_node_get_attribute_by_name((GF_Node *)xlinkp->href->target, all_atts.attributeName->name, 0, 1, 1, &info)==GF_OK) { | |
| u32 anim_value_type = info.fieldType; | |
| u32 i; | |
| for (i=0; i<3; i++) { | |
| u32 tag = 0; | |
| switch (i) { | |
| case 0: | |
| tag=TAG_SVG_ATT_to; | |
| break; | |
| case 1: | |
| tag=TAG_SVG_ATT_from; | |
| break; | |
| case 2: | |
| tag=TAG_SVG_ATT_by; | |
| break; | |
| } | |
| if (gf_node_get_attribute_by_tag((GF_Node *)node, tag, 0, 0, &info)==GF_OK) { | |
| SMIL_AnimateValue *attval = info.far_ptr; | |
| if (attval->type==0) { | |
| SVG_String string = attval->value; | |
| attval->value = NULL; | |
| if (string) { | |
| u32 slen = (u32) strlen(string); | |
| if (gf_utf8_is_legal(string, slen+1)) | |
| gf_svg_parse_attribute((GF_Node *)node, &info, string, anim_value_type); | |
| gf_free(string); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| e->animp = gf_malloc(sizeof(SMILAnimationAttributesPointers)); | |
| animp = e->animp; | |
| animp->accumulate = all_atts.accumulate; | |
| animp->additive = all_atts.additive; | |
| animp->attributeName = all_atts.attributeName; | |
| animp->attributeType = all_atts.attributeType; | |
| animp->by = all_atts.by; | |
| animp->calcMode = all_atts.calcMode; | |
| animp->from = all_atts.from; | |
| animp->keySplines = all_atts.keySplines; | |
| animp->keyTimes = all_atts.keyTimes; | |
| animp->lsr_enabled = all_atts.lsr_enabled; | |
| animp->to = all_atts.to; | |
| animp->type = all_atts.transform_type; | |
| animp->values = all_atts.values; | |
| if (node->sgprivate->tag == TAG_SVG_animateMotion) { | |
| e->animp->keyPoints = all_atts.keyPoints; | |
| e->animp->origin = all_atts.origin; | |
| e->animp->path = all_atts.path; | |
| e->animp->rotate = all_atts.rotate; | |
| } else { | |
| e->animp->keyPoints = NULL; | |
| e->animp->origin = NULL; | |
| e->animp->path = NULL; | |
| e->animp->rotate = NULL; | |
| } | |
| end_init: | |
| gf_smil_timing_init_runtime_info(node); | |
| gf_smil_anim_init_runtime_info(node); | |
| gf_smil_anim_set_anim_runtime_in_timing(node); | |
| } | |
| #endif /*GPAC_DISABLE_SVG*/ |