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);
#include "gdkdisplay-wayland.h"
#include "gdksurface-wayland.h"
+#include "gdksurface-wayland-private.h"
#include "gdkwaylanddisplay.h"
#include "gdkwaylandglcontext.h"
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);
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);
WL_SURFACE_OFFSET_SINCE_VERSION)
return;
+#if 0
if (impl->pending_buffer_offset_x == 0 &&
impl->pending_buffer_offset_y == 0)
return;
impl->pending_buffer_offset_y);
impl->pending_buffer_offset_x = 0;
impl->pending_buffer_offset_y = 0;
+#endif
}
void
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")),
}
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);
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.
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);
}
// 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));
}
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__ */
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;
}
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;
-
- 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);
+ float alpha, orig_alpha;
+ graphene_vec4_t pixel;
+ guint32* pixel_data;
- pattern = cairo_pop_group (cr);
cairo_pattern_get_surface (pattern, &surface);
image_surface = cairo_surface_map_to_image (surface, NULL);
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)
{
((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);
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);
GskRenderNode *mask;
GskRenderNode *source;
+ GskMaskMode mask_mode;
};
static void
{
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);
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);
}
/**
* 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`
*
*/
GskRenderNode *
gsk_mask_node_new (GskRenderNode *source,
- GskRenderNode *mask)
+ GskRenderNode *mask,
+ GskMaskMode mask_mode)
{
GskMaskNode *self;
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.
*
/**
* 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.
*
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 */
{ 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)
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)
{
{
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 },
};
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);
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));
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));
char *message;
} debug;
struct {
+ GskMaskMode mask_mode;
GskRenderNode *mask_node;
} mask;
} data;
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);
/**
* 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].
*
* 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;
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,
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,
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: