[gtk+] gsk: Add GskColorMatrixNode



commit 7540702cf0196cf4890de87f4225165a5f2f53f2
Author: Benjamin Otte <otte redhat com>
Date:   Sat Dec 31 01:13:19 2016 +0100

    gsk: Add GskColorMatrixNode
    
    This node essentially implements the feColorMatrix SVG filter. I got the
    idea yesterday after looking at the opacity implementation.
    
    It can be used for opacity (not sure if we want to) and to implement a
    bunch of the CSS filters.

 docs/reference/gsk/gsk4-sections.txt   |    1 +
 gsk/gskenums.h                         |    1 +
 gsk/gskrendernode.h                    |    5 +
 gsk/gskrendernodeimpl.c                |  246 ++++++++++++++++++++++++++++++++
 gsk/gskrendernodeprivate.h             |    4 +
 gtk/inspector/gtktreemodelrendernode.c |    4 +
 gtk/inspector/recorder.c               |    2 +
 7 files changed, 263 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index a56cd4a..a74d4ad 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -55,6 +55,7 @@ gsk_transform_node_new
 gsk_transform_node_get_child
 gsk_opacity_node_new
 gsk_opacity_node_get_child
+gsk_color_matrix_node_new
 gsk_clip_node_new
 gsk_clip_node_get_child
 gsk_rounded_clip_node_new
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 9199782..060febb 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -61,6 +61,7 @@ typedef enum {
   GSK_OUTSET_SHADOW_NODE,
   GSK_TRANSFORM_NODE,
   GSK_OPACITY_NODE,
+  GSK_COLOR_MATRIX_NODE,
   GSK_CLIP_NODE,
   GSK_ROUNDED_CLIP_NODE,
   GSK_SHADOW_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 649dc69..e4a75ab 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -135,6 +135,11 @@ GDK_AVAILABLE_IN_3_90
 GskRenderNode *         gsk_opacity_node_get_child              (GskRenderNode            *node);
 
 GDK_AVAILABLE_IN_3_90
+GskRenderNode *         gsk_color_matrix_node_new               (GskRenderNode            *child,
+                                                                 const graphene_matrix_t  *color_matrix,
+                                                                 const graphene_vec4_t    *color_offset);
+
+GDK_AVAILABLE_IN_3_90
 GskRenderNode *         gsk_clip_node_new                       (GskRenderNode            *child,
                                                                  const graphene_rect_t    *clip);
 GDK_AVAILABLE_IN_3_90
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 6a632f0..61d504e 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -2336,6 +2336,251 @@ gsk_opacity_node_get_opacity (GskRenderNode *node)
   return self->opacity;
 }
 
