diff options
Diffstat (limited to 'gsk')
-rw-r--r-- | gsk/gskcairoblur.c | 118 | ||||
-rw-r--r-- | gsk/gskcairoblurprivate.h | 10 | ||||
-rw-r--r-- | gsk/gskenums.h | 2 | ||||
-rw-r--r-- | gsk/gskrendernode.h | 15 | ||||
-rw-r--r-- | gsk/gskrendernodeimpl.c | 170 | ||||
-rw-r--r-- | gsk/gskrendernodeprivate.h | 4 |
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); |