[gtk/mask-nodes: 4/8] Render fill and stroke nodes using the mask shader
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/mask-nodes: 4/8] Render fill and stroke nodes using the mask shader
- Date: Wed, 16 Dec 2020 05:04:13 +0000 (UTC)
commit 9b18b5524f0c76842197646f95d95fdf6b39aac0
Author: Matthias Clasen <mclasen redhat com>
Date: Mon Dec 14 20:35:39 2020 -0500
Render fill and stroke nodes using the mask shader
Convert the path to an alpha mask and upload that
as a texture to use with the mask shader. Still
to do: cache the masks.
gsk/gl/gskglrenderer.c | 174 ++++++++++++++++++++++++++++++++++++++++-
gsk/gl/gskglrenderopsprivate.h | 1 +
2 files changed, 172 insertions(+), 3 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index a5a9b3191f..7e1e2b20c7 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -2736,6 +2736,169 @@ render_mask_node (GskGLRenderer *self,
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
+static GdkTexture *
+make_path_mask (GskPath *path,
+ GskFillRule fill_rule,
+ graphene_rect_t *bounds)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int width;
+ int height;
+ int stride;
+ guchar *buffer;
+ GBytes *bytes;
+ GdkTexture *mask;
+
+ gsk_path_get_bounds (path, bounds);
+
+ width = ceilf (bounds->size.width);
+ height = ceilf (bounds->size.height);
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+
+ buffer = g_malloc0 (stride * height);
+ surface = cairo_image_surface_create_for_data (buffer,
+ CAIRO_FORMAT_ARGB32,
+ width, height,
+ stride);
+
+ cr = cairo_create (surface);
+ cairo_translate (cr, 0, bounds->size.height);
+ cairo_scale (cr, 1, -1);
+ cairo_translate (cr, - bounds->origin.x, - bounds->origin.y);
+
+ switch (fill_rule)
+ {
+ case GSK_FILL_RULE_WINDING:
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+ break;
+ case GSK_FILL_RULE_EVEN_ODD:
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ gsk_path_to_cairo (path, cr);
+ cairo_clip (cr);
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ bytes = g_bytes_new_take (buffer, stride * height);
+ mask = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride);
+ g_bytes_unref (bytes);
+
+ cairo_surface_destroy (surface);
+
+ return mask;
+}
+
+static inline void
+render_fill_node (GskGLRenderer *self,
+ GskRenderNode *node,
+ RenderOpBuilder *builder)
+{
+ GskRenderNode *child = gsk_fill_node_get_child (node);
+ TextureRegion child_region;
+ gboolean is_offscreen1;
+ GdkTexture *mask;
+ graphene_rect_t mask_bounds;
+ int mask_texture_id;
+ OpMask *op;
+
+ if (!add_offscreen_ops (self, builder,
+ &node->bounds,
+ child,
+ &child_region, &is_offscreen1,
+ FORCE_OFFSCREEN | RESET_CLIP))
+ {
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+ return;
+ }
+
+ /* TODO: cache masks, and apply scale */
+ mask = make_path_mask (gsk_fill_node_get_path (node),
+ gsk_fill_node_get_fill_rule (node),
+ &mask_bounds);
+
+ mask_texture_id =
+ gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ mask,
+ GL_LINEAR,
+ GL_LINEAR);
+
+ g_object_unref (mask);
+
+ ops_set_program (builder, &self->programs->mask_program);
+
+ op = ops_begin (builder, OP_CHANGE_MASK);
+ op->mask = mask_texture_id;
+ op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width;
+ op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height;
+ op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) /
node->bounds.size.width;
+ op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) /
node->bounds.size.height;
+
+ ops_set_texture (builder, child_region.texture_id);
+
+ load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
+}
+
+static inline void
+render_stroke_node (GskGLRenderer *self,
+ GskRenderNode *node,
+ RenderOpBuilder *builder)
+{
+ GskRenderNode *child = gsk_stroke_node_get_child (node);
+ TextureRegion child_region;
+ gboolean is_offscreen1;
+ GskPath *path;
+ GdkTexture *mask;
+ int mask_texture_id;
+ graphene_rect_t mask_bounds;
+ OpMask *op;
+
+ if (!add_offscreen_ops (self, builder,
+ &node->bounds,
+ child,
+ &child_region, &is_offscreen1,
+ FORCE_OFFSCREEN | RESET_CLIP))
+ {
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+ return;
+ }
+
+ /* TODO: cache masks, and apply scale */
+ path = gsk_path_stroke (gsk_stroke_node_get_path (node),
+ (GskStroke *)gsk_stroke_node_get_stroke (node));
+ mask = make_path_mask (path, GSK_FILL_RULE_WINDING, &mask_bounds);
+
+ mask_texture_id =
+ gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ mask,
+ GL_LINEAR,
+ GL_LINEAR);
+
+ g_object_unref (mask);
+ gsk_path_unref (path);
+
+ ops_set_program (builder, &self->programs->mask_program);
+
+ op = ops_begin (builder, OP_CHANGE_MASK);
+ op->mask = mask_texture_id;
+ op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width;
+ op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height;
+ op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) /
node->bounds.size.width;
+ op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) /
node->bounds.size.height;
+
+ ops_set_texture (builder, child_region.texture_id);
+
+ load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
+}
+
static inline void
render_blend_node (GskGLRenderer *self,
GskRenderNode *node,
@@ -3465,7 +3628,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds);
INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect);
-
/* We initialize the alpha uniform here, since the default value is important.
* We can't do it in the shader like a reasonable person would because that doesn't
* work in gles. */
@@ -3845,10 +4007,16 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_mask_node (self, node, builder);
break;
- case GSK_REPEATING_LINEAR_GRADIENT_NODE:
- case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_FILL_NODE:
+ render_fill_node (self, node, builder);
+ break;
+
case GSK_STROKE_NODE:
+ render_stroke_node (self, node, builder);
+ break;
+
+ case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+ case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
default:
{
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 6cd4d57795..33fce85948 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -181,6 +181,7 @@ struct _Program
} glshader;
struct {
int mask_location;
+ int child_rect_location;
int texture_rect_location;
} mask;
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]