/* * Copyright © 2012 Red Hat Inc. * * This library 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.1 of the License, or (at your option) any later version. * * This library 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. If not, see . * * Authors: Benjamin Otte */ #include "config.h" #include "gtkcssanimationprivate.h" #include "gtkcsseasevalueprivate.h" #include G_DEFINE_TYPE (GtkCssAnimation, _gtk_css_animation, GTK_TYPE_STYLE_ANIMATION) /* NB: Return value can be negative (if animation hasn't started yet) */ static gint64 gtk_css_animation_get_elapsed (GtkCssAnimation *animation, gint64 for_time_us) { if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED) return animation->timestamp; else return for_time_us - animation->timestamp; } /* NB: Return value can be negative and +-Inf */ static double gtk_css_animation_get_iteration (GtkCssAnimation *animation, gint64 for_time_us) { return (double) gtk_css_animation_get_elapsed (animation, for_time_us) / animation->duration; } static gboolean gtk_css_animation_is_executing_at_iteration (GtkCssAnimation *animation, double iteration) { switch (animation->fill_mode) { case GTK_CSS_FILL_NONE: return iteration >= 0 && iteration <= animation->iteration_count; case GTK_CSS_FILL_FORWARDS: return iteration >= 0; case GTK_CSS_FILL_BACKWARDS: return iteration <= animation->iteration_count; case GTK_CSS_FILL_BOTH: return TRUE; default: g_return_val_if_reached (FALSE); } } static double gtk_css_animation_get_progress_from_iteration (GtkCssAnimation *animation, double iteration) { double d; iteration = CLAMP (iteration, 0, animation->iteration_count); switch (animation->direction) { case GTK_CSS_DIRECTION_NORMAL: if (iteration == animation->iteration_count) return 1; else return iteration - floor (iteration); case GTK_CSS_DIRECTION_REVERSE: if (iteration == animation->iteration_count) return 1; else return ceil (iteration) - iteration; case GTK_CSS_DIRECTION_ALTERNATE: d = floor (iteration); if (fmod (d, 2)) return iteration - d; else return 1 + d - iteration; case GTK_CSS_DIRECTION_ALTERNATE_REVERSE: d = floor (iteration); if (fmod (d, 2)) return 1 + d - iteration; else return iteration - d; default: g_return_val_if_reached (0); } } static void gtk_css_animation_set_values (GtkStyleAnimation *style_animation, gint64 for_time_us, GtkCssComputedValues *values) { GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation); double iteration, progress; guint i; iteration = gtk_css_animation_get_iteration (animation, for_time_us); if (!gtk_css_animation_is_executing_at_iteration (animation, iteration)) return; progress = gtk_css_animation_get_progress_from_iteration (animation, iteration); progress = _gtk_css_ease_value_transform (animation->ease, progress); for (i = 0; i < _gtk_css_keyframes_get_n_properties (animation->keyframes); i++) { GtkCssValue *value; guint property_id; property_id = _gtk_css_keyframes_get_property_id (animation->keyframes, i); value = _gtk_css_keyframes_get_value (animation->keyframes, i, progress, _gtk_css_computed_values_get_intrinsic_value (values, i)); _gtk_css_computed_values_set_animated_value (values, property_id, value); _gtk_css_value_unref (value); } } static gboolean gtk_css_animation_is_finished (GtkStyleAnimation *style_animation, gint64 at_time_us) { return FALSE; } static gboolean gtk_css_animation_is_static (GtkStyleAnimation *style_animation, gint64 at_time_us) { GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation); double iteration; if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED) return TRUE; iteration = gtk_css_animation_get_iteration (animation, at_time_us); return iteration >= animation->iteration_count; } static void gtk_css_animation_finalize (GObject *object) { GtkCssAnimation *animation = GTK_CSS_ANIMATION (object); g_free (animation->name); _gtk_css_keyframes_unref (animation->keyframes); _gtk_css_value_unref (animation->ease); G_OBJECT_CLASS (_gtk_css_animation_parent_class)->finalize (object); } static void _gtk_css_animation_class_init (GtkCssAnimationClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkStyleAnimationClass *animation_class = GTK_STYLE_ANIMATION_CLASS (klass); object_class->finalize = gtk_css_animation_finalize; animation_class->set_values = gtk_css_animation_set_values; animation_class->is_finished = gtk_css_animation_is_finished; animation_class->is_static = gtk_css_animation_is_static; } static void _gtk_css_animation_init (GtkCssAnimation *animation) { } GtkStyleAnimation * _gtk_css_animation_new (const char *name, GtkCssKeyframes *keyframes, gint64 timestamp, gint64 delay_us, gint64 duration_us, GtkCssValue *ease, GtkCssDirection direction, GtkCssPlayState play_state, GtkCssFillMode fill_mode, double iteration_count) { GtkCssAnimation *animation; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (keyframes != NULL, NULL); g_return_val_if_fail (ease != NULL, NULL); g_return_val_if_fail (iteration_count >= 0, NULL); animation = g_object_new (GTK_TYPE_CSS_ANIMATION, NULL); animation->name = g_strdup (name); animation->keyframes = _gtk_css_keyframes_ref (keyframes); if (play_state == GTK_CSS_PLAY_STATE_PAUSED) animation->timestamp = - delay_us; else animation->timestamp = timestamp + delay_us; animation->duration = duration_us; animation->ease = _gtk_css_value_ref (ease); animation->direction = direction; animation->play_state = play_state; animation->fill_mode = fill_mode; animation->iteration_count = iteration_count; return GTK_STYLE_ANIMATION (animation); } const char * _gtk_css_animation_get_name (GtkCssAnimation *animation) { g_return_val_if_fail (GTK_IS_CSS_ANIMATION (animation), NULL); return animation->name; } GtkStyleAnimation * _gtk_css_animation_copy (GtkCssAnimation *animation, gint64 at_time_us, GtkCssPlayState play_state) { g_return_val_if_fail (GTK_IS_CSS_ANIMATION (animation), NULL); if (animation->play_state == play_state) return g_object_ref (animation); return _gtk_css_animation_new (animation->name, animation->keyframes, at_time_us, - gtk_css_animation_get_elapsed (animation, at_time_us), animation->duration, animation->ease, animation->direction, play_state, animation->fill_mode, animation->iteration_count); }