[gtk+] cssvalue: Add GtkCssEaseValue



commit 75111091031504c4d14d520f8b323032c78efe4e
Author: Benjamin Otte <otte redhat com>
Date:   Sun Apr 1 06:20:48 2012 +0200

    cssvalue: Add GtkCssEaseValue
    
    This is supposed to hold the transition-easing-function and
    animation-easing-function values.

 gtk/Makefile.am              |    2 +
 gtk/gtkcsseasevalue.c        |  370 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkcsseasevalueprivate.h |   41 +++++
 3 files changed, 413 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 351545b..54faaa1 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -426,6 +426,7 @@ gtk_private_h_sources =		\
 	gtkcssarrayvalueprivate.h	\
 	gtkcsscomputedvaluesprivate.h \
 	gtkcsscustompropertyprivate.h \
+	gtkcsseasevalueprivate.h	\
 	gtkcssenumvalueprivate.h	\
 	gtkcssimagegradientprivate.h	\
 	gtkcssimagelinearprivate.h	\
@@ -631,6 +632,7 @@ gtk_base_c_sources = 		\
 	gtkcssarrayvalue.c	\
 	gtkcsscomputedvalues.c	\
 	gtkcsscustomproperty.c	\
+	gtkcsseasevalue.c	\
 	gtkcssenumvalue.c	\
 	gtkcssimage.c		\
 	gtkcssimagegradient.c	\
