Make mask nodes more versatile

Add a GskMaskMode enumeration and implement it
in the GL and cairo renderers.
This commit is contained in:
Matthias Clasen 2023-02-14 13:44:39 -05:00
parent f1311dc053
commit 0eb791eaaa
13 changed files with 244 additions and 49 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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")),

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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__ */

View File

@ -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;

View File

@ -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 */

View File

@ -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));

View File

@ -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,

View File

@ -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,

View File

@ -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: