From 0eb791eaaa2672ffdb69c4bca47125aea6b1c2ba Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 14 Feb 2023 13:44:39 -0500 Subject: [PATCH 1/4] Make mask nodes more versatile Add a GskMaskMode enumeration and implement it in the GL and cairo renderers. --- demos/gtk-demo/demo4widget.c | 2 +- gdk/wayland/gdkglcontext-wayland.c | 6 ++ gdk/wayland/gdksurface-wayland.c | 4 +- gsk/gl/gskglprograms.defs | 3 +- gsk/gl/gskglrenderjob.c | 8 +- gsk/gl/resources/mask.glsl | 16 +++- gsk/gskenums.h | 20 +++++ gsk/gskrendernode.h | 5 +- gsk/gskrendernodeimpl.c | 137 +++++++++++++++++++++++------ gsk/gskrendernodeparser.c | 73 ++++++++++++--- gtk/gtksnapshot.c | 10 ++- gtk/gtksnapshot.h | 3 +- gtk/inspector/recorder.c | 6 ++ 13 files changed, 244 insertions(+), 49 deletions(-) diff --git a/demos/gtk-demo/demo4widget.c b/demos/gtk-demo/demo4widget.c index 3478272197..0152581c3d 100644 --- a/demos/gtk-demo/demo4widget.c +++ b/demos/gtk-demo/demo4widget.c @@ -92,7 +92,7 @@ demo4_widget_snapshot (GtkWidget *widget, width = gtk_widget_get_width (widget); height = gtk_widget_get_height (widget); - gtk_snapshot_push_mask (snapshot); + gtk_snapshot_push_mask (snapshot, GSK_MASK_MODE_INVERTED_ALPHA); gtk_snapshot_append_layout (snapshot, self->layout, &(GdkRGBA) { 0, 0, 0, 1 }); gtk_snapshot_pop (snapshot); diff --git a/gdk/wayland/gdkglcontext-wayland.c b/gdk/wayland/gdkglcontext-wayland.c index e7341d4fe6..44e15fa0fd 100644 --- a/gdk/wayland/gdkglcontext-wayland.c +++ b/gdk/wayland/gdkglcontext-wayland.c @@ -25,6 +25,7 @@ #include "gdkdisplay-wayland.h" #include "gdksurface-wayland.h" +#include "gdksurface-wayland-private.h" #include "gdkwaylanddisplay.h" #include "gdkwaylandglcontext.h" @@ -60,10 +61,15 @@ gdk_wayland_gl_context_end_frame (GdkDrawContext *draw_context, cairo_region_t *painted) { GdkSurface *surface = gdk_draw_context_get_surface (draw_context); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + int dx = impl->pending_buffer_offset_x; + int dy = impl->pending_buffer_offset_y; gdk_wayland_surface_sync (surface); gdk_wayland_surface_request_frame (surface); + wl_surface_offset (impl->display_server.wl_surface, dx, dy); + GDK_DRAW_CONTEXT_CLASS (gdk_wayland_gl_context_parent_class)->end_frame (draw_context, painted); gdk_wayland_surface_notify_committed (surface); diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 62115ab850..8e34132bcc 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -229,7 +229,7 @@ gdk_wayland_surface_update_size (GdkSurface *surface, if (impl->display_server.egl_window) wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0); - if (impl->display_server.wl_surface) + if (impl->display_server.wl_surface && scale_changed) wl_surface_set_buffer_scale (impl->display_server.wl_surface, scale); gdk_surface_invalidate_rect (surface, NULL); @@ -588,6 +588,7 @@ gdk_wayland_surface_sync_offset (GdkSurface *surface) WL_SURFACE_OFFSET_SINCE_VERSION) return; +#if 0 if (impl->pending_buffer_offset_x == 0 && impl->pending_buffer_offset_y == 0) return; @@ -597,6 +598,7 @@ gdk_wayland_surface_sync_offset (GdkSurface *surface) impl->pending_buffer_offset_y); impl->pending_buffer_offset_x = 0; impl->pending_buffer_offset_y = 0; +#endif } void diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs index 3301529f9c..9afb826bf6 100644 --- a/gsk/gl/gskglprograms.defs +++ b/gsk/gl/gskglprograms.defs @@ -62,7 +62,8 @@ GSK_GL_DEFINE_PROGRAM (linear_gradient, GSK_GL_DEFINE_PROGRAM (mask, GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("mask.glsl")), - GSK_GL_ADD_UNIFORM (1, MASK_SOURCE, u_mask)) + GSK_GL_ADD_UNIFORM (1, MASK_SOURCE, u_mask) + GSK_GL_ADD_UNIFORM (2, MASK_MODE, u_mode)) GSK_GL_DEFINE_PROGRAM (outset_shadow, GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("outset_shadow.glsl")), diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 6381bcbb14..83f928ba5d 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -3278,8 +3278,8 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job, } static inline void -gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job, - const GskRenderNode *node) +gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job, + const GskRenderNode *node) { const GskRenderNode *source = gsk_mask_node_get_source (node); const GskRenderNode *mask = gsk_mask_node_get_mask (node); @@ -3293,6 +3293,7 @@ gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job, mask_offscreen.bounds = &node->bounds; mask_offscreen.force_offscreen = TRUE; mask_offscreen.reset_clip = TRUE; + mask_offscreen.do_not_cache = TRUE; /* TODO: We create 2 textures here as big as the mask node, but both * nodes might be a lot smaller than that. @@ -3323,6 +3324,9 @@ gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job, GL_TEXTURE_2D, GL_TEXTURE1, mask_offscreen.texture_id); + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_MASK_MODE, 0, + gsk_mask_node_get_mask_mode (node)); gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds); gsk_gl_render_job_end_draw (job); } diff --git a/gsk/gl/resources/mask.glsl b/gsk/gl/resources/mask.glsl index 9c187b5a4e..f856cb8fd0 100644 --- a/gsk/gl/resources/mask.glsl +++ b/gsk/gl/resources/mask.glsl @@ -10,10 +10,24 @@ void main() { // FRAGMENT_SHADER: // mask.glsl +uniform int u_mode; uniform sampler2D u_mask; void main() { vec4 source = GskTexture(u_source, vUv); vec4 mask = GskTexture(u_mask, vUv); - gskSetOutputColor(vec4 (source * mask.a)); + float mask_value; + + if (u_mode == 0) + mask_value = mask.a; + else if (u_mode == 1) + mask_value = 1 - mask.a; + else if (u_mode == 2) + mask_value = (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a; + else if (u_mode == 3) + mask_value = 1 - (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a; + else + mask_value = 0; + + gskSetOutputColor(vec4 (source * mask_value)); } diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 20bcf05020..92e6aa456a 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -255,5 +255,25 @@ typedef enum GSK_GL_UNIFORM_TYPE_VEC4, } GskGLUniformType; +/** + * GskMaskMode: + * @GSK_MASK_MODE_ALPHA: Use the alpha channel of the mask + * @GSK_MASK_MODE_INVERTED_ALPHA: Use the inverted alpha channel of the mask + * @GSK_MASK_MODE_LUMINANCE: Use the luminance of the mask, + * multiplied by mask alpha + * @GSK_MASK_MODE_INVERTED_LUMINANCE: Use the inverted luminance of the mask, + * multiplied by mask alpha + * + * The mask modes available for mask nodes. + * + * Since: 4.10 + */ +typedef enum +{ + GSK_MASK_MODE_ALPHA, + GSK_MASK_MODE_INVERTED_ALPHA, + GSK_MASK_MODE_LUMINANCE, + GSK_MASK_MODE_INVERTED_LUMINANCE +} GskMaskMode; #endif /* __GSK_TYPES_H__ */ diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 7147b0b848..36b7ae190c 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -535,11 +535,14 @@ GDK_AVAILABLE_IN_4_10 GType gsk_mask_node_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_4_10 GskRenderNode * gsk_mask_node_new (GskRenderNode *source, - GskRenderNode *mask); + GskRenderNode *mask, + GskMaskMode mask_mode); GDK_AVAILABLE_IN_4_10 GskRenderNode * gsk_mask_node_get_source (const GskRenderNode *node); GDK_AVAILABLE_IN_4_10 GskRenderNode * gsk_mask_node_get_mask (const GskRenderNode *node); +GDK_AVAILABLE_IN_4_10 +GskMaskMode gsk_mask_node_get_mask_mode (const GskRenderNode *node); GDK_AVAILABLE_IN_ALL GType gsk_gl_shader_node_get_type (void) G_GNUC_CONST; diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index f9de3752d3..819446e88e 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -3410,29 +3410,18 @@ gsk_color_matrix_node_finalize (GskRenderNode *node) } static void -gsk_color_matrix_node_draw (GskRenderNode *node, - cairo_t *cr) +apply_color_matrix_to_pattern (cairo_pattern_t *pattern, + const graphene_matrix_t *color_matrix, + const graphene_vec4_t *color_offset, + gboolean multiply_alpha) { - 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; + float alpha, orig_alpha; + graphene_vec4_t pixel; + guint32* pixel_data; - cairo_save (cr); - - /* clip so the push_group() creates a smaller surface */ - gsk_cairo_rectangle (cr, &node->bounds); - 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); @@ -3446,7 +3435,7 @@ gsk_color_matrix_node_draw (GskRenderNode *node, pixel_data = (guint32 *) data; for (x = 0; x < width; x++) { - alpha = ((pixel_data[x] >> 24) & 0xFF) / 255.0; + alpha = orig_alpha = ((pixel_data[x] >> 24) & 0xFF) / 255.0; if (alpha == 0) { @@ -3459,12 +3448,16 @@ gsk_color_matrix_node_draw (GskRenderNode *node, ((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_matrix_transform_vec4 (color_matrix, &pixel, &pixel); } - graphene_vec4_add (&pixel, &self->color_offset, &pixel); + graphene_vec4_add (&pixel, color_offset, &pixel); alpha = graphene_vec4_get_w (&pixel); + + if (multiply_alpha) + alpha *= orig_alpha; + if (alpha > 0.0) { alpha = MIN (alpha, 1.0); @@ -3483,6 +3476,28 @@ gsk_color_matrix_node_draw (GskRenderNode *node, cairo_surface_mark_dirty (image_surface); cairo_surface_unmap_image (surface, image_surface); +} + +static void +gsk_color_matrix_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskColorMatrixNode *self = (GskColorMatrixNode *) node; + cairo_pattern_t *pattern; + + cairo_save (cr); + + /* clip so the push_group() creates a smaller surface */ + gsk_cairo_rectangle (cr, &node->bounds); + cairo_clip (cr); + + cairo_push_group (cr); + + gsk_render_node_draw (self->child, cr); + + pattern = cairo_pop_group (cr); + + apply_color_matrix_to_pattern (pattern, &self->color_matrix, &self->color_offset, FALSE); cairo_set_source (cr, pattern); cairo_paint (cr); @@ -5202,6 +5217,7 @@ struct _GskMaskNode GskRenderNode *mask; GskRenderNode *source; + GskMaskMode mask_mode; }; static void @@ -5219,6 +5235,8 @@ gsk_mask_node_draw (GskRenderNode *node, { GskMaskNode *self = (GskMaskNode *) node; cairo_pattern_t *mask_pattern; + graphene_matrix_t color_matrix; + graphene_vec4_t color_offset; cairo_push_group (cr); gsk_render_node_draw (self->source, cr); @@ -5228,6 +5246,41 @@ gsk_mask_node_draw (GskRenderNode *node, gsk_render_node_draw (self->mask, cr); mask_pattern = cairo_pop_group (cr); + switch (self->mask_mode) + { + case GSK_MASK_MODE_ALPHA: + break; + case GSK_MASK_MODE_INVERTED_ALPHA: + graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, -1 }); + graphene_vec4_init (&color_offset, 0, 0, 0, 1); + apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, FALSE); + break; + case GSK_MASK_MODE_LUMINANCE: + graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0.2126, + 0, 1, 0, 0.7152, + 0, 0, 1, 0.0722, + 0, 0, 0, 0 }); + graphene_vec4_init (&color_offset, 0, 0, 0, 0); + apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE); + break; + case GSK_MASK_MODE_INVERTED_LUMINANCE: + graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, -0.2126, + 0, 1, 0, -0.7152, + 0, 0, 1, -0.0722, + 0, 0, 0, 0 }); + graphene_vec4_init (&color_offset, 0, 0, 0, 1); + apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE); + break; + default: + g_assert_not_reached (); + } + + gsk_cairo_rectangle (cr, &node->bounds); + cairo_clip (cr); + cairo_mask (cr, mask_pattern); } @@ -5245,11 +5298,15 @@ gsk_mask_node_diff (GskRenderNode *node1, /** * gsk_mask_node_new: - * @source: The bottom node to be drawn - * @mask: The node to be blended onto the @bottom node + * @source: The source node to be drawn + * @mask: The node to be used as mask + * @mask_mode: The mask mode to use * - * Creates a `GskRenderNode` that will use @blend_mode to blend the @top - * node onto the @bottom node. + * Creates a `GskRenderNode` that will mask a given node by another. + * + * The @mask_mode determines how the 'mask values' are derived from + * the colors of the @mask. Applying the mask consists of multiplying + * the 'mask value' with the alpha of the source. * * Returns: (transfer full) (type GskMaskNode): A new `GskRenderNode` * @@ -5257,7 +5314,8 @@ gsk_mask_node_diff (GskRenderNode *node1, */ GskRenderNode * gsk_mask_node_new (GskRenderNode *source, - GskRenderNode *mask) + GskRenderNode *mask, + GskMaskMode mask_mode) { GskMaskNode *self; @@ -5267,15 +5325,18 @@ gsk_mask_node_new (GskRenderNode *source, self = gsk_render_node_alloc (GSK_MASK_NODE); self->source = gsk_render_node_ref (source); self->mask = gsk_render_node_ref (mask); + self->mask_mode = mask_mode; - graphene_rect_union (&source->bounds, &mask->bounds, &self->render_node.bounds); + self->render_node.bounds = source->bounds; + + self->render_node.prefers_high_depth = gsk_render_node_prefers_high_depth (source); return &self->render_node; } /** * gsk_mask_node_get_source: - * @node: (type GskBlendNode): a mask `GskRenderNode` + * @node: (type GskMaskNode): a mask `GskRenderNode` * * Retrieves the source `GskRenderNode` child of the @node. * @@ -5295,7 +5356,7 @@ gsk_mask_node_get_source (const GskRenderNode *node) /** * gsk_mask_node_get_mask: - * @node: (type GskBlendNode): a mask `GskRenderNode` + * @node: (type GskMaskNode): a mask `GskRenderNode` * * Retrieves the mask `GskRenderNode` child of the @node. * @@ -5313,6 +5374,24 @@ gsk_mask_node_get_mask (const GskRenderNode *node) return self->mask; } +/** + * gsk_mask_node_get_mask_mode: + * @node: (type GskMaskNode): a blending `GskRenderNode` + * + * Retrieves the mask mode used by @node. + * + * Returns: the mask mode + * + * Since: 4.10 + */ +GskMaskMode +gsk_mask_node_get_mask_mode (const GskRenderNode *node) +{ + const GskMaskNode *self = (const GskMaskNode *) node; + + return self->mask_mode; +} + /* }}} */ /* {{{ GSK_DEBUG_NODE */ diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index 671ea34c02..fd68dd16a8 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -649,6 +649,18 @@ static const struct { GSK_BLEND_MODE_LUMINOSITY, "luminosity" } }; +static const char * +get_blend_mode_name (GskBlendMode mode) +{ + for (unsigned int i = 0; i < G_N_ELEMENTS (blend_modes); i++) + { + if (blend_modes[i].mode == mode) + return blend_modes[i].name; + } + + return NULL; +} + static gboolean parse_blend_mode (GtkCssParser *parser, gpointer out_mode) @@ -667,6 +679,46 @@ parse_blend_mode (GtkCssParser *parser, return FALSE; } +static const struct +{ + GskMaskMode mode; + const char *name; +} mask_modes[] = { + { GSK_MASK_MODE_ALPHA, "alpha" }, + { GSK_MASK_MODE_INVERTED_ALPHA, "inverted-alpha" }, + { GSK_MASK_MODE_LUMINANCE, "luminance" }, +}; + +static const char * +get_mask_mode_name (GskMaskMode mode) +{ + for (unsigned int i = 0; i < G_N_ELEMENTS (mask_modes); i++) + { + if (mask_modes[i].mode == mode) + return mask_modes[i].name; + } + + return NULL; +} + +static gboolean +parse_mask_mode (GtkCssParser *parser, + gpointer out_mode) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (mask_modes); i++) + { + if (gtk_css_parser_try_ident (parser, mask_modes[i].name)) + { + *(GskMaskMode *) out_mode = mask_modes[i].mode; + return TRUE; + } + } + + return FALSE; +} + static PangoFont * font_from_string (const char *string) { @@ -1381,7 +1433,9 @@ parse_mask_node (GtkCssParser *parser) { GskRenderNode *source = NULL; GskRenderNode *mask = NULL; + GskMaskMode mode = GSK_MASK_MODE_ALPHA; const Declaration declarations[] = { + { "mode", parse_mask_mode, NULL, &mode }, { "source", parse_node, clear_node, &source }, { "mask", parse_node, clear_node, &mask }, }; @@ -1393,7 +1447,7 @@ parse_mask_node (GtkCssParser *parser) if (mask == NULL) mask = gsk_color_node_new (&GDK_RGBA("AAFF00"), &GRAPHENE_RECT_INIT (0, 0, 50, 50)); - result = gsk_mask_node_new (source, mask); + result = gsk_mask_node_new (source, mask, mode); gsk_render_node_unref (source); gsk_render_node_unref (mask); @@ -3103,21 +3157,13 @@ render_node_print (Printer *p, case GSK_BLEND_NODE: { GskBlendMode mode = gsk_blend_node_get_blend_mode (node); - guint i; start_node (p, "blend"); if (mode != GSK_BLEND_MODE_DEFAULT) { _indent (p); - for (i = 0; i < G_N_ELEMENTS (blend_modes); i++) - { - if (blend_modes[i].mode == mode) - { - g_string_append_printf (p->str, "mode: %s;\n", blend_modes[i].name); - break; - } - } + g_string_append_printf (p->str, "mode: %s;\n", get_blend_mode_name (mode)); } append_node_param (p, "bottom", gsk_blend_node_get_bottom_child (node)); append_node_param (p, "top", gsk_blend_node_get_top_child (node)); @@ -3128,8 +3174,15 @@ render_node_print (Printer *p, case GSK_MASK_NODE: { + GskMaskMode mode = gsk_mask_node_get_mask_mode (node); + start_node (p, "mask"); + if (mode != GSK_MASK_MODE_ALPHA) + { + _indent (p); + g_string_append_printf (p->str, "mode: %s;\n", get_mask_mode_name (mode)); + } append_node_param (p, "source", gsk_mask_node_get_source (node)); append_node_param (p, "mask", gsk_mask_node_get_mask (node)); diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c index f64a3cc99f..1ebc641bea 100644 --- a/gtk/gtksnapshot.c +++ b/gtk/gtksnapshot.c @@ -122,6 +122,7 @@ struct _GtkSnapshotState { char *message; } debug; struct { + GskMaskMode mask_mode; GskRenderNode *mask_node; } mask; } data; @@ -1258,7 +1259,7 @@ gtk_snapshot_collect_mask_source (GtkSnapshot *snapshot, if (source_child == NULL || mask_child == NULL) return NULL; - mask_node = gsk_mask_node_new (source_child, mask_child); + mask_node = gsk_mask_node_new (source_child, mask_child, state->data.mask.mask_mode); gsk_render_node_unref (source_child); gsk_render_node_unref (mask_child); @@ -1290,9 +1291,11 @@ gtk_snapshot_collect_mask_mask (GtkSnapshot *snapshot, /** * gtk_snapshot_push_mask: * @snapshot: a #GtkSnapshot + * @mask_mode: mask mode to use * * Until the first call to [method@Gtk.Snapshot.pop], the * mask image for the mask operation will be recorded. + * * After that call, the source image will be recorded until * the second call to [method@Gtk.Snapshot.pop]. * @@ -1301,7 +1304,8 @@ gtk_snapshot_collect_mask_mask (GtkSnapshot *snapshot, * Since: 4.10 */ void -gtk_snapshot_push_mask (GtkSnapshot *snapshot) +gtk_snapshot_push_mask (GtkSnapshot *snapshot, + GskMaskMode mask_mode) { GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot); GtkSnapshotState *source_state; @@ -1311,6 +1315,8 @@ gtk_snapshot_push_mask (GtkSnapshot *snapshot) gtk_snapshot_collect_mask_source, gtk_snapshot_clear_mask_source); + source_state->data.mask.mask_mode = mask_mode; + gtk_snapshot_push_state (snapshot, source_state->transform, gtk_snapshot_collect_mask_mask, diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h index d1d5c1a757..f24e7955ce 100644 --- a/gtk/gtksnapshot.h +++ b/gtk/gtksnapshot.h @@ -96,7 +96,8 @@ GDK_AVAILABLE_IN_ALL void gtk_snapshot_push_blend (GtkSnapshot *snapshot, GskBlendMode blend_mode); GDK_AVAILABLE_IN_4_10 -void gtk_snapshot_push_mask (GtkSnapshot *snapshot); +void gtk_snapshot_push_mask (GtkSnapshot *snapshot, + GskMaskMode mask_mode); GDK_AVAILABLE_IN_ALL void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot, diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 9f75b1ab6e..78db4669cf 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -1140,6 +1140,12 @@ populate_render_node_properties (GListStore *store, break; case GSK_MASK_NODE: + { + GskMaskMode mode = gsk_mask_node_get_mask_mode (node); + tmp = g_enum_to_string (GSK_TYPE_MASK_MODE, mode); + add_text_row (store, "Mask mode", tmp); + g_free (tmp); + } break; case GSK_BLUR_NODE: From cb0aaf0b2c65f11c4f3f02e84837ef9c00857be9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 14 Feb 2023 19:04:34 -0500 Subject: [PATCH 2/4] Add more mask node tests --- testsuite/gsk/compare/empty-mask.node | 1 + testsuite/gsk/compare/empty-mask.png | Bin 0 -> 146 bytes testsuite/gsk/meson.build | 3 +++ testsuite/gsk/nodeparser/empty-mask.node | 1 + testsuite/gsk/nodeparser/empty-mask.ref.node | 10 ++++++++++ 5 files changed, 15 insertions(+) create mode 100644 testsuite/gsk/compare/empty-mask.node create mode 100644 testsuite/gsk/compare/empty-mask.png create mode 100644 testsuite/gsk/nodeparser/empty-mask.node create mode 100644 testsuite/gsk/nodeparser/empty-mask.ref.node diff --git a/testsuite/gsk/compare/empty-mask.node b/testsuite/gsk/compare/empty-mask.node new file mode 100644 index 0000000000..d7b7e4dd10 --- /dev/null +++ b/testsuite/gsk/compare/empty-mask.node @@ -0,0 +1 @@ +mask { } diff --git a/testsuite/gsk/compare/empty-mask.png b/testsuite/gsk/compare/empty-mask.png new file mode 100644 index 0000000000000000000000000000000000000000..eddaf9d38c61740a3e69380b70dc0ccec4c45108 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETNKY5Xkc`H+=NUN<7zi9Vu Date: Tue, 14 Feb 2023 18:36:43 -0500 Subject: [PATCH 3/4] Document mask node parser format --- demos/node-editor/node-format.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/demos/node-editor/node-format.md b/demos/node-editor/node-format.md index 557a8950a5..e769865887 100644 --- a/demos/node-editor/node-format.md +++ b/demos/node-editor/node-format.md @@ -178,6 +178,16 @@ Creates a node like `gsk_inset_shadow_node_new()` with the given properties. Creates a node like `gsk_linear_gradient_node_new()` with the given properties. +### mask + +| property | syntax | default | printed | +| -------- | ---------------- | ---------------------- | ----------- | +| source | `` | color { } | always | +| mode | `` | alpha | non-default | +| mask | `` | color { } | always | + +Creates a node like `gsk_mask_node_new()` with the given properties. + ### opacity | property | syntax | default | printed | From 662c251cd0fcefeca911bcf3a09df98791feab87 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 14 Feb 2023 20:16:15 +0100 Subject: [PATCH 4/4] demo: Spice up the mask demo Also use all the mask modes, weeeee! --- demos/gtk-demo/demo4widget.c | 111 +++++++++++++++++++++++++++++++++-- demos/gtk-demo/mask.c | 17 ++++-- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/demos/gtk-demo/demo4widget.c b/demos/gtk-demo/demo4widget.c index 0152581c3d..8bee75d8b7 100644 --- a/demos/gtk-demo/demo4widget.c +++ b/demos/gtk-demo/demo4widget.c @@ -2,12 +2,19 @@ #include "demo4widget.h" #include "hsla.h" +enum +{ + PROP_0, + PROP_PROGRESS, +}; + struct _Demo4Widget { GtkWidget parent_instance; PangoLayout *layout; GskColorStop stops[8]; gsize n_stops; + double progress; guint tick; }; @@ -49,6 +56,8 @@ demo4_widget_init (Demo4Widget *self) { PangoFontDescription *desc; + self->progress = 0.5; + self->n_stops = 8; self->stops[0].offset = 0; self->stops[0].color = (GdkRGBA) { 1, 0, 0, 1 }; @@ -83,16 +92,24 @@ demo4_widget_dispose (GObject *object) } static void -demo4_widget_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) +demo4_widget_snapshot_content (GtkWidget *widget, + GtkSnapshot *snapshot, + GskMaskMode mode) { Demo4Widget *self = DEMO4_WIDGET (widget); - int width, height; + int width, height, layout_width, layout_height; + double scale; width = gtk_widget_get_width (widget); height = gtk_widget_get_height (widget); - gtk_snapshot_push_mask (snapshot, GSK_MASK_MODE_INVERTED_ALPHA); + gtk_snapshot_push_mask (snapshot, mode); + pango_layout_get_pixel_size (self->layout, &layout_width, &layout_height); + scale = MIN ((double) width / layout_width, (double) height / layout_height); + gtk_snapshot_translate (snapshot, + &GRAPHENE_POINT_INIT ((width - scale * layout_width) / 2, + (height - scale * layout_height) / 2)); + gtk_snapshot_scale (snapshot, scale, scale); gtk_snapshot_append_layout (snapshot, self->layout, &(GdkRGBA) { 0, 0, 0, 1 }); gtk_snapshot_pop (snapshot); @@ -105,6 +122,84 @@ demo4_widget_snapshot (GtkWidget *widget, gtk_snapshot_pop (snapshot); } +static void +demo4_widget_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + Demo4Widget *self = DEMO4_WIDGET (widget); + int width, height; + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + gtk_snapshot_push_mask (snapshot, GSK_MASK_MODE_INVERTED_LUMINANCE); + gtk_snapshot_append_linear_gradient (snapshot, + &GRAPHENE_RECT_INIT (0, 0, width, height), + &GRAPHENE_POINT_INIT (0, 0), + &GRAPHENE_POINT_INIT (width, 0), + (GskColorStop[2]) { + { MAX (0.0, self->progress - 5.0 / width), { 1, 1, 1, 1 } }, + { MIN (1.0, self->progress + 5.0 / width), { 0, 0, 0, 1 } } + }, 2); + gtk_snapshot_pop (snapshot); + demo4_widget_snapshot_content (widget, snapshot, GSK_MASK_MODE_INVERTED_ALPHA); + gtk_snapshot_pop (snapshot); + + gtk_snapshot_push_mask (snapshot, GSK_MASK_MODE_LUMINANCE); + gtk_snapshot_append_linear_gradient (snapshot, + &GRAPHENE_RECT_INIT (0, 0, width, height), + &GRAPHENE_POINT_INIT (0, 0), + &GRAPHENE_POINT_INIT (width, 0), + (GskColorStop[2]) { + { MAX (0.0, self->progress - 5.0 / width), { 1, 1, 1, 1 } }, + { MIN (1.0, self->progress + 5.0 / width), { 0, 0, 0, 1 } } + }, 2); + gtk_snapshot_pop (snapshot); + demo4_widget_snapshot_content (widget, snapshot, GSK_MASK_MODE_ALPHA); + gtk_snapshot_pop (snapshot); +} + +static void +demo4_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + Demo4Widget *self = DEMO4_WIDGET (object); + + switch (prop_id) + { + case PROP_PROGRESS: + self->progress = g_value_get_double (value); + gtk_widget_queue_draw (GTK_WIDGET (object)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +demo4_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + Demo4Widget *self = DEMO4_WIDGET (object); + + switch (prop_id) + { + case PROP_PROGRESS: + g_value_set_double (value, self->progress); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void demo4_widget_class_init (Demo4WidgetClass *class) { @@ -112,8 +207,15 @@ demo4_widget_class_init (Demo4WidgetClass *class) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); object_class->dispose = demo4_widget_dispose; + object_class->get_property = demo4_widget_get_property; + object_class->set_property = demo4_widget_set_property; widget_class->snapshot = demo4_widget_snapshot; + + g_object_class_install_property (object_class, PROP_PROGRESS, + g_param_spec_double ("progress", NULL, NULL, + 0.0, 1.0, 0.5, + G_PARAM_READWRITE)); } GtkWidget * @@ -121,3 +223,4 @@ demo4_widget_new (void) { return g_object_new (DEMO4_TYPE_WIDGET, NULL); } + diff --git a/demos/gtk-demo/mask.c b/demos/gtk-demo/mask.c index 4266b44b52..ad846ab2d4 100644 --- a/demos/gtk-demo/mask.c +++ b/demos/gtk-demo/mask.c @@ -18,7 +18,8 @@ do_mask (GtkWidget *do_widget) if (!window) { GtkWidget *box; - GtkWidget *widget; + GtkWidget *demo; + GtkWidget *scale; window = gtk_window_new (); gtk_window_set_title (GTK_WINDOW (window), "Mask Nodes"); @@ -30,11 +31,17 @@ do_mask (GtkWidget *do_widget) box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_window_set_child (GTK_WINDOW (window), box); - widget = demo4_widget_new (); - gtk_widget_set_hexpand (widget, TRUE); - gtk_widget_set_vexpand (widget, TRUE); + demo = demo4_widget_new (); + gtk_widget_set_hexpand (demo, TRUE); + gtk_widget_set_vexpand (demo, TRUE); - gtk_box_append (GTK_BOX (box), widget); + gtk_box_append (GTK_BOX (box), demo); + + scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.1); + gtk_range_set_value (GTK_RANGE (scale), 0.5); + g_object_bind_property (gtk_range_get_adjustment (GTK_RANGE (scale)), "value", demo, "progress", 0); + + gtk_box_append (GTK_BOX (box), scale); } if (!gtk_widget_get_visible (window))