/* * 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 and +-Inf */ static double gtk_css_animation_get_iteration (GtkCssAnimation *animation, gint64 for_time_us) { gint64 elapsed; double iterations; elapsed = for_time_us - animation->timestamp; iterations = (double) elapsed / animation->duration; return iterations; } 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 GtkBitmask * gtk_css_animation_set_values (GtkStyleAnimation *style_animation, GtkBitmask *changed, 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 changed; 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_value (values, i)); /* XXX: Is using 0 correct here? */ _gtk_css_computed_values_set_value (values, property_id, value, 0, NULL); _gtk_css_value_unref (value); changed = _gtk_bitmask_set (changed, property_id, TRUE); } return changed; } static gboolean gtk_css_animation_is_finished (GtkStyleAnimation *style_animation, gint64 at_time_us) { return FALSE; } 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; } static void _gtk_css_animation_init (GtkCssAnimation *animation) { } GtkStyleAnimation * _gtk_css_animation_new (const char *name, GtkCssKeyframes *keyframes, gint64 start_time_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); animation->timestamp = start_time_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; }