diff --git a/gtk/gtkcsseasevalue.c b/gtk/gtkcsseasevalue.c
new file mode 100644
index 0000000..be2c44e
--- /dev/null
+++ b/gtk/gtkcsseasevalue.c
@@ -0,0 +1,370 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkcsseasevalueprivate.h"
+
+#include <math.h>
+
+typedef enum {
+  GTK_CSS_EASE_CUBIC_BEZIER,
+  GTK_CSS_EASE_STEPS
+} GtkCssEaseType;
+
+struct _GtkCssValue {
+  GTK_CSS_VALUE_BASE
+  GtkCssEaseType type;
+  union {
+    struct {
+      double x1;
+      double y1;
+      double x2;
+      double y2;
+    } cubic;
+    struct {
+      guint steps;
+      gboolean start;
+    } steps;
+  } u;
+};
+
+static void
+gtk_css_value_ease_free (GtkCssValue *value)
+{
+  g_slice_free (GtkCssValue, value);
+}
+
+static gboolean
+gtk_css_value_ease_equal (const GtkCssValue *ease1,
+                          const GtkCssValue *ease2)
+{
+  if (ease1->type != ease2->type)
+    return FALSE;
+  
+  switch (ease1->type)
+    {
+    case GTK_CSS_EASE_CUBIC_BEZIER:
+      return ease1->u.cubic.x1 == ease2->u.cubic.x1 &&
+             ease1->u.cubic.y1 == ease2->u.cubic.y1 &&
+             ease1->u.cubic.x2 == ease2->u.cubic.x2 &&
+             ease1->u.cubic.y2 == ease2->u.cubic.y2;
+    case GTK_CSS_EASE_STEPS:
+      return ease1->u.steps.steps == ease2->u.steps.steps &&
+             ease1->u.steps.start == ease2->u.steps.start;
+    default:
+      g_assert_not_reached ();
+      return FALSE;
+    }
+}
+
+static GtkCssValue *
+gtk_css_value_ease_transition (GtkCssValue *start,
+                               GtkCssValue *end,
+                               double       progress)
+{
+  return NULL;
+}
+
+static void
+gtk_css_value_ease_print (const GtkCssValue *ease,
+                          GString           *string)
+{
+  switch (ease->type)
+    {
+    case GTK_CSS_EASE_CUBIC_BEZIER:
+      if (ease->u.cubic.x1 == 0.25 && ease->u.cubic.y1 == 0.1 &&
+          ease->u.cubic.x2 == 0.25 && ease->u.cubic.y2 == 1.0)
+        g_string_append (string, "ease");
+      else if (ease->u.cubic.x1 == 0.0 && ease->u.cubic.y1 == 0.0 &&
+               ease->u.cubic.x2 == 1.0 && ease->u.cubic.y2 == 1.0)
+        g_string_append (string, "linear");
+      else if (ease->u.cubic.x1 == 0.42 && ease->u.cubic.y1 == 0.0 &&
+               ease->u.cubic.x2 == 1.0  && ease->u.cubic.y2 == 1.0)
+        g_string_append (string, "ease-in");
+      else if (ease->u.cubic.x1 == 0.0  && ease->u.cubic.y1 == 0.0 &&
+               ease->u.cubic.x2 == 0.58 && ease->u.cubic.y2 == 1.0)
+        g_string_append (string, "ease-out");
+      else if (ease->u.cubic.x1 == 0.42 && ease->u.cubic.y1 == 0.0 &&
+               ease->u.cubic.x2 == 0.58 && ease->u.cubic.y2 == 1.0)
+        g_string_append (string, "ease-in-out");
+      else
+        g_string_append_printf (string, "cubic-bezier(%g,%g,%g,%g)", 
+                                ease->u.cubic.x1, ease->u.cubic.y1,
+                                ease->u.cubic.x2, ease->u.cubic.y2);
+      break;
+    case GTK_CSS_EASE_STEPS:
+      if (ease->u.steps.steps == 1)
+        {
+          g_string_append (string, ease->u.steps.start ? "step-start" : "step-end");
+        }
+      else
+        {
+          g_string_append_printf (string, "steps(%u%s)", ease->u.steps.steps, ease->u.steps.start ? ",start" : "");
+        }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static const GtkCssValueClass GTK_CSS_VALUE_EASE = {
+  gtk_css_value_ease_free,
+  gtk_css_value_ease_equal,
+  gtk_css_value_ease_transition,
+  gtk_css_value_ease_print
+};
+
+GtkCssValue *
+_gtk_css_ease_value_new_cubic_bezier (double x1,
+                                      double y1,
+                                      double x2,
+                                      double y2)
+{
+  GtkCssValue *value;
+
+  g_return_val_if_fail (x1 >= 0.0, NULL);
+  g_return_val_if_fail (x1 <= 1.0, NULL);
+  g_return_val_if_fail (x2 >= 0.0, NULL);
+  g_return_val_if_fail (x2 <= 1.0, NULL);
+
+  value = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_EASE);
+  
+  value->type = GTK_CSS_EASE_CUBIC_BEZIER;
+  value->u.cubic.x1 = x1;
+  value->u.cubic.y1 = y1;
+  value->u.cubic.x2 = x2;
+  value->u.cubic.y2 = y2;
+
+  return value;
+}
+
+static GtkCssValue *
+_gtk_css_ease_value_new_steps (guint n_steps,
+                               gboolean start)
+{
+  GtkCssValue *value;
+
+  g_return_val_if_fail (n_steps > 0, NULL);
+
+  value = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_EASE);
+  
+  value->type = GTK_CSS_EASE_STEPS;
+  value->u.steps.steps = n_steps;
+  value->u.steps.start = start;
+
+  return value;
+}
+
+static const struct {
+  const char *name;
+  guint is_bezier :1;
+  guint needs_custom :1;
+  double values[4];
+} parser_values[] = {
+  { "linear",       TRUE,  FALSE, { 0.0,  0.0, 1.0,  1.0 } },
+  { "ease-in-out",  TRUE,  FALSE, { 0.42, 0.0, 0.58, 1.0 } },
+  { "ease-in",      TRUE,  FALSE, { 0.42, 0.0, 1.0,  1.0 } },
+  { "ease-out",     TRUE,  FALSE, { 0.0,  0.0, 0.58, 1.0 } },
+  { "ease",         TRUE,  FALSE, { 0.25, 0.1, 0.25, 1.0 } },
+  { "step-start",   FALSE, FALSE, { 1.0,  1.0, 0.0,  0.0 } },
+  { "step-end",     FALSE, FALSE, { 1.0,  0.0, 0.0,  0.0 } },
+  { "steps",        FALSE, TRUE,  { 0.0,  0.0, 0.0,  0.0 } },
+  { "cubic-bezier", TRUE,  TRUE,  { 0.0,  0.0, 0.0,  0.0 } }
+};
+
+gboolean
+_gtk_css_ease_value_can_parse (GtkCssParser *parser)
+{
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (parser_values); i++)
+    {
+      if (_gtk_css_parser_has_prefix (parser, parser_values[i].name))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static GtkCssValue *
+gtk_css_ease_value_parse_cubic_bezier (GtkCssParser *parser)
+{
+  double values[4];
+  guint i;
+
+  for (i = 0; i < 4; i++)
+    {
+      if (!_gtk_css_parser_try (parser, i ? "," : "(", TRUE))
+        {
+          _gtk_css_parser_error (parser, "Expected '%s'", i ? "," : "(");
+          return NULL;
+        }
+      if (!_gtk_css_parser_try_double (parser, &values[i]))
+        {
+          _gtk_css_parser_error (parser, "Expected a number");
+          return NULL;
+        }
+      if ((i == 0 || i == 2) &&
+          (values[i] < 0 || values[i] > 1.0))
+        {
+          _gtk_css_parser_error (parser, "value %g out of range. Must be from 0.0 to 1.0", values[i]);
+          return NULL;
+        }
+    }
+
+  if (!_gtk_css_parser_try (parser, ")", TRUE))
+    {
+      _gtk_css_parser_error (parser, "Missing closing ')' for cubic-bezier");
+      return NULL;
+    }
+
+  return _gtk_css_ease_value_new_cubic_bezier (values[0], values[1], values[2], values[3]);
+}
+
+static GtkCssValue *
+gtk_css_ease_value_parse_steps (GtkCssParser *parser)
+{
+  guint n_steps;
+  gboolean start;
+
+  if (!_gtk_css_parser_try (parser, "(", TRUE))
+    {
+      _gtk_css_parser_error (parser, "Expected '('");
+      return NULL;
+    }
+
+  if (!_gtk_css_parser_try_uint (parser, &n_steps))
+    {
+      _gtk_css_parser_error (parser, "Expected number of steps");
+      return NULL;
+    }
+
+  if (_gtk_css_parser_try (parser, ",", TRUE))
+    {
+      if (_gtk_css_parser_try (parser, "start", TRUE))
+        start = TRUE;
+      else if (_gtk_css_parser_try (parser, "end", TRUE))
+        start = FALSE;
+      else
+        {
+          _gtk_css_parser_error (parser, "Only allowed values are 'start' and 'end'");
+          return NULL;
+        }
+    }
+  else
+    start = FALSE;
+
+  if (!_gtk_css_parser_try (parser, ")", TRUE))
+    {
+      _gtk_css_parser_error (parser, "Missing closing ')' for steps");
+      return NULL;
+    }
+
+  return _gtk_css_ease_value_new_steps (n_steps, start);
+}
+
+GtkCssValue *
+_gtk_css_ease_value_parse (GtkCssParser *parser)
+{
+  g_return_val_if_fail (parser != NULL, NULL);
+
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (parser_values); i++)
+    {
+      if (_gtk_css_parser_try (parser, parser_values[i].name, FALSE))
+        {
+          if (parser_values[i].needs_custom)
+            {
+              if (parser_values[i].is_bezier)
+                return gtk_css_ease_value_parse_cubic_bezier (parser);
+              else
+                return gtk_css_ease_value_parse_steps (parser);
+            }
+
+          _gtk_css_parser_skip_whitespace (parser);
+
+          if (parser_values[i].is_bezier)
+            return _gtk_css_ease_value_new_cubic_bezier (parser_values[i].values[0],
+                                                         parser_values[i].values[1],
+                                                         parser_values[i].values[2],
+                                                         parser_values[i].values[3]);
+          else
+            return _gtk_css_ease_value_new_steps (parser_values[i].values[0],
+                                                  parser_values[i].values[1] != 0.0);
+        }
+    }
+
+  _gtk_css_parser_error (parser, "Unknown value");
+  return NULL;
+}
+
+double
+_gtk_css_ease_value_transform (const GtkCssValue *ease,
+                               double             progress)
+{
+  g_return_val_if_fail (ease->class == &GTK_CSS_VALUE_EASE, 1.0);
+
+  if (progress <= 0)
+    return 0;
+  if (progress >= 1)
+    return 1;
+
+  switch (ease->type)
+    {
+    case GTK_CSS_EASE_CUBIC_BEZIER:
+      {
+        static const double epsilon = 0.00001;
+        double tmin, t, tmax;
+
+        tmin = 0.0;
+        tmax = 1.0;
+        t = progress;
+
+        while (tmin < tmax)
+          {
+             double sample;
+             sample = (((1.0 + 3 * ease->u.cubic.x1 - 3 * ease->u.cubic.x2) * t
+                       +      -6 * ease->u.cubic.x1 + 3 * ease->u.cubic.x2) * t
+                       +       3 * ease->u.cubic.x1                       ) * t;
+             if (fabs(sample - progress) < epsilon)
+               break;
+
+             if (progress > sample)
+               tmin = t;
+             else
+               tmax = t;
+             t = (tmax + tmin) * .5;
+          }
+
+        return (((1.0 + 3 * ease->u.cubic.y1 - 3 * ease->u.cubic.y2) * t
+                +      -6 * ease->u.cubic.y1 + 3 * ease->u.cubic.y2) * t
+                +       3 * ease->u.cubic.y1                       ) * t;
+      }
+    case GTK_CSS_EASE_STEPS:
+      progress *= ease->u.steps.steps;
+      progress = floor (progress) + ease->u.steps.start ? 0 : 1;
+      return progress / ease->u.steps.steps;
+    default:
+      g_assert_not_reached ();
+      return 1.0;
+    }
+}
+
+
diff --git a/gtk/gtkcsseasevalueprivate.h b/gtk/gtkcsseasevalueprivate.h
new file mode 100644
index 0000000..6b4dab3
--- /dev/null
+++ b/gtk/gtkcsseasevalueprivate.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Alexander Larsson <alexl gnome org>
+ */
+
+#ifndef __GTK_CSS_EASE_VALUE_PRIVATE_H__
+#define __GTK_CSS_EASE_VALUE_PRIVATE_H__
+
+#include "gtkcssparserprivate.h"
+#include "gtkcssvalueprivate.h"
+
+G_BEGIN_DECLS
+
+GtkCssValue *   _gtk_css_ease_value_new_cubic_bezier  (double                x1,
+                                                       double                y1,
+                                                       double                x2,
+                                                       double                y2);
+gboolean        _gtk_css_ease_value_can_parse         (GtkCssParser         *parser);
+GtkCssValue *   _gtk_css_ease_value_parse             (GtkCssParser         *parser);
+
+double          _gtk_css_ease_value_transform         (const GtkCssValue    *ease,
+                                                       double                progress);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_EASE_VALUE_PRIVATE_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]