case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
+ case GSK_MASK_NODE:
default:
}
break; /* Fallback */
+ case GSK_MASK_NODE:
case GSK_TEXTURE_SCALE_NODE:
case GSK_TEXT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
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;
* @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
GSK_ROUNDED_CLIP_NODE,
GSK_SHADOW_NODE,
GSK_BLEND_NODE,
+ GSK_MASK_NODE,
GSK_CROSS_FADE_NODE,
GSK_TEXT_NODE,
GSK_BLUR_NODE,
#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;
typedef struct _GskCrossFadeNode GskCrossFadeNode;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
+typedef struct _GskMaskNode GskMaskNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
GDK_AVAILABLE_IN_ALL
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,
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 */
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)
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 =
{
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)
{
{ "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;
}
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;
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;
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);
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:
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:
}
break;
+ case GSK_MASK_NODE:
+ break;
+
case GSK_BLUR_NODE:
add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
break;