summaryrefslogtreecommitdiff
path: root/gsk
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2016-12-19 00:45:35 +0100
committerBenjamin Otte <otte@redhat.com>2016-12-20 18:01:12 +0100
commit2480e0d57530b72a8efa4fefeff98971b61e16da (patch)
treed3cfc00d1b527c4a87c5745389ce98e61d90870a /gsk
parent071c9a8221b53ab3e3586349187119221621d00a (diff)
downloadgtk+-2480e0d57530b72a8efa4fefeff98971b61e16da.tar.gz
gsk: Add GskShadowNode
... and make the icon rendering code use it. This requires moving even more shadow renering code into GSK, but so be it. At least the "shadows not implemented" warning is now gone!
Diffstat (limited to 'gsk')
-rw-r--r--gsk/gskcairoblur.c118
-rw-r--r--gsk/gskcairoblurprivate.h10
-rw-r--r--gsk/gskenums.h2
-rw-r--r--gsk/gskrendernode.h15
-rw-r--r--gsk/gskrendernodeimpl.c170
-rw-r--r--gsk/gskrendernodeprivate.h4
6 files changed, 318 insertions, 1 deletions
diff --git a/gsk/gskcairoblur.c b/gsk/gskcairoblur.c
index 0fcd1c63b4..0e66a5eb02 100644
--- a/gsk/gskcairoblur.c
+++ b/gsk/gskcairoblur.c
@@ -280,3 +280,121 @@ gsk_cairo_blur_compute_pixels (double radius)
return floor (radius * GAUSSIAN_SCALE_FACTOR * 1.5 + 0.5);
}
+static gboolean
+needs_blur (float radius)
+{
+ /* The code doesn't actually do any blurring for radius 1, as it
+ * ends up with box filter size 1 */
+ if (radius <= 1.0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static const cairo_user_data_key_t original_cr_key;
+
+cairo_t *
+gsk_cairo_blur_start_drawing (cairo_t *cr,
+ float radius,
+ GskBlurFlags blur_flags)
+{
+ cairo_rectangle_int_t clip_rect;
+ cairo_surface_t *surface;
+ cairo_t *blur_cr;
+ gdouble clip_radius;
+ gdouble x_scale, y_scale;
+ gboolean blur_x = (blur_flags & GSK_BLUR_X) != 0;
+ gboolean blur_y = (blur_flags & GSK_BLUR_Y) != 0;
+
+ if (!needs_blur (radius))
+ return cr;
+
+ gdk_cairo_get_clip_rectangle (cr, &clip_rect);
+
+ clip_radius = gsk_cairo_blur_compute_pixels (radius);
+
+ x_scale = y_scale = 1;
+ cairo_surface_get_device_scale (cairo_get_target (cr), &x_scale, &y_scale);
+
+ if (blur_flags & GSK_BLUR_REPEAT)
+ {
+ if (!blur_x)
+ clip_rect.width = 1;
+ if (!blur_y)
+ clip_rect.height = 1;
+ }
+
+ /* Create a larger surface to center the blur. */
+ surface = cairo_surface_create_similar_image (cairo_get_target (cr),
+ CAIRO_FORMAT_A8,
+ x_scale * (clip_rect.width + (blur_x ? 2 * clip_radius : 0)),
+ y_scale * (clip_rect.height + (blur_y ? 2 * clip_radius : 0)));
+ cairo_surface_set_device_scale (surface, x_scale, y_scale);
+ cairo_surface_set_device_offset (surface,
+ x_scale * ((blur_x ? clip_radius : 0) - clip_rect.x),
+ y_scale * ((blur_y ? clip_radius * y_scale : 0) - clip_rect.y));
+
+ blur_cr = cairo_create (surface);
+ cairo_set_user_data (blur_cr, &original_cr_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
+
+ if (cairo_has_current_point (cr))
+ {
+ double x, y;
+
+ cairo_get_current_point (cr, &x, &y);
+ cairo_move_to (blur_cr, x, y);
+ }
+
+ return blur_cr;
+}
+
+static void
+mask_surface_repeat (cairo_t *cr,
+ cairo_surface_t *surface)
+{
+ cairo_pattern_t *pattern;
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+ cairo_mask (cr, pattern);
+
+ cairo_pattern_destroy (pattern);
+}
+
+cairo_t *
+gsk_cairo_blur_finish_drawing (cairo_t *cr,
+ float radius,
+ const GdkRGBA *color,
+ GskBlurFlags blur_flags)
+{
+ cairo_t *original_cr;
+ cairo_surface_t *surface;
+ gdouble x_scale;
+
+ if (!needs_blur (radius))
+ return cr;
+
+ original_cr = cairo_get_user_data (cr, &original_cr_key);
+
+ /* Blur the surface. */
+ surface = cairo_get_target (cr);
+
+ x_scale = 1;
+ cairo_surface_get_device_scale (cairo_get_target (cr), &x_scale, NULL);
+
+ gsk_cairo_blur_surface (surface, x_scale * radius, blur_flags);
+
+ gdk_cairo_set_source_rgba (original_cr, color);
+ if (blur_flags & GSK_BLUR_REPEAT)
+ mask_surface_repeat (original_cr, surface);
+ else
+ cairo_mask_surface (original_cr, surface, 0, 0);
+
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (surface);
+
+ return original_cr;
+}
+
diff --git a/gsk/gskcairoblurprivate.h b/gsk/gskcairoblurprivate.h
index d723ca6f96..cd1e68758a 100644
--- a/gsk/gskcairoblurprivate.h
+++ b/gsk/gskcairoblurprivate.h
@@ -24,7 +24,7 @@
#ifndef _GSK_CAIRO_BLUR_H
#define _GSK_CAIRO_BLUR_H
-#include <glib.h>
+#include <gdk/gdk.h>
#include <cairo.h>
G_BEGIN_DECLS
@@ -41,6 +41,14 @@ void gsk_cairo_blur_surface (cairo_surface_t *surface,
GskBlurFlags flags);
int gsk_cairo_blur_compute_pixels (double radius);
+cairo_t * gsk_cairo_blur_start_drawing (cairo_t *cr,
+ float radius,
+ GskBlurFlags blur_flags);
+cairo_t * gsk_cairo_blur_finish_drawing (cairo_t *cr,
+ float radius,
+ const GdkRGBA *color,
+ GskBlurFlags blur_flags);
+
G_END_DECLS
#endif /* _GSK_CAIRO_BLUR_H */
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 7bad39d1c2..9defa36653 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -38,6 +38,7 @@
* @GSK_OPACITY_NODE: A node that changes the opacity of its child
* @GSK_CLIP_NODE: A node that clips its child to a rectangular area
* @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle
+ * @GSK_SHADOW_NODE: A node that draws a shadow below its child
* @GSK_BLEND_NODE: A node the blends two children together
* @GSK_CROSS_FADE_NODE: A node the cross-fades between two children
*
@@ -58,6 +59,7 @@ typedef enum {
GSK_OPACITY_NODE,
GSK_CLIP_NODE,
GSK_ROUNDED_CLIP_NODE,
+ GSK_SHADOW_NODE,
GSK_BLEND_NODE,
GSK_CROSS_FADE_NODE
} GskRenderNodeType;
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index a349b8b302..1c4620737b 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
typedef struct _GskRenderNode GskRenderNode;
typedef struct _GskColorStop GskColorStop;
+typedef struct _GskShadow GskShadow;
struct _GskColorStop
{
@@ -41,6 +42,15 @@ struct _GskColorStop
GdkRGBA color;
};
+struct _GskShadow
+{
+ GdkRGBA color;
+ float dx;
+ float dy;
+ float spread;
+ float radius;
+};
+
GDK_AVAILABLE_IN_3_90
GType gsk_render_node_get_type (void) G_GNUC_CONST;
@@ -118,6 +128,11 @@ GDK_AVAILABLE_IN_3_90
GskRenderNode * gsk_rounded_clip_node_get_child (GskRenderNode *node);
GDK_AVAILABLE_IN_3_90
+GskRenderNode * gsk_shadow_node_new (GskRenderNode *child,
+ const GskShadow *shadows,
+ gsize n_shadows);
+
+GDK_AVAILABLE_IN_3_90
GskRenderNode * gsk_blend_node_new (GskRenderNode *bottom,
GskRenderNode *top,
GskBlendMode blend_mode);
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index c5d8d615d3..a7453c6e88 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -18,6 +18,7 @@
#include "gskrendernodeprivate.h"
+#include "gskcairoblurprivate.h"
#include "gskdebugprivate.h"
#include "gskrendererprivate.h"
#include "gskroundedrectprivate.h"
@@ -1410,6 +1411,175 @@ gsk_rounded_clip_node_peek_clip (GskRenderNode *node)
return &self->clip;
}
+/*** GSK_SHADOW_NODE ***/
+
+typedef struct _GskShadowNode GskShadowNode;
+
+struct _GskShadowNode
+{
+ GskRenderNode render_node;
+
+ GskRenderNode *child;
+
+ GskShadow *shadows;
+ gsize n_shadows;
+};
+
+static void
+gsk_shadow_node_finalize (GskRenderNode *node)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+
+ gsk_render_node_unref (self->child);
+
+ g_free (self->shadows);
+}
+
+static void
+gsk_shadow_node_make_immutable (GskRenderNode *node)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+
+ gsk_render_node_make_immutable (self->child);
+}
+
+static void
+gsk_shadow_node_draw (GskRenderNode *node,
+ cairo_t *cr)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+ cairo_pattern_t *pattern;
+ gsize i;
+
+ cairo_push_group (cr);
+ gsk_render_node_draw (self->child, cr);
+ pattern = cairo_pop_group (cr);
+
+ for (i = 0; i < self->n_shadows; i++)
+ {
+ GskShadow *shadow = &self->shadows[i];
+
+ /* We don't need to draw invisible shadows */
+ if (gdk_rgba_is_clear (&shadow->color))
+ continue;
+
+ cairo_save (cr);
+ gdk_cairo_set_source_rgba (cr, &shadow->color);
+ cr = gsk_cairo_blur_start_drawing (cr, shadow->radius, GSK_BLUR_X | GSK_BLUR_Y);
+
+ cairo_translate (cr, shadow->dx, shadow->dy);
+ cairo_mask (cr, pattern);
+
+ cr = gsk_cairo_blur_finish_drawing (cr, shadow->radius, &shadow->color, GSK_BLUR_X | GSK_BLUR_Y);
+ cairo_restore (cr);
+ }
+
+ cairo_set_source (cr, pattern);
+ cairo_paint (cr);
+
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+gsk_shadow_node_get_bounds (GskRenderNode *node,
+ graphene_rect_t *bounds)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+ float top = 0, right = 0, bottom = 0, left = 0;
+ gsize i;
+
+ gsk_render_node_get_bounds (self->child, bounds);
+
+ for (i = 0; i < self->n_shadows; i++)
+ {
+ float clip_radius = gsk_cairo_blur_compute_pixels (self->shadows[i].radius);
+ top = MAX (top, clip_radius - self->shadows[i].dy);
+ right = MAX (right, clip_radius + self->shadows[i].dx);
+ bottom = MAX (bottom, clip_radius + self->shadows[i].dy);
+ left = MAX (left, clip_radius - self->shadows[i].dx);
+ }
+
+ bounds->origin.x -= left;
+ bounds->origin.y -= top;
+ bounds->size.width += left + right;
+ bounds->size.height += top + bottom;
+}
+
+static const GskRenderNodeClass GSK_SHADOW_NODE_CLASS = {
+ GSK_SHADOW_NODE,
+ sizeof (GskShadowNode),
+ "GskShadowNode",
+ gsk_shadow_node_finalize,
+ gsk_shadow_node_make_immutable,
+ gsk_shadow_node_draw,
+ gsk_shadow_node_get_bounds
+};
+
+/**
+ * gsk_shadow_node_new:
+ * @child: The node to draw
+ * @shadows: (array length=n_shadows): The shadows to apply
+ * @n_shadows: number of entries in the @shadows array
+ *
+ * Creates a #GskRenderNode that will draw a @child with the given
+ * @shadows below it.
+ *
+ * Returns: A new #GskRenderNode
+ *
+ * Since: 3.90
+ */
+GskRenderNode *
+gsk_shadow_node_new (GskRenderNode *child,
+ const GskShadow *shadows,
+ gsize n_shadows)
+{
+ GskShadowNode *self;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+ g_return_val_if_fail (shadows != NULL, NULL);
+ g_return_val_if_fail (n_shadows > 0, NULL);
+
+ self = (GskShadowNode *) gsk_render_node_new (&GSK_SHADOW_NODE_CLASS);
+
+ self->child = gsk_render_node_ref (child);
+ self->shadows = g_memdup (shadows, n_shadows * sizeof (GskShadow));
+ self->n_shadows = n_shadows;
+
+ return &self->render_node;
+}
+
+GskRenderNode *
+gsk_shadow_node_get_child (GskRenderNode *node)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_SHADOW_NODE), NULL);
+
+ return self->child;
+}
+
+const GskShadow *
+gsk_shadow_node_peek_shadow (GskRenderNode *node,
+ gsize i)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_SHADOW_NODE), NULL);
+ g_return_val_if_fail (i < self->n_shadows, NULL);
+
+ return &self->shadows[i];
+}
+
+gsize
+gsk_shadow_node_get_n_shadows (GskRenderNode *node)
+{
+ GskShadowNode *self = (GskShadowNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_SHADOW_NODE), 0);
+
+ return self->n_shadows;
+}
+
/*** GSK_BLEND_NODE ***/
typedef struct _GskBlendNode GskBlendNode;
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 24e87e8880..d56c85ce3b 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -62,6 +62,10 @@ const graphene_rect_t * gsk_clip_node_peek_clip (GskRenderNode *node);
const GskRoundedRect * gsk_rounded_clip_node_peek_clip (GskRenderNode *node);
+GskRenderNode * gsk_shadow_node_get_child (GskRenderNode *node);
+const GskShadow * gsk_shadow_node_peek_shadow (GskRenderNode *node, gsize i);
+gsize gsk_shadow_node_get_n_shadows (GskRenderNode *node);
+
void gsk_transform_node_get_transform (GskRenderNode *node, graphene_matrix_t *transform);
GskRenderNode * gsk_blend_node_get_bottom_child (GskRenderNode *node);