[gtk+] gtk: Implement -gtk-icon-filter



commit 2645b5a7d755e6b58f84215072bf1f0b7ded8cf0
Author: Benjamin Otte <otte redhat com>
Date:   Sat Dec 31 01:15:52 2016 +0100

    gtk: Implement -gtk-icon-filter
    
    This uses the new GskColorMatrixNode to implement a filter that applies
    to icons. It's meant to replace -gtk-icon-effect.

 gtk/Makefile.am                |    2 +
 gtk/gtkcssfiltervalue.c        |  863 ++++++++++++++++++++++++++++++++++++++++
 gtk/gtkcssfiltervalueprivate.h |   42 ++
 gtk/gtkcssstylepropertyimpl.c  |   16 +
 gtk/gtkcsstypesprivate.h       |    1 +
 gtk/gtkrendericon.c            |   23 +-
 6 files changed, 941 insertions(+), 6 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index bdb4898..b49b2d5 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -401,6 +401,7 @@ gtk_private_h_sources =             \
        gtkcssdimensionvalueprivate.h   \
        gtkcsseasevalueprivate.h        \
        gtkcssenumvalueprivate.h        \
+       gtkcssfiltervalueprivate.h      \
        gtkcssgadgetprivate.h           \
        gtkcssiconthemevalueprivate.h   \
        gtkcssimagebuiltinprivate.h     \
@@ -668,6 +669,7 @@ gtk_base_c_sources =                \
        gtkcssdimensionvalue.c  \
        gtkcsseasevalue.c       \
        gtkcssenumvalue.c       \
+       gtkcssfiltervalue.c     \
        gtkcssgadget.c          \
        gtkcssiconthemevalue.c  \
        gtkcssimage.c           \
diff --git a/gtk/gtkcssfiltervalue.c b/gtk/gtkcssfiltervalue.c
new file mode 100644
index 0000000..808d41f
--- /dev/null
+++ b/gtk/gtkcssfiltervalue.c
@@ -0,0 +1,863 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017 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 "gtkcssbgsizevalueprivate.h"
+
+#include <math.h>
+#include <string.h>
+
+#include "gtkcssfiltervalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
+
+typedef union _GtkCssFilter GtkCssFilter;
+
+typedef enum {
+  GTK_CSS_FILTER_NONE,
+  GTK_CSS_FILTER_BLUR,
+  GTK_CSS_FILTER_BRIGHTNESS,
+  GTK_CSS_FILTER_CONTRAST,
+  GTK_CSS_FILTER_DROP_SHADOW,
+  GTK_CSS_FILTER_GRAYSCALE,
+  GTK_CSS_FILTER_HUE_ROTATE,
+  GTK_CSS_FILTER_INVERT,
+  GTK_CSS_FILTER_OPACITY,
+  GTK_CSS_FILTER_SATURATE,
+  GTK_CSS_FILTER_SEPIA
+} GtkCssFilterType;
+
+union _GtkCssFilter {
+  GtkCssFilterType       type;
+  struct {
+    GtkCssFilterType     type;
+    GtkCssValue         *value;
+  }            brightness, contrast, grayscale, hue_rotate, invert, opacity, saturate, sepia;
+};
+
+struct _GtkCssValue {
+  GTK_CSS_VALUE_BASE
+  guint                 n_filters;
+  GtkCssFilter          filters[1];
+};
+
+static GtkCssValue *    gtk_css_filter_value_alloc           (guint                  n_values);
+static gboolean         gtk_css_filter_value_is_none         (const GtkCssValue     *value);
+
+static void
+gtk_css_filter_clear (GtkCssFilter *filter)
+{
+  switch (filter->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      _gtk_css_value_unref (filter->brightness.value);
+      break;
+    case GTK_CSS_FILTER_CONTRAST:
+      _gtk_css_value_unref (filter->contrast.value);
+      break;
+    case GTK_CSS_FILTER_GRAYSCALE:
+      _gtk_css_value_unref (filter->grayscale.value);
+      break;
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      _gtk_css_value_unref (filter->hue_rotate.value);
+      break;
+    case GTK_CSS_FILTER_INVERT:
+      _gtk_css_value_unref (filter->invert.value);
+      break;
+    case GTK_CSS_FILTER_OPACITY:
+      _gtk_css_value_unref (filter->opacity.value);
+      break;
+    case GTK_CSS_FILTER_SATURATE:
+      _gtk_css_value_unref (filter->saturate.value);
+      break;
+    case GTK_CSS_FILTER_SEPIA:
+      _gtk_css_value_unref (filter->sepia.value);
+      break;
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static void
+gtk_css_filter_init_identity (GtkCssFilter     *filter,
+                              GtkCssFilterType  type)
+{
+  switch (type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      filter->brightness.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_CONTRAST:
+      filter->contrast.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_GRAYSCALE:
+      filter->grayscale.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      filter->hue_rotate.value = _gtk_css_number_value_new (0, GTK_CSS_DEG);
+      break;
+    case GTK_CSS_FILTER_INVERT:
+      filter->invert.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_OPACITY:
+      filter->opacity.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_SATURATE:
+      filter->saturate.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_SEPIA:
+      filter->sepia.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
+      break;
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  filter->type = type;
+}
+
+#define R 0.2126
+#define G 0.7152
+#define B 0.0722
+
+static void
+gtk_css_filter_get_matrix (const GtkCssFilter *filter,
+                           graphene_matrix_t  *matrix,
+                           graphene_vec4_t    *offset)
+{
+  double value;
+
+  switch (filter->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      value = _gtk_css_number_value_get (filter->brightness.value, 1.0);
+      graphene_matrix_init_scale (matrix, value, value, value);
+      graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_CONTRAST:
+      value = _gtk_css_number_value_get (filter->contrast.value, 1.0);
+      graphene_matrix_init_scale (matrix, value, value, value);
+      graphene_vec4_init (offset, 0.5 - 0.5 * value, 0.5 - 0.5 * value, 0.5 - 0.5 * value, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_GRAYSCALE:
+      value = _gtk_css_number_value_get (filter->grayscale.value, 1.0);
+      graphene_matrix_init_from_float (matrix, (float[16]) {
+                                           1.0 - (1.0 - R) * value, R * value, R * value, 0.0,
+                                           G * value, 1.0 - (1.0 - G) * value, G * value, 0.0,
+                                           B * value, B * value, 1.0 - (1.0 - B) * value, 0.0,
+                                           0.0, 0.0, 0.0, 1.0
+                                       });
+      graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      {
+        double c, s;
+        value = _gtk_css_number_value_get (filter->grayscale.value, 1.0) * G_PI / 180.0;
+        c = cos (value);
+        s = sin (value);
+        graphene_matrix_init_from_float (matrix, (float[16]) {
+                                             0.213 + 0.787 * c - 0.213 * s,
+                                             0.213 - 0.213 * c + 0.143 * s,
+                                             0.213 - 0.213 * c - 0.787 * s,
+                                             0,
+                                             0.715 - 0.715 * c - 0.715 * s,
+                                             0.715 + 0.285 * c + 0.140 * s,
+                                             0.715 - 0.715 * c + 0.715 * s,
+                                             0,
+                                             0.072 - 0.072 * c + 0.928 * s,
+                                             0.072 - 0.072 * c - 0.283 * s,
+                                             0.072 + 0.928 * c + 0.072 * s,
+                                             0,
+                                             0, 0, 0, 1
+                                         });
+        graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      }
+      break;
+
+    case GTK_CSS_FILTER_INVERT:
+      value = _gtk_css_number_value_get (filter->invert.value, 1.0);
+      graphene_matrix_init_scale (matrix, 1.0 - 2 * value, 1.0 - 2 * value, 1.0 - 2 * value);
+      graphene_vec4_init (offset, value, value, value, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_OPACITY:
+      value = _gtk_css_number_value_get (filter->invert.value, 1.0);
+      graphene_matrix_init_from_float (matrix, (float[16]) {
+                                           1.0, 0.0, 0.0, 0.0,
+                                           0.0, 1.0, 0.0, 0.0,
+                                           0.0, 0.0, 1.0, 0.0,
+                                           0.0, 0.0, 0.0, value
+                                       });
+      graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_SATURATE:
+      value = _gtk_css_number_value_get (filter->saturate.value, 1.0);
+      graphene_matrix_init_from_float (matrix, (float[16]) {
+                                           R + (1.0 - R) * value, R - R * value, R - R * value, 0.0,
+                                           G - G * value, G + (1.0 - G) * value, G - G * value, 0.0,
+                                           B - B * value, B - B * value, B + (1.0 - B) * value, 0.0,
+                                           0.0, 0.0, 0.0, 1.0
+                                       });
+      graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_SEPIA:
+      value = _gtk_css_number_value_get (filter->sepia.value, 1.0);
+      graphene_matrix_init_from_float (matrix, (float[16]) {
+                                           1.0 - 0.607 * value, 0.349 * value, 0.272 * value, 0.0,
+                                           0.769 * value, 1.0 - 0.314 * value, 0.534 * value, 0.0,
+                                           0.189 * value, 0.168 * value, 1.0 - 0.869 * value, 0.0,
+                                           0.0, 0.0, 0.0, 1.0
+                                       });
+      graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
+      break;
+
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+#undef R
+#undef G
+#undef B
+
+static void
+gtk_css_filter_value_compute_matrix (const GtkCssValue *value,
+                                     graphene_matrix_t *matrix,
+                                     graphene_vec4_t   *offset)
+{
+  graphene_matrix_t m, m2;
+  graphene_vec4_t o, o2;
+  guint i;
+
+  gtk_css_filter_get_matrix (&value->filters[0], matrix, offset);
+
+  for (i = 1; i < value->n_filters; i++)
+    {
+      gtk_css_filter_get_matrix (&value->filters[i], &m, &o);
+
+      graphene_matrix_multiply (matrix, &m, &m2);
+      graphene_matrix_transform_vec4 (&m, offset, &o2);
+
+      graphene_matrix_init_from_matrix (matrix, &m2);
+      graphene_vec4_add (&o, &o2, offset);
+    }
+}
+
+static void
+gtk_css_value_filter_free (GtkCssValue *value)
+{
+  guint i;
+
+  for (i = 0; i < value->n_filters; i++)
+    {
+      gtk_css_filter_clear (&value->filters[i]);
+    }
+
+  g_slice_free1 (sizeof (GtkCssValue) + sizeof (GtkCssFilter) * (value->n_filters - 1), value);
+}
+
+/* returns TRUE if dest == src */
+static gboolean
+gtk_css_filter_compute (GtkCssFilter            *dest,
+                        GtkCssFilter            *src,
+                        guint                    property_id,
+                        GtkStyleProviderPrivate *provider,
+                        GtkCssStyle             *style,
+                        GtkCssStyle             *parent_style)
+{
+  dest->type = src->type;
+
+  switch (src->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      dest->brightness.value = _gtk_css_value_compute (src->brightness.value, property_id, provider, style, 
parent_style);
+      return dest->brightness.value == src->brightness.value;
+
+    case GTK_CSS_FILTER_CONTRAST:
+      dest->contrast.value = _gtk_css_value_compute (src->contrast.value, property_id, provider, style, 
parent_style);
+      return dest->contrast.value == src->contrast.value;
+
+    case GTK_CSS_FILTER_GRAYSCALE:
+      dest->grayscale.value = _gtk_css_value_compute (src->grayscale.value, property_id, provider, style, 
parent_style);
+      return dest->grayscale.value == src->grayscale.value;
+
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      dest->hue_rotate.value = _gtk_css_value_compute (src->hue_rotate.value, property_id, provider, style, 
parent_style);
+      return dest->hue_rotate.value == src->hue_rotate.value;
+
+    case GTK_CSS_FILTER_INVERT:
+      dest->invert.value = _gtk_css_value_compute (src->invert.value, property_id, provider, style, 
parent_style);
+      return dest->invert.value == src->invert.value;
+
+    case GTK_CSS_FILTER_OPACITY:
+      dest->opacity.value = _gtk_css_value_compute (src->opacity.value, property_id, provider, style, 
parent_style);
+      return dest->opacity.value == src->opacity.value;
+
+    case GTK_CSS_FILTER_SATURATE:
+      dest->saturate.value = _gtk_css_value_compute (src->saturate.value, property_id, provider, style, 
parent_style);
+      return dest->saturate.value == src->saturate.value;
+
+    case GTK_CSS_FILTER_SEPIA:
+      dest->sepia.value = _gtk_css_value_compute (src->sepia.value, property_id, provider, style, 
parent_style);
+      return dest->sepia.value == src->sepia.value;
+
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      return FALSE;
+    }
+}
+
+static GtkCssValue *
+gtk_css_value_filter_compute (GtkCssValue             *value,
+                              guint                    property_id,
+                              GtkStyleProviderPrivate *provider,
+                              GtkCssStyle             *style,
+                              GtkCssStyle             *parent_style)
+{
+  GtkCssValue *result;
+  gboolean changes;
+  guint i;
+
+  /* Special case the 99% case of "none" */
+  if (gtk_css_filter_value_is_none (value))
+    return _gtk_css_value_ref (value);
+
+  changes = FALSE;
+  result = gtk_css_filter_value_alloc (value->n_filters);
+
+  for (i = 0; i < value->n_filters; i++)
+    {
+      changes |= !gtk_css_filter_compute (&result->filters[i],
+                                          &value->filters[i],
+                                          property_id,
+                                          provider,
+                                          style,
+                                          parent_style);
+    }
+
+  if (!changes)
+    {
+      _gtk_css_value_unref (result);
+      result = _gtk_css_value_ref (value);
+    }
+
+  return result;
+}
+
+static gboolean
+gtk_css_filter_equal (const GtkCssFilter *filter1,
+                      const GtkCssFilter *filter2)
+{
+  if (filter1->type != filter2->type)
+    return FALSE;
+
+  switch (filter1->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      return _gtk_css_value_equal (filter1->brightness.value, filter2->brightness.value);
+
+    case GTK_CSS_FILTER_CONTRAST:
+      return _gtk_css_value_equal (filter1->contrast.value, filter2->contrast.value);
+
+    case GTK_CSS_FILTER_GRAYSCALE:
+      return _gtk_css_value_equal (filter1->grayscale.value, filter2->grayscale.value);
+
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      return _gtk_css_value_equal (filter1->hue_rotate.value, filter2->hue_rotate.value);
+
+    case GTK_CSS_FILTER_INVERT:
+      return _gtk_css_value_equal (filter1->invert.value, filter2->invert.value);
+
+    case GTK_CSS_FILTER_OPACITY:
+      return _gtk_css_value_equal (filter1->opacity.value, filter2->opacity.value);
+
+    case GTK_CSS_FILTER_SATURATE:
+      return _gtk_css_value_equal (filter1->saturate.value, filter2->saturate.value);
+
+    case GTK_CSS_FILTER_SEPIA:
+      return _gtk_css_value_equal (filter1->sepia.value, filter2->sepia.value);
+
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      return FALSE;
+    }
+}
+
+static gboolean
+gtk_css_value_filter_equal (const GtkCssValue *value1,
+                            const GtkCssValue *value2)
+{
+  const GtkCssValue *larger;
+  guint i, n;
+
+  n = MIN (value1->n_filters, value2->n_filters);
+  for (i = 0; i < n; i++)
+    {
+      if (!gtk_css_filter_equal (&value1->filters[i], &value2->filters[i]))
+        return FALSE;
+    }
+
+  larger = value1->n_filters > value2->n_filters ? value1 : value2;
+
+  for (; i < larger->n_filters; i++)
+    {
+      GtkCssFilter filter;
+
+      gtk_css_filter_init_identity (&filter, larger->filters[i].type);
+
+      if (!gtk_css_filter_equal (&larger->filters[i], &filter))
+        {
+          gtk_css_filter_clear (&filter);
+          return FALSE;
+        }
+
+      gtk_css_filter_clear (&filter);
+    }
+
+  return TRUE;
+}
+
+static void
+gtk_css_filter_transition (GtkCssFilter       *result,
+                           const GtkCssFilter *start,
+                           const GtkCssFilter *end,
+                           guint               property_id,
+                           double              progress)
+{
+  result->type = start->type;
+
+  switch (start->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      result->brightness.value = _gtk_css_value_transition (start->brightness.value, end->brightness.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_CONTRAST:
+      result->contrast.value = _gtk_css_value_transition (start->contrast.value, end->contrast.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_GRAYSCALE:
+      result->grayscale.value = _gtk_css_value_transition (start->grayscale.value, end->grayscale.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      result->hue_rotate.value = _gtk_css_value_transition (start->hue_rotate.value, end->hue_rotate.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_INVERT:
+      result->invert.value = _gtk_css_value_transition (start->invert.value, end->invert.value, property_id, 
progress);
+      break;
+
+    case GTK_CSS_FILTER_OPACITY:
+      result->opacity.value = _gtk_css_value_transition (start->opacity.value, end->opacity.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_SATURATE:
+      result->saturate.value = _gtk_css_value_transition (start->saturate.value, end->saturate.value, 
property_id, progress);
+      break;
+
+    case GTK_CSS_FILTER_SEPIA:
+      result->sepia.value = _gtk_css_value_transition (start->sepia.value, end->sepia.value, property_id, 
progress);
+      break;
+
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static GtkCssValue *
+gtk_css_value_filter_transition (GtkCssValue *start,
+                                 GtkCssValue *end,
+                                 guint        property_id,
+                                 double       progress)
+{
+  GtkCssValue *result;
+  guint i, n;
+
+  if (gtk_css_filter_value_is_none (start))
+    {
+      if (gtk_css_filter_value_is_none (end))
+        return _gtk_css_value_ref (start);
+
+      n = 0;
+    }
+  else if (gtk_css_filter_value_is_none (end))
+    {
+      n = 0;
+    }
+  else
+    {
+      n = MIN (start->n_filters, end->n_filters);
+    }
+
+  /* Check filters are compatible. If not, transition between
+   * their result matrices.
+   */
+  for (i = 0; i < n; i++)
+    {
+      if (start->filters[i].type != end->filters[i].type)
+        {
+          /* XXX: can we improve this? */
+          return NULL;
+        }
+    }
+
+  result = gtk_css_filter_value_alloc (MAX (start->n_filters, end->n_filters));
+
+  for (i = 0; i < n; i++)
+    {
+      gtk_css_filter_transition (&result->filters[i],
+                                 &start->filters[i],
+                                 &end->filters[i],
+                                 property_id,
+                                 progress);
+    }
+
+  for (; i < start->n_filters; i++)
+    {
+      GtkCssFilter filter;
+
+      gtk_css_filter_init_identity (&filter, start->filters[i].type);
+      gtk_css_filter_transition (&result->filters[i],
+                                 &start->filters[i],
+                                 &filter,
+                                 property_id,
+                                 progress);
+      gtk_css_filter_clear (&filter);
+    }
+  for (; i < end->n_filters; i++)
+    {
+      GtkCssFilter filter;
+
+      gtk_css_filter_init_identity (&filter, end->filters[i].type);
+      gtk_css_filter_transition (&result->filters[i],
+                                 &filter,
+                                 &end->filters[i],
+                                 property_id,
+                                 progress);
+      gtk_css_filter_clear (&filter);
+    }
+
+  g_assert (i == MAX (start->n_filters, end->n_filters));
+
+  return result;
+}
+
+static void
+gtk_css_filter_print (const GtkCssFilter *filter,
+                      GString            *string)
+{
+  switch (filter->type)
+    {
+    case GTK_CSS_FILTER_BRIGHTNESS:
+      g_string_append (string, "brightness(");
+      _gtk_css_value_print (filter->brightness.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_CONTRAST:
+      g_string_append (string, "contrast(");
+      _gtk_css_value_print (filter->contrast.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_GRAYSCALE:
+      g_string_append (string, "grayscale(");
+      _gtk_css_value_print (filter->grayscale.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_HUE_ROTATE:
+      g_string_append (string, "hue-rotate(");
+      _gtk_css_value_print (filter->hue_rotate.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_INVERT:
+      g_string_append (string, "invert(");
+      _gtk_css_value_print (filter->invert.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_OPACITY:
+      g_string_append (string, "opacity(");
+      _gtk_css_value_print (filter->opacity.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_SATURATE:
+      g_string_append (string, "saturate(");
+      _gtk_css_value_print (filter->saturate.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_SEPIA:
+      g_string_append (string, "sepia(");
+      _gtk_css_value_print (filter->sepia.value, string);
+      g_string_append (string, ")");
+      break;
+
+    case GTK_CSS_FILTER_NONE:
+    case GTK_CSS_FILTER_BLUR:
+    case GTK_CSS_FILTER_DROP_SHADOW:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static void
+gtk_css_value_filter_print (const GtkCssValue *value,
+                            GString           *string)
+{
+  guint i;
+
+  if (gtk_css_filter_value_is_none (value))
+    {
+      g_string_append (string, "none");
+      return;
+    }
+
+  for (i = 0; i < value->n_filters; i++)
+    {
+      if (i > 0)
+        g_string_append_c (string, ' ');
+
+      gtk_css_filter_print (&value->filters[i], string);
+    }
+}
+
+static const GtkCssValueClass GTK_CSS_VALUE_FILTER = {
+  gtk_css_value_filter_free,
+  gtk_css_value_filter_compute,
+  gtk_css_value_filter_equal,
+  gtk_css_value_filter_transition,
+  gtk_css_value_filter_print
+};
+
+static GtkCssValue none_singleton = { &GTK_CSS_VALUE_FILTER, 1, 0, {  { GTK_CSS_FILTER_NONE } } };
+
+static GtkCssValue *
+gtk_css_filter_value_alloc (guint n_filters)
+{
+  GtkCssValue *result;
+           
+  g_return_val_if_fail (n_filters > 0, NULL);
+         
+  result = _gtk_css_value_alloc (&GTK_CSS_VALUE_FILTER, sizeof (GtkCssValue) + sizeof (GtkCssFilter) * 
(n_filters - 1));
+  result->n_filters = n_filters;
+            
+  return result;
+}
+
+GtkCssValue *
+gtk_css_filter_value_new_none (void)
+{
+  return _gtk_css_value_ref (&none_singleton);
+}
+
+static gboolean
+gtk_css_filter_value_is_none (const GtkCssValue *value)
+{
+  return value->n_filters == 0;
+}
+
+static gboolean
+gtk_css_filter_parse (GtkCssFilter *filter,
+                         GtkCssParser    *parser)
+{
+  if (_gtk_css_parser_try (parser, "brightness(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_BRIGHTNESS;
+
+      filter->brightness.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->brightness.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "contrast(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_CONTRAST;
+
+      filter->contrast.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->contrast.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "grayscale(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_GRAYSCALE;
+
+      filter->grayscale.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->grayscale.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "hue-rotate(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_HUE_ROTATE;
+
+      filter->hue_rotate.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE);
+      if (filter->hue_rotate.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "invert(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_INVERT;
+
+      filter->invert.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->invert.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "opacity(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_OPACITY;
+
+      filter->opacity.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->opacity.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "saturate(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_SATURATE;
+
+      filter->saturate.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->saturate.value == NULL)
+        return FALSE;
+    }
+  else if (_gtk_css_parser_try (parser, "sepia(", TRUE))
+    {
+      filter->type = GTK_CSS_FILTER_SEPIA;
+
+      filter->sepia.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | 
GTK_CSS_PARSE_PERCENT);
+      if (filter->sepia.value == NULL)
+        return FALSE;
+    }
+  else
+    {
+      _gtk_css_parser_error (parser, "unknown syntax for filter");
+      return FALSE;
+    }
+
+  if (!_gtk_css_parser_try (parser, ")", TRUE))
+    {
+      gtk_css_filter_clear (filter);
+      _gtk_css_parser_error (parser, "Expected closing ')'");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+GtkCssValue *
+gtk_css_filter_value_parse (GtkCssParser *parser)
+{
+  GtkCssValue *value;
+  GArray *array;
+  guint i;
+
+  if (_gtk_css_parser_try (parser, "none", TRUE))
+    return gtk_css_filter_value_new_none ();
+
+  array = g_array_new (FALSE, FALSE, sizeof (GtkCssFilter));
+
+  do {
+    GtkCssFilter filter;
+
+    if (!gtk_css_filter_parse (&filter, parser))
+      {
+        for (i = 0; i < array->len; i++)
+          {
+            gtk_css_filter_clear (&g_array_index (array, GtkCssFilter, i));
+          }
+        g_array_free (array, TRUE);
+        return NULL;
+      }
+    g_array_append_val (array, filter);
+  } while (!_gtk_css_parser_begins_with (parser, ';'));
+
+  value = gtk_css_filter_value_alloc (array->len);
+  memcpy (value->filters, array->data, sizeof (GtkCssFilter) * array->len);
+
+  g_array_free (array, TRUE);
+
+  return value;
+}
+
+gboolean
+gtk_css_filter_value_get_color_matrix (const GtkCssValue *filter,
+                                       graphene_matrix_t *matrix,
+                                       graphene_vec4_t   *offset)
+{
+  g_return_val_if_fail (filter->class == &GTK_CSS_VALUE_FILTER, FALSE);
+  g_return_val_if_fail (matrix != NULL, FALSE);
+  
+  gtk_css_filter_value_compute_matrix (filter, matrix, offset);
+
+  return TRUE;
+}
+
+void
+gtk_css_filter_value_push_snapshot (const GtkCssValue *filter,
+                                    GtkSnapshot       *snapshot)
+{
+  graphene_matrix_t matrix;
+  graphene_vec4_t offset;
+
+  if (gtk_css_filter_value_is_none (filter))
+    return;
+
+  gtk_css_filter_value_get_color_matrix (filter, &matrix, &offset);
+
+  gtk_snapshot_push_color_matrix (snapshot,
+                                  &matrix,
+                                  &offset,
+                                  "IconFilter<%u>", filter->n_filters);
+}
+
+void
+gtk_css_filter_value_pop_snapshot (const GtkCssValue *filter,
+                                   GtkSnapshot       *snapshot)
+{
+  if (gtk_css_filter_value_is_none (filter))
+    return;
+
+  gtk_snapshot_pop_and_append (snapshot);
+}
diff --git a/gtk/gtkcssfiltervalueprivate.h b/gtk/gtkcssfiltervalueprivate.h
new file mode 100644
index 0000000..796ba42
--- /dev/null
+++ b/gtk/gtkcssfiltervalueprivate.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2017 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: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_FILTER_VALUE_PRIVATE_H__
+#define __GTK_CSS_FILTER_VALUE_PRIVATE_H__
+
+#include "gtkcssparserprivate.h"
+#include "gtkcssvalueprivate.h"
+
+G_BEGIN_DECLS
+
+GtkCssValue *   gtk_css_filter_value_new_none           (void);
+GtkCssValue *   gtk_css_filter_value_parse              (GtkCssParser           *parser);
+
+gboolean        gtk_css_filter_value_get_color_matrix   (const GtkCssValue      *filter,
+                                                         graphene_matrix_t      *matrix,
+                                                         graphene_vec4_t        *offset);
+
+void            gtk_css_filter_value_push_snapshot      (const GtkCssValue      *filter,
+                                                         GtkSnapshot            *snapshot);
+void            gtk_css_filter_value_pop_snapshot       (const GtkCssValue      *filter,
+                                                         GtkSnapshot            *snapshot);
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_FILTER_VALUE_PRIVATE_H__ */
diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c
index 66f0586..6229d2d 100644
--- a/gtk/gtkcssstylepropertyimpl.c
+++ b/gtk/gtkcssstylepropertyimpl.c
@@ -44,6 +44,7 @@
 #include "gtkcsscolorvalueprivate.h"
 #include "gtkcsscornervalueprivate.h"
 #include "gtkcsseasevalueprivate.h"
+#include "gtkcssfiltervalueprivate.h"
 #include "gtkcssiconthemevalueprivate.h"
 #include "gtkcssimageprivate.h"
 #include "gtkcssimagebuiltinprivate.h"
@@ -563,6 +564,13 @@ transform_value_parse (GtkCssStyleProperty *property,
 }
 
 static GtkCssValue *
+filter_value_parse (GtkCssStyleProperty *property,
+                    GtkCssParser        *parser)
+{
+  return gtk_css_filter_value_parse (parser);
+}
+
+static GtkCssValue *
 border_spacing_value_parse (GtkCssStyleProperty *property,
                             GtkCssParser        *parser)
 {
@@ -1454,6 +1462,14 @@ _gtk_css_style_property_init_properties (void)
                                           transform_value_parse,
                                           NULL,
                                           _gtk_css_transform_value_new_none ());
+  gtk_css_style_property_register        ("-gtk-icon-filter",
+                                          GTK_CSS_PROPERTY_ICON_FILTER,
+                                          G_TYPE_NONE,
+                                          GTK_STYLE_PROPERTY_ANIMATED,
+                                          GTK_CSS_AFFECTS_ICON | GTK_CSS_AFFECTS_SYMBOLIC_ICON | 
GTK_CSS_AFFECTS_CLIP,
+                                          filter_value_parse,
+                                          NULL,
+                                          gtk_css_filter_value_new_none ());
 
   gtk_css_style_property_register        ("border-spacing",
                                           GTK_CSS_PROPERTY_BORDER_SPACING,
diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h
index 87582e5..885d9d4 100644
--- a/gtk/gtkcsstypesprivate.h
+++ b/gtk/gtkcsstypesprivate.h
@@ -208,6 +208,7 @@ enum { /*< skip >*/
   GTK_CSS_PROPERTY_ICON_SHADOW,
   GTK_CSS_PROPERTY_ICON_STYLE,
   GTK_CSS_PROPERTY_ICON_TRANSFORM,
+  GTK_CSS_PROPERTY_ICON_FILTER,
   GTK_CSS_PROPERTY_BORDER_SPACING,
   GTK_CSS_PROPERTY_MIN_WIDTH,
   GTK_CSS_PROPERTY_MIN_HEIGHT,
diff --git a/gtk/gtkrendericon.c b/gtk/gtkrendericon.c
index 15f8397..9cf0b6e 100644
--- a/gtk/gtkrendericon.c
+++ b/gtk/gtkrendericon.c
@@ -21,6 +21,7 @@
 
 #include "gtkrendericonprivate.h"
 
+#include "gtkcssfiltervalueprivate.h"
 #include "gtkcssimagebuiltinprivate.h"
 #include "gtkcssimagevalueprivate.h"
 #include "gtkcssshadowsvalueprivate.h"
@@ -95,7 +96,7 @@ gtk_css_style_snapshot_icon (GtkCssStyle            *style,
                              double                  height,
                              GtkCssImageBuiltinType  builtin_type)
 {
-  const GtkCssValue *shadows_value, *transform;
+  const GtkCssValue *shadows_value, *transform_value, *filter_value;
   graphene_matrix_t transform_matrix;
   GtkCssImage *image;
   GskShadow *shadows;
@@ -109,11 +110,14 @@ gtk_css_style_snapshot_icon (GtkCssStyle            *style,
     return;
 
   shadows_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
-  transform = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
+  transform_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
+  filter_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_FILTER);
 
-  if (!gtk_css_transform_value_get_matrix (transform, &transform_matrix))
+  if (!gtk_css_transform_value_get_matrix (transform_value, &transform_matrix))
     return;
 
+  gtk_css_filter_value_push_snapshot (filter_value, snapshot);
+
   shadows = gtk_css_shadows_value_get_shadows (shadows_value, &n_shadows);
   if (shadows)
     gtk_snapshot_push_shadow (snapshot, shadows, n_shadows, "IconShadow<%zu>", n_shadows);
@@ -144,6 +148,8 @@ gtk_css_style_snapshot_icon (GtkCssStyle            *style,
       gtk_snapshot_pop_and_append (snapshot);
       g_free (shadows);
     }
+  
+  gtk_css_filter_value_pop_snapshot (filter_value, snapshot);
 }
 
 static gboolean
@@ -266,7 +272,7 @@ gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
                                      GskTexture  *texture,
                                      double       texture_scale)
 {
-  const GtkCssValue *shadows_value, *transform;
+  const GtkCssValue *shadows_value, *transform_value, *filter_value;
   graphene_matrix_t transform_matrix;
   graphene_rect_t bounds;
   double width, height;
@@ -279,13 +285,16 @@ gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
   g_return_if_fail (texture_scale > 0);
 
   shadows_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
-  transform = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
+  transform_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
+  filter_value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_FILTER);
   width = gsk_texture_get_width (texture) / texture_scale;
   height = gsk_texture_get_height (texture) / texture_scale;
 
-  if (!gtk_css_transform_value_get_matrix (transform, &transform_matrix))
+  if (!gtk_css_transform_value_get_matrix (transform_value, &transform_matrix))
     return;
 
+  gtk_css_filter_value_push_snapshot (filter_value, snapshot);
+
   shadows = gtk_css_shadows_value_get_shadows (shadows_value, &n_shadows);
   if (shadows)
     gtk_snapshot_push_shadow (snapshot, shadows, n_shadows, "IconShadow<%zu>", n_shadows);
@@ -321,4 +330,6 @@ gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
       gtk_snapshot_pop_and_append (snapshot);
       g_free (shadows);
     }
+  
+  gtk_css_filter_value_pop_snapshot (filter_value, snapshot);
 }


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