diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c index 6fee960edf..0408c25cff 100644 --- a/gsk/broadway/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -270,6 +270,7 @@ collect_reused_child_nodes (GskRenderer *renderer, case GSK_BLEND_NODE: case GSK_CROSS_FADE_NODE: case GSK_BLUR_NODE: + case GSK_MASK_NODE: default: @@ -846,6 +847,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, } break; /* Fallback */ + case GSK_MASK_NODE: case GSK_TEXTURE_SCALE_NODE: case GSK_TEXT_NODE: case GSK_RADIAL_GRADIENT_NODE: diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index b26a691cc5..2db40bd25f 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -3869,6 +3869,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job, gsk_gl_render_job_visit_as_fallback (job, node); break; + case GSK_MASK_NODE: + gsk_gl_render_job_visit_as_fallback (job, node); + break; + case GSK_OPACITY_NODE: gsk_gl_render_job_visit_opacity_node (job, node); break; diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 0c92555cfd..7b89dac642 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -46,6 +46,7 @@ * @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 that blends two children together + * @GSK_MASK_NODE: A node that masks one child with another * @GSK_CROSS_FADE_NODE: A node that cross-fades between two children * @GSK_TEXT_NODE: A node containing a glyph string * @GSK_BLUR_NODE: A node that applies a blur @@ -77,6 +78,7 @@ typedef enum { GSK_ROUNDED_CLIP_NODE, GSK_SHADOW_NODE, GSK_BLEND_NODE, + GSK_MASK_NODE, GSK_CROSS_FADE_NODE, GSK_TEXT_NODE, GSK_BLUR_NODE, diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index fb6e1f51f6..7147b0b848 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -164,6 +164,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes #define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type()) #define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type()) #define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type()) +#define GSK_TYPE_MASK_NODE (gsk_mask_node_get_type()) #define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type()) typedef struct _GskDebugNode GskDebugNode; @@ -191,6 +192,7 @@ typedef struct _GskBlendNode GskBlendNode; typedef struct _GskCrossFadeNode GskCrossFadeNode; typedef struct _GskTextNode GskTextNode; typedef struct _GskBlurNode GskBlurNode; +typedef struct _GskMaskNode GskMaskNode; typedef struct _GskGLShaderNode GskGLShaderNode; GDK_AVAILABLE_IN_ALL @@ -529,8 +531,18 @@ GskRenderNode * gsk_blur_node_get_child (const GskRender GDK_AVAILABLE_IN_ALL float gsk_blur_node_get_radius (const GskRenderNode *node) G_GNUC_PURE; +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); +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_ALL -GType gsk_gl_shader_node_get_type (void) G_GNUC_CONST; +GType gsk_gl_shader_node_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL GskRenderNode * gsk_gl_shader_node_new (GskGLShader *shader, const graphene_rect_t *bounds, diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 376d32cb4e..f9de3752d3 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -5184,6 +5184,135 @@ gsk_blur_node_get_radius (const GskRenderNode *node) return self->radius; } +/* }}} */ +/* {{{ GSK_MASK_NODE */ + +/** + * GskMaskNode: + * + * A render node masking one child node with another. + * + * Since: 4.10 + */ +typedef struct _GskMaskNode GskMaskNode; + +struct _GskMaskNode +{ + GskRenderNode render_node; + + GskRenderNode *mask; + GskRenderNode *source; +}; + +static void +gsk_mask_node_finalize (GskRenderNode *node) +{ + GskMaskNode *self = (GskMaskNode *) node; + + gsk_render_node_unref (self->source); + gsk_render_node_unref (self->mask); +} + +static void +gsk_mask_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskMaskNode *self = (GskMaskNode *) node; + cairo_pattern_t *mask_pattern; + + cairo_push_group (cr); + gsk_render_node_draw (self->source, cr); + cairo_pop_group_to_source (cr); + + cairo_push_group (cr); + gsk_render_node_draw (self->mask, cr); + mask_pattern = cairo_pop_group (cr); + + cairo_mask (cr, mask_pattern); +} + +static void +gsk_mask_node_diff (GskRenderNode *node1, + GskRenderNode *node2, + cairo_region_t *region) +{ + GskMaskNode *self1 = (GskMaskNode *) node1; + GskMaskNode *self2 = (GskMaskNode *) node2; + + gsk_render_node_diff (self1->source, self2->source, region); + gsk_render_node_diff (self1->mask, self2->mask, region); +} + +/** + * gsk_mask_node_new: + * @source: The bottom node to be drawn + * @mask: The node to be blended onto the @bottom node + * + * Creates a `GskRenderNode` that will use @blend_mode to blend the @top + * node onto the @bottom node. + * + * Returns: (transfer full) (type GskMaskNode): A new `GskRenderNode` + * + * Since: 4.10 + */ +GskRenderNode * +gsk_mask_node_new (GskRenderNode *source, + GskRenderNode *mask) +{ + GskMaskNode *self; + + g_return_val_if_fail (GSK_IS_RENDER_NODE (source), NULL); + g_return_val_if_fail (GSK_IS_RENDER_NODE (mask), NULL); + + self = gsk_render_node_alloc (GSK_MASK_NODE); + self->source = gsk_render_node_ref (source); + self->mask = gsk_render_node_ref (mask); + + graphene_rect_union (&source->bounds, &mask->bounds, &self->render_node.bounds); + + return &self->render_node; +} + +/** + * gsk_mask_node_get_source: + * @node: (type GskBlendNode): a mask `GskRenderNode` + * + * Retrieves the source `GskRenderNode` child of the @node. + * + * Returns: (transfer none): the source child node + * + * Since: 4.10 + */ +GskRenderNode * +gsk_mask_node_get_source (const GskRenderNode *node) +{ + const GskMaskNode *self = (const GskMaskNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL); + + return self->source; +} + +/** + * gsk_mask_node_get_mask: + * @node: (type GskBlendNode): a mask `GskRenderNode` + * + * Retrieves the mask `GskRenderNode` child of the @node. + * + * Returns: (transfer none): the mask child node + * + * Since: 4.10 + */ +GskRenderNode * +gsk_mask_node_get_mask (const GskRenderNode *node) +{ + const GskMaskNode *self = (const GskMaskNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL); + + return self->mask; +} + /* }}} */ /* {{{ GSK_DEBUG_NODE */ @@ -5556,6 +5685,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE) +GSK_DEFINE_RENDER_NODE_TYPE (gsk_mask_node, GSK_MASK_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE) @@ -5946,6 +6076,22 @@ gsk_render_node_init_types_once (void) gsk_render_node_types[GSK_BLUR_NODE] = node_type; } + { + const GskRenderNodeTypeInfo node_info = + { + GSK_MASK_NODE, + sizeof (GskMaskNode), + NULL, + gsk_mask_node_finalize, + gsk_mask_node_draw, + NULL, + gsk_mask_node_diff, + }; + + GType node_type = gsk_render_node_type_register_static (I_("GskMaskNode"), &node_info); + gsk_render_node_types[GSK_MASK_NODE] = node_type; + } + { const GskRenderNodeTypeInfo node_info = { diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index ee27eda5a7..671ea34c02 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -1376,6 +1376,31 @@ parse_glshader_node (GtkCssParser *parser) return node; } +static GskRenderNode * +parse_mask_node (GtkCssParser *parser) +{ + GskRenderNode *source = NULL; + GskRenderNode *mask = NULL; + const Declaration declarations[] = { + { "source", parse_node, clear_node, &source }, + { "mask", parse_node, clear_node, &mask }, + }; + GskRenderNode *result; + + parse_declarations (parser, declarations, G_N_ELEMENTS(declarations)); + if (source == NULL) + source = create_default_render_node (); + 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); + + gsk_render_node_unref (source); + gsk_render_node_unref (mask); + + return result; +} + static GskRenderNode * parse_border_node (GtkCssParser *parser) { @@ -1914,6 +1939,7 @@ parse_node (GtkCssParser *parser, { "texture-scale", parse_texture_scale_node }, { "transform", parse_transform_node }, { "glshader", parse_glshader_node }, + { "mask", parse_mask_node }, }; GskRenderNode **node_p = out_node; guint i; @@ -3100,6 +3126,17 @@ render_node_print (Printer *p, } break; + case GSK_MASK_NODE: + { + start_node (p, "mask"); + + append_node_param (p, "source", gsk_mask_node_get_source (node)); + append_node_param (p, "mask", gsk_mask_node_get_mask (node)); + + end_node (p); + } + break; + case GSK_NOT_A_RENDER_NODE: g_assert_not_reached (); break; diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index 3008c42154..38a2f806e5 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -504,6 +504,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, g_array_append_val (self->render_ops, op); return; + case GSK_MASK_NODE: + goto fallback; + case GSK_COLOR_MATRIX_NODE: if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX; diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 53528a26cb..9f75b1ab6e 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -305,6 +305,10 @@ create_list_model_for_render_node (GskRenderNode *node) return create_render_node_list_model ((GskRenderNode *[2]) { gsk_blend_node_get_bottom_child (node), gsk_blend_node_get_top_child (node) }, 2); + case GSK_MASK_NODE: + return create_render_node_list_model ((GskRenderNode *[2]) { gsk_mask_node_get_source (node), + gsk_mask_node_get_mask (node) }, 2); + case GSK_CROSS_FADE_NODE: return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node), gsk_cross_fade_node_get_end_child (node) }, 2); @@ -425,6 +429,8 @@ node_type_name (GskRenderNodeType type) return "Shadow"; case GSK_BLEND_NODE: return "Blend"; + case GSK_MASK_NODE: + return "Mask"; case GSK_CROSS_FADE_NODE: return "CrossFade"; case GSK_TEXT_NODE: @@ -462,6 +468,7 @@ node_name (GskRenderNode *node) case GSK_ROUNDED_CLIP_NODE: case GSK_SHADOW_NODE: case GSK_BLEND_NODE: + case GSK_MASK_NODE: case GSK_CROSS_FADE_NODE: case GSK_TEXT_NODE: case GSK_BLUR_NODE: @@ -1132,6 +1139,9 @@ populate_render_node_properties (GListStore *store, } break; + case GSK_MASK_NODE: + break; + case GSK_BLUR_NODE: add_float_row (store, "Radius", gsk_blur_node_get_radius (node)); break;