[gtk+/wip/otte/rendernode: 22/30] gsk: Add GskOutsetShadowNode



commit 61baf9bf600f67942e248a773ce5e624683c2f8c
Author: Benjamin Otte <otte redhat com>
Date:   Mon Dec 19 15:39:43 2016 +0100

    gsk: Add GskOutsetShadowNode

 docs/reference/gsk/gsk4-sections.txt   |    1 +
 gsk/gskenums.h                         |    2 +
 gsk/gskrendernode.h                    |    7 +
 gsk/gskrendernodeimpl.c                |  220 +++++++++++++++++++++++++++++++-
 gtk/gtkcssshadowvalue.c                |   35 +++---
 gtk/inspector/gtktreemodelrendernode.c |    1 +
 gtk/inspector/recorder.c               |    2 +
 7 files changed, 249 insertions(+), 19 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 9ddf205..9c9fee1 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -38,6 +38,7 @@ gsk_repeating_linear_gradient_node_new
 gsk_border_node_new
 gsk_texture_node_new
 gsk_inset_shadow_node_new
+gsk_outset_shadow_node_new
 gsk_cairo_node_new
 gsk_cairo_node_get_draw_context
 gsk_container_node_new
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 1653654..3b6df78 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -34,6 +34,7 @@
  * @GSK_BORDER_NODE: A node stroking a border around an area
  * @GSK_TEXTURE_NODE: A node drawing a #GskTexture
  * @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
+ * @GSK_OUTSET_SHADOW_NODE: A node drawing an outset shadow
  * @GSK_TRANSFORM_NODE: A node that renders its child after applying a
  *     matrix transform
  * @GSK_OPACITY_NODE: A node that changes the opacity of its child