+/*** GSK_COLOR_MATRIX_NODE ***/
+
+typedef struct _GskColorMatrixNode GskColorMatrixNode;
+
+struct _GskColorMatrixNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *child;
+  graphene_matrix_t color_matrix;
+  graphene_vec4_t color_offset;
+};
+
+static void
+gsk_color_matrix_node_finalize (GskRenderNode *node)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+
+  gsk_render_node_unref (self->child);
+}
+
+static void
+gsk_color_matrix_node_draw (GskRenderNode *node,
+                            cairo_t       *cr)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+  cairo_pattern_t *pattern;
+  cairo_surface_t *surface, *image_surface;
+  graphene_vec4_t pixel;
+  guint32* pixel_data;
+  guchar *data;
+  gsize x, y, width, height, stride;
+  float alpha;
+
+  cairo_save (cr);
+
+  /* clip so the push_group() creates a smaller surface */
+  cairo_rectangle (cr, node->bounds.origin.x, node->bounds.origin.y,
+                   node->bounds.size.width, node->bounds.size.height);
+  cairo_clip (cr);
+
+  cairo_push_group (cr);
+
+  gsk_render_node_draw (self->child, cr);
+
+  pattern = cairo_pop_group (cr);
+  cairo_pattern_get_surface (pattern, &surface);
+  image_surface = cairo_surface_map_to_image (surface, NULL);
+
+  data = cairo_image_surface_get_data (image_surface);
+  width = cairo_image_surface_get_width (image_surface);
+  height = cairo_image_surface_get_height (image_surface);
+  stride = cairo_image_surface_get_stride (image_surface);
+
+  for (y = 0; y < height; y++)
+    {
+      pixel_data = (guint32 *) data;
+      for (x = 0; x < width; x++)
+        {
+          alpha = ((pixel_data[x] >> 24) & 0xFF) / 255.0;
+
+          if (alpha == 0)
+            {
+              graphene_vec4_init (&pixel, 0.0, 0.0, 0.0, 0.0);
+            }
+          else
+            {
+              graphene_vec4_init (&pixel,
+                                  ((pixel_data[x] >> 16) & 0xFF) / (255.0 * alpha),
+                                  ((pixel_data[x] >>  8) & 0xFF) / (255.0 * alpha),
+                                  ( pixel_data[x]        & 0xFF) / (255.0 * alpha),
+                                  alpha);
+              graphene_matrix_transform_vec4 (&self->color_matrix, &pixel, &pixel);
+            }
+
+          graphene_vec4_add (&pixel, &self->color_offset, &pixel);
+
+          alpha = graphene_vec4_get_w (&pixel);
+          if (alpha > 0.0)
+            {
+              alpha = MIN (alpha, 1.0);
+              pixel_data[x] = (((guint32) (alpha * 255)) << 24) |
+                              (((guint32) (CLAMP (graphene_vec4_get_x (&pixel), 0, 1) * alpha * 255)) << 16) 
|
+                              (((guint32) (CLAMP (graphene_vec4_get_y (&pixel), 0, 1) * alpha * 255)) <<  8) 
|
+                               ((guint32) (CLAMP (graphene_vec4_get_z (&pixel), 0, 1) * alpha * 255));
+            }
+          else
+            {
+              pixel_data[x] = 0;
+            }
+        }
+      data += stride;
+    }
+
+  cairo_surface_mark_dirty (image_surface);
+  cairo_surface_unmap_image (surface, image_surface);
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_restore (cr);
+}
+
+#define GSK_COLOR_MATRIX_NODE_VARIANT_TYPE "(dddddddddddddddddddduv)"
+
+static GVariant *
+gsk_color_matrix_node_serialize (GskRenderNode *node)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+  float mat[16], vec[4];
+
+  graphene_matrix_to_float (&self->color_matrix, mat);
+  graphene_vec4_to_float (&self->color_offset, vec);
+
+  return g_variant_new (GSK_COLOR_MATRIX_NODE_VARIANT_TYPE,
+                        (double) mat[0], (double) mat[1], (double) mat[2], (double) mat[3],
+                        (double) mat[4], (double) mat[5], (double) mat[6], (double) mat[7],
+                        (double) mat[8], (double) mat[9], (double) mat[10], (double) mat[11],
+                        (double) mat[12], (double) mat[13], (double) mat[14], (double) mat[15],
+                        (double) vec[0], (double) vec[1], (double) vec[2], (double) vec[3],
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_color_matrix_node_deserialize (GVariant  *variant,
+                                   GError   **error)
+{
+  double mat[16], vec[4];
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+  graphene_matrix_t matrix;
+  graphene_vec4_t offset;
+
+  if (!check_variant_type (variant, GSK_COLOR_MATRIX_NODE_VARIANT_TYPE, error))
+    return NULL;
+
+  g_variant_get (variant, GSK_COLOR_MATRIX_NODE_VARIANT_TYPE,
+                 &mat[0], &mat[1], &mat[2], &mat[3],
+                 &mat[4], &mat[5], &mat[6], &mat[7],
+                 &mat[8], &mat[9], &mat[10], &mat[11],
+                 &mat[12], &mat[13], &mat[14], &mat[15],
+                 &vec[0], &vec[1], &vec[2], &vec[3],
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant, error);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  graphene_matrix_init_from_float (&matrix,
+                                   (float[16]) {
+                                       mat[0], mat[1], mat[2], mat[3],
+                                       mat[4], mat[5], mat[6], mat[7],
+                                       mat[8], mat[9], mat[10], mat[11],
+                                       mat[12], mat[13], mat[14], mat[15]
+                                   });
+  graphene_vec4_init (&offset, vec[0], vec[1], vec[2], vec[3]);
+                                    
+  result = gsk_color_matrix_node_new (child, &matrix, &offset);
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
+static const GskRenderNodeClass GSK_COLOR_MATRIX_NODE_CLASS = {
+  GSK_COLOR_MATRIX_NODE,
+  sizeof (GskColorMatrixNode),
+  "GskColorMatrixNode",
+  gsk_color_matrix_node_finalize,
+  gsk_color_matrix_node_draw,
+  gsk_color_matrix_node_serialize,
+  gsk_color_matrix_node_deserialize
+};
+
+/**
+ * gsk_color_matrix_node_new:
+ * @child: The node to draw
+ * @color_matrix: The matrix to apply
+ * @color_offset: Values to add to the color
+ *
+ * Creates a #GskRenderNode that will drawn the @child with reduced
+ * @color_matrix.
+ *
+ * In particular, the node will transform the operation  
+ *   pixel = color_matrix * pixel + color_offset
+ * for every pixel.
+ *
+ * Returns: A new #GskRenderNode
+ *
+ * Since: 3.90
+ */
+GskRenderNode *
+gsk_color_matrix_node_new (GskRenderNode           *child,
+                           const graphene_matrix_t *color_matrix,
+                           const graphene_vec4_t   *color_offset)
+{
+  GskColorMatrixNode *self;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+
+  self = (GskColorMatrixNode *) gsk_render_node_new (&GSK_COLOR_MATRIX_NODE_CLASS, 0);
+
+  self->child = gsk_render_node_ref (child);
+  graphene_matrix_init_from_matrix (&self->color_matrix, color_matrix);
+  graphene_vec4_init_from_vec4 (&self->color_offset, color_offset);
+
+  graphene_rect_init_from_rect (&self->render_node.bounds, &child->bounds);
+
+  return &self->render_node;
+}
+
+GskRenderNode *
+gsk_color_matrix_node_get_child (GskRenderNode *node)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_COLOR_MATRIX_NODE), NULL);
+
+  return self->child;
+}
+
+const graphene_matrix_t *
+gsk_color_matrix_node_peek_color_matrix (GskRenderNode *node)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_COLOR_MATRIX_NODE), NULL);
+
+  return &self->color_matrix;
+}
+
+const graphene_vec4_t *
+gsk_color_matrix_node_peek_color_offset (GskRenderNode *node)
+{
+  GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_COLOR_MATRIX_NODE), NULL);
+
+  return &self->color_offset;
+}
+
 /*** GSK_CLIP_NODE ***/
 
 typedef struct _GskClipNode GskClipNode;
@@ -3279,6 +3524,7 @@ static const GskRenderNodeClass *klasses[] = {
   [GSK_OUTSET_SHADOW_NODE] = &GSK_OUTSET_SHADOW_NODE_CLASS,
   [GSK_TRANSFORM_NODE] = &GSK_TRANSFORM_NODE_CLASS,
   [GSK_OPACITY_NODE] = &GSK_OPACITY_NODE_CLASS,
+  [GSK_COLOR_MATRIX_NODE] = &GSK_COLOR_MATRIX_NODE_CLASS,
   [GSK_CLIP_NODE] = &GSK_CLIP_NODE_CLASS,
   [GSK_ROUNDED_CLIP_NODE] = &GSK_ROUNDED_CLIP_NODE_CLASS,
   [GSK_SHADOW_NODE] = &GSK_SHADOW_NODE_CLASS,
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 7290263..90deb10 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -46,6 +46,10 @@ GskRenderNode * gsk_render_node_deserialize_node (GskRenderNodeType type, GVaria
 
 double gsk_opacity_node_get_opacity (GskRenderNode *node);
 
+GskRenderNode * gsk_color_matrix_node_get_child (GskRenderNode *node);
+const graphene_matrix_t * gsk_color_matrix_node_peek_color_matrix (GskRenderNode *node);
+const graphene_vec4_t * gsk_color_matrix_node_peek_color_offset (GskRenderNode *node);
+
 const graphene_point_t * gsk_linear_gradient_node_peek_start (GskRenderNode *node);
 const graphene_point_t * gsk_linear_gradient_node_peek_end (GskRenderNode *node);
 const gsize gsk_linear_gradient_node_get_n_color_stops (GskRenderNode *node);
diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c
index 1a37680..ddf6d21 100644
--- a/gtk/inspector/gtktreemodelrendernode.c
+++ b/gtk/inspector/gtktreemodelrendernode.c
@@ -541,6 +541,10 @@ append_node (GtkTreeModelRenderNode *nodemodel,
       append_node (nodemodel, gsk_opacity_node_get_child (node), priv->nodes->len - 1);
       break;
 
+    case GSK_COLOR_MATRIX_NODE:
+      append_node (nodemodel, gsk_color_matrix_node_get_child (node), priv->nodes->len - 1);
+      break;
+
     case GSK_CLIP_NODE:
       append_node (nodemodel, gsk_clip_node_get_child (node), priv->nodes->len - 1);
       break;
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 25f98a5..f6b1fc6 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -162,6 +162,8 @@ node_type_name (GskRenderNodeType type)
       return "Transform";
     case GSK_OPACITY_NODE:
       return "Opacity";
+    case GSK_COLOR_MATRIX_NODE:
+      return "Color Matrix";
     case GSK_CLIP_NODE:
       return "Clip";
     case GSK_ROUNDED_CLIP_NODE:


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