summaryrefslogtreecommitdiff
path: root/gsk
diff options
context:
space:
mode:
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);