@@ -57,6 +58,7 @@ typedef enum {
   GSK_BORDER_NODE,
   GSK_TEXTURE_NODE,
   GSK_INSET_SHADOW_NODE,
+  GSK_OUTSET_SHADOW_NODE,
   GSK_TRANSFORM_NODE,
   GSK_OPACITY_NODE,
   GSK_CLIP_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 6fc8724..fad1e53 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -95,6 +95,13 @@ GskRenderNode *         gsk_inset_shadow_node_new               (const GskRounde
                                                                  float                     dy,
                                                                  float                     spread,
                                                                  float                     blur_radius);
+GDK_AVAILABLE_IN_3_90
+GskRenderNode *         gsk_outset_shadow_node_new              (const GskRoundedRect     *outline,
+                                                                 const GdkRGBA            *color,
+                                                                 float                     dx,
+                                                                 float                     dy,
+                                                                 float                     spread,
+                                                                 float                     blur_radius);
 
 GDK_AVAILABLE_IN_3_90
 GskRenderNode *         gsk_cairo_node_new                      (const graphene_rect_t    *bounds);
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 9a9e6cd..20ff948 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -866,11 +866,11 @@ draw_shadow_side (cairo_t               *cr,
 }
 
 static gboolean
-needs_blur (GskInsetShadowNode *self)
+needs_blur (double radius)
 {
   /* The code doesn't actually do any blurring for radius 1, as it
    * ends up with box filter size 1 */
-  if (self->blur_radius <= 1.0)
+  if (radius <= 1.0)
     return FALSE;
 
   return TRUE;
@@ -907,7 +907,7 @@ gsk_inset_shadow_node_draw (GskRenderNode *node,
   gsk_rounded_rect_init_copy (&clip_box, &self->outline);
   gsk_rounded_rect_shrink (&clip_box, -clip_radius, -clip_radius, -clip_radius, -clip_radius);
 
-  if (!needs_blur (self))
+  if (!needs_blur (self->blur_radius))
     draw_shadow (cr, TRUE, &box, &clip_box, self->blur_radius, &self->color, GSK_BLUR_NONE);
   else
     {
@@ -1034,6 +1034,220 @@ gsk_inset_shadow_node_new (const GskRoundedRect *outline,
   return &self->render_node;
 }
 
+/*** GSK_OUTSET_SHADOW_NODE ***/
+
+typedef struct _GskOutsetShadowNode GskOutsetShadowNode;
+
+struct _GskOutsetShadowNode
+{
+  GskRenderNode render_node;
+
+  GskRoundedRect outline;
+  GdkRGBA color;
+  float dx;
+  float dy;
+  float spread;
+  float blur_radius;
+};
+
+static void
+gsk_outset_shadow_node_finalize (GskRenderNode *node)
+{
+}
+
+static void
+gsk_outset_shadow_node_make_immutable (GskRenderNode *node)
+{
+}
+
+static void
+gsk_outset_shadow_get_extents (GskOutsetShadowNode *self,
+                               float               *top,
+                               float               *right,
+                               float               *bottom,
+                               float               *left)
+{
+  float clip_radius;
+
+  clip_radius = gsk_cairo_blur_compute_pixels (self->blur_radius);
+  *top = MAX (0, clip_radius + self->spread - self->dy);
+  *right = MAX (0, ceil (clip_radius + self->spread + self->dx));
+  *bottom = MAX (0, ceil (clip_radius + self->spread + self->dy));
+  *left = MAX (0, ceil (clip_radius + self->spread - self->dx));
+}
+
+static void
+gsk_outset_shadow_node_draw (GskRenderNode *node,
+                             cairo_t       *cr)
+{
+  GskOutsetShadowNode *self = (GskOutsetShadowNode *) node;
+  GskRoundedRect box, clip_box;
+  int clip_radius;
+  double x1c, y1c, x2c, y2c;
+  float top, right, bottom, left;
+
+  /* We don't need to draw invisible shadows */
+  if (gdk_rgba_is_clear (&self->color))
+    return;
+
+  cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
+  if (gsk_rounded_rect_contains_rect (&self->outline, &GRAPHENE_RECT_INIT (x1c, y1c, x2c - x1c, y2c - y1c)))
+    return;
+
+  clip_radius = gsk_cairo_blur_compute_pixels (self->blur_radius);
+
+  cairo_save (cr);
+
+  gsk_rounded_rect_init_copy (&clip_box, &self->outline);
+  gsk_outset_shadow_get_extents (self, &top, &right, &bottom, &left);
+  gsk_rounded_rect_shrink (&clip_box, -top, -right, -bottom, -left);
+
+  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+  gsk_rounded_rect_path (&self->outline, cr);
+  cairo_rectangle (cr,
+                   clip_box.bounds.origin.x, clip_box.bounds.origin.y,
+                   clip_box.bounds.size.width, clip_box.bounds.size.height);
+
+  cairo_clip (cr);
+
+  gsk_rounded_rect_init_copy (&box, &self->outline);
+  gsk_rounded_rect_offset (&box, self->dx, self->dy);
+  gsk_rounded_rect_shrink (&box, -self->spread, -self->spread, -self->spread, -self->spread);
+
+  if (!needs_blur (self->blur_radius))
+    draw_shadow (cr, FALSE, &box, &clip_box, self->blur_radius, &self->color, GSK_BLUR_NONE);
+  else
+    {
+      int i;
+      cairo_region_t *remaining;
+      cairo_rectangle_int_t r;
+
+      /* For the blurred case we divide the rendering into 9 parts,
+       * 4 of the corners, 4 for the horizonat/vertical lines and
+       * one for the interior. We make the non-interior parts
+       * large enought to fit the full radius of the blur, so that
+       * the interior part can be drawn solidly.
+       */
+
+      /* In the outset case we want to paint the entire box, plus as far
+       * as the radius reaches from it */
+      r.x = floor (box.bounds.origin.x - clip_radius);
+      r.y = floor (box.bounds.origin.y - clip_radius);
+      r.width = ceil (box.bounds.origin.x + box.bounds.size.width + clip_radius) - r.x;
+      r.height = ceil (box.bounds.origin.y + box.bounds.size.height + clip_radius) - r.y;
+
+      remaining = cairo_region_create_rectangle (&r);
+
+      /* First do the corners of box */
+      for (i = 0; i < 4; i++)
+       {
+         cairo_save (cr);
+          /* Always clip with remaining to ensure we never draw any area twice */
+          gdk_cairo_region (cr, remaining);
+          cairo_clip (cr);
+         draw_shadow_corner (cr, FALSE, &box, &clip_box, self->blur_radius, &self->color, i, &r);
+         cairo_restore (cr);
+
+         /* We drew the region, remove it from remaining */
+         cairo_region_subtract_rectangle (remaining, &r);
+       }
+
+      /* Then the sides */
+      for (i = 0; i < 4; i++)
+       {
+         cairo_save (cr);
+          /* Always clip with remaining to ensure we never draw any area twice */
+          gdk_cairo_region (cr, remaining);
+          cairo_clip (cr);
+         draw_shadow_side (cr, FALSE, &box, &clip_box, self->blur_radius, &self->color, i, &r);
+         cairo_restore (cr);
+
+         /* We drew the region, remove it from remaining */
+         cairo_region_subtract_rectangle (remaining, &r);
+       }
+
+      /* Then the rest, which needs no blurring */
+
+      cairo_save (cr);
+      gdk_cairo_region (cr, remaining);
+      cairo_clip (cr);
+      draw_shadow (cr, FALSE, &box, &clip_box, self->blur_radius, &self->color, GSK_BLUR_NONE);
+      cairo_restore (cr);
+
+      cairo_region_destroy (remaining);
+    }
+
+  cairo_restore (cr);
+}
+
+static void
+gsk_outset_shadow_node_get_bounds (GskRenderNode   *node,
+                                   graphene_rect_t *bounds)
+{
+  GskOutsetShadowNode *self = (GskOutsetShadowNode *) node;
+  float top, right, bottom, left;
+
+  gsk_outset_shadow_get_extents (self, &top, &right, &bottom, &left);
+
+  graphene_rect_init_from_rect (bounds, &self->outline.bounds); 
+
+  bounds->origin.x -= left;
+  bounds->origin.y -= top;
+  bounds->size.width += left + right;
+  bounds->size.height += top + bottom;
+}
+
+static const GskRenderNodeClass GSK_OUTSET_SHADOW_NODE_CLASS = {
+  GSK_OUTSET_SHADOW_NODE,
+  sizeof (GskOutsetShadowNode),
+  "GskOutsetShadowNode",
+  gsk_outset_shadow_node_finalize,
+  gsk_outset_shadow_node_make_immutable,
+  gsk_outset_shadow_node_draw,
+  gsk_outset_shadow_node_get_bounds
+};
+
+/**
+ * gsk_outset_shadow_node_new:
+ * @outline: outline of the region surrounded by shadow
+ * @color: color of the shadow
+ * @dx: horizontal offset of shadow
+ * @dy: vertical offset of shadow
+ * @spread: how far the shadow spreads towards the inside
+ * @blur_radius: how much blur to apply to the shadow
+ *
+ * Creates a #GskRenderNode that will render an outset shadow
+ * around the box given by @outline.
+ *
+ * Returns: A new #GskRenderNode
+ *
+ * Since: 3.90
+ */
+GskRenderNode *
+gsk_outset_shadow_node_new (const GskRoundedRect *outline,
+                            const GdkRGBA        *color,
+                            float                 dx,
+                            float                 dy,
+                            float                 spread,
+                            float                 blur_radius)
+{
+  GskOutsetShadowNode *self;
+
+  g_return_val_if_fail (outline != NULL, NULL);
+  g_return_val_if_fail (color != NULL, NULL);
+
+  self = (GskOutsetShadowNode *) gsk_render_node_new (&GSK_OUTSET_SHADOW_NODE_CLASS);
+
+  gsk_rounded_rect_init_copy (&self->outline, outline);
+  self->color = *color;
+  self->dx = dx;
+  self->dy = dy;
+  self->spread = spread;
+  self->blur_radius = blur_radius;
+
+  return &self->render_node;
+}
+
 /*** GSK_CAIRO_NODE ***/
 
 typedef struct _GskCairoNode GskCairoNode;
diff --git a/gtk/gtkcssshadowvalue.c b/gtk/gtkcssshadowvalue.c
index 91b24bf..d9e1aaa 100644
--- a/gtk/gtkcssshadowvalue.c
+++ b/gtk/gtkcssshadowvalue.c
@@ -1035,12 +1035,13 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
 }
 
 void
-gtk_css_shadow_value_snapshot_outset (const GtkCssValue   *shadow,
-                                      GtkSnapshot         *snapshot,
-                                      const GskRoundedRect*border_box)
+gtk_css_shadow_value_snapshot_outset (const GtkCssValue    *shadow,
+                                      GtkSnapshot          *snapshot,
+                                      const GskRoundedRect *border_box)
 {
-  GtkBorder extents;
-  cairo_t *cr;
+  GskRoundedRect outline;
+  GskRenderNode *node;
+  double off_x, off_y;
 
   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
@@ -1048,17 +1049,19 @@ gtk_css_shadow_value_snapshot_outset (const GtkCssValue   *shadow,
   if (gdk_rgba_is_clear (_gtk_css_rgba_value_get_rgba (shadow->color)))
     return;
 
-  gtk_css_shadow_value_get_extents (shadow, &extents);
+  gtk_snapshot_get_offset (snapshot, &off_x, &off_y);
+  gsk_rounded_rect_init_copy (&outline, border_box);
+  gsk_rounded_rect_offset (&outline, off_x, off_y);
 
-  cr = gtk_snapshot_append_cairo_node (snapshot,
-                                       &GRAPHENE_RECT_INIT (
-                                          border_box->bounds.origin.x - extents.left,
-                                          border_box->bounds.origin.y - extents.top,
-                                          border_box->bounds.size.width + extents.left + extents.right,
-                                          border_box->bounds.size.height + extents.top + extents.bottom),
-                                       "Outset Shadow");
-  _gtk_css_shadow_value_paint_box (shadow, cr, border_box);
-  cairo_destroy (cr);
+  node = gsk_outset_shadow_node_new (&outline, 
+                                     _gtk_css_rgba_value_get_rgba (shadow->color),
+                                     _gtk_css_number_value_get (shadow->hoffset, 0),
+                                     _gtk_css_number_value_get (shadow->voffset, 0),
+                                     _gtk_css_number_value_get (shadow->spread, 0),
+                                     _gtk_css_number_value_get (shadow->radius, 0));
+  gsk_render_node_set_name (node, "Outset Shadow");
+  gtk_snapshot_append_node (snapshot, node);
+  gsk_render_node_unref (node);
 }
 
 void
diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c
index 32177fa..1a37680 100644
--- a/gtk/inspector/gtktreemodelrendernode.c
+++ b/gtk/inspector/gtktreemodelrendernode.c
@@ -529,6 +529,7 @@ append_node (GtkTreeModelRenderNode *nodemodel,
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_BORDER_NODE:
     case GSK_INSET_SHADOW_NODE:
+    case GSK_OUTSET_SHADOW_NODE:
       /* no children */
       break;
 
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 23f6d86..e39bdab 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -153,6 +153,8 @@ node_type_name (GskRenderNodeType type)
       return "Texture";
     case GSK_INSET_SHADOW_NODE:
       return "Inset Shadow";
+    case GSK_OUTSET_SHADOW_NODE:
+      return "Outset Shadow";
     case GSK_TRANSFORM_NODE:
       return "Transform";
     case GSK_OPACITY_NODE:


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