Compare commits

...

3 Commits

Author SHA1 Message Date
Matthias Clasen
51ad547995 gl: Add a shader for mask nodes 2020-12-14 15:32:58 -05:00
Matthias Clasen
df48be7c4c Introduce mask nodes
Add GskMaskNode, and support it in the render node
parser, in the inspector and in GtkSnapshot.

The rendering is just fallback for now.

Based on old work by Timm Bäder.
2020-12-14 14:31:56 -05:00
Matthias Clasen
f64e86ebcd Avoid adding // to resource paths
The GResource machinery is not forgiving about this
sort of thing.

Fixes: #3469
2020-12-14 08:00:19 -05:00
16 changed files with 328 additions and 6 deletions

View File

@@ -2716,6 +2716,48 @@ render_cross_fade_node (GskGLRenderer *self,
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static inline void
render_mask_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskRenderNode *source = gsk_mask_node_get_source (node);
GskRenderNode *mask = gsk_mask_node_get_mask (node);
TextureRegion source_region;
TextureRegion mask_region;
gboolean is_offscreen1, is_offscreen2;
OpMask *op;
if (!add_offscreen_ops (self, builder,
&node->bounds,
source,
&source_region, &is_offscreen1,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, source, builder);
return;
}
if (!add_offscreen_ops (self, builder,
&node->bounds,
mask,
&mask_region, &is_offscreen2,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, source, builder);
return;
}
ops_set_program (builder, &self->programs->mask_program);
op = ops_begin (builder, OP_CHANGE_MASK);
op->mask = mask_region.texture_id;
ops_set_texture (builder, source_region.texture_id);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static inline void
render_blend_node (GskGLRenderer *self,
GskRenderNode *node,
@@ -3203,6 +3245,17 @@ apply_repeat_op (const Program *program,
glUniform4fv (program->repeat.texture_rect_location, 1, op->texture_rect);
}
static inline void
apply_mask_op (const Program *program,
const OpMask *op)
{
OP_PRINT (" -> Mask ");
/* Mask texture id */
glUniform1i (program->mask.mask_location, 1);
glActiveTexture (GL_TEXTURE0 + 1);
glBindTexture (GL_TEXTURE_2D, op->mask);
}
static void
gsk_gl_renderer_dispose (GObject *gobject)
{
@@ -3326,6 +3379,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
{ "/org/gtk/libgsk/glsl/outset_shadow.glsl", "outset shadow" },
{ "/org/gtk/libgsk/glsl/repeat.glsl", "repeat" },
{ "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" },
{ "/org/gtk/libgsk/glsl/mask.glsl", "mask" },
};
gsk_gl_shader_builder_init (&shader_builder,
@@ -3421,6 +3475,9 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress);
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2);
/* mask */
INIT_PROGRAM_UNIFORM_LOCATION (mask, mask);
/* blend */
INIT_PROGRAM_UNIFORM_LOCATION (blend, source2);
INIT_PROGRAM_UNIFORM_LOCATION (blend, mode);
@@ -3805,6 +3862,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_gl_shader_node (self, node, builder);
break;
case GSK_MASK_NODE:
render_mask_node (self, node, builder);
break;
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
@@ -4110,6 +4171,11 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_blend_op (program, ptr);
break;
case OP_CHANGE_MASK:
g_assert (program == &self->programs->mask_program);
apply_mask_op (program, ptr);
break;
case OP_CHANGE_LINEAR_GRADIENT:
apply_linear_gradient_op (program, ptr);
break;

View File

@@ -13,7 +13,7 @@
#include "opbuffer.h"
#define GL_N_VERTICES 6
#define GL_N_PROGRAMS 15
#define GL_N_PROGRAMS 16
#define GL_MAX_GRADIENT_STOPS 6
typedef struct
@@ -179,6 +179,9 @@ struct _Program
int texture_locations[4];
GError *compile_error;
} glshader;
struct {
int mask_location;
} mask;
};
ProgramState state;
};
@@ -203,6 +206,7 @@ typedef struct {
Program outset_shadow_program;
Program repeat_program;
Program unblurred_outset_shadow_program;
Program mask_program;
};
};
GHashTable *custom_programs; /* GskGLShader -> Program* */

View File

@@ -34,6 +34,7 @@ static guint op_sizes[OP_LAST] = {
sizeof (OpGLShader),
sizeof (OpExtraTexture),
sizeof (OpConicGradient),
sizeof (OpMask),
};
void

View File

@@ -42,6 +42,7 @@ typedef enum
OP_CHANGE_GL_SHADER_ARGS = 28,
OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29,
OP_CHANGE_CONIC_GRADIENT = 30,
OP_CHANGE_MASK = 31,
OP_LAST
} OpKind;
@@ -222,6 +223,11 @@ typedef struct
const guchar *uniform_data;
} OpGLShader;
typedef struct
{
int mask;
} OpMask;
void op_buffer_init (OpBuffer *buffer);
void op_buffer_destroy (OpBuffer *buffer);
void op_buffer_clear (OpBuffer *buffer);

View File

@@ -79,7 +79,8 @@ typedef enum {
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE
GSK_GL_SHADER_NODE,
GSK_MASK_NODE,
} GskRenderNodeType;
/**

View File

@@ -164,6 +164,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type())
#define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type())
#define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type())
#define GSK_TYPE_MASK_NODE (gsk_mask_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@@ -190,6 +191,7 @@ typedef struct _GskCrossFadeNode GskCrossFadeNode;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
typedef struct _GskMaskNode GskMaskNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@@ -531,6 +533,16 @@ GBytes * gsk_gl_shader_node_get_args (GskRenderNode
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_node_get_shader (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_mask_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_new (GskRenderNode *source_child,
GskRenderNode *mask_child);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_get_source (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_get_mask (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_H__ */

View File

@@ -5175,6 +5175,83 @@ gsk_gl_shader_node_get_args (GskRenderNode *node)
return self->args;
}
/*** GSK_MASK_NODE ***/
typedef struct _GskMaskNode GskMaskNode;
struct _GskMaskNode
{
GskRenderNode render_node;
GskRenderNode *mask_child;
GskRenderNode *source_child;
};
static void
gsk_mask_node_finalize (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
gsk_render_node_unref (self->source_child);
gsk_render_node_unref (self->mask_child);
}
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_child, cr);
cairo_pop_group_to_source (cr);
cairo_push_group (cr);
gsk_render_node_draw (self->mask_child, cr);
mask_pattern = cairo_pop_group (cr);
cairo_mask (cr, mask_pattern);
}
GskRenderNode *
gsk_mask_node_new (GskRenderNode *source_child,
GskRenderNode *mask_child)
{
GskMaskNode *self;
g_return_val_if_fail (GSK_IS_RENDER_NODE (source_child), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (mask_child), NULL);
self = gsk_render_node_alloc (GSK_MASK_NODE);
self->source_child = gsk_render_node_ref (source_child);
self->mask_child = gsk_render_node_ref (mask_child);
graphene_rect_union (&source_child->bounds, &mask_child->bounds, &self->render_node.bounds);
return &self->render_node;
}
GskRenderNode *
gsk_mask_node_get_source (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
return self->source_child;
}
GskRenderNode *
gsk_mask_node_get_mask (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
return self->mask_child;
}
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
#ifndef I_
@@ -5214,6 +5291,7 @@ 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_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_mask_node, GSK_MASK_NODE)
static void
gsk_render_node_init_types_once (void)
@@ -5617,6 +5695,22 @@ gsk_render_node_init_types_once (void)
GType node_type = gsk_render_node_type_register_static (I_("GskDebugNode"), &node_info);
gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_MASK_NODE,
sizeof (GskMaskNode),
NULL,
gsk_mask_node_finalize,
gsk_mask_node_draw,
NULL,
NULL,
};
GType node_type = gsk_render_node_type_register_static (I_("GskMaskNode"), &node_info);
gsk_render_node_types[GSK_MASK_NODE] = node_type;
}
}
/*< private >
* gsk_render_node_init_types:

View File

@@ -1565,6 +1565,31 @@ parse_blend_node (GtkCssParser *parser)
return result;
}
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_repeat_node (GtkCssParser *parser)
{
@@ -1843,6 +1868,7 @@ parse_node (GtkCssParser *parser,
{ "texture", parse_texture_node },
{ "transform", parse_transform_node },
{ "glshader", parse_glshader_node },
{ "mask", parse_mask_node },
};
GskRenderNode **node_p = out_node;
guint i;
@@ -2911,6 +2937,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;

View File

@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GL_SHADER_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_MASK_NODE + 1)
extern GType gsk_render_node_types[];

View File

@@ -18,6 +18,7 @@ gsk_private_gl_shaders = [
'resources/glsl/blend.glsl',
'resources/glsl/repeat.glsl',
'resources/glsl/custom.glsl',
'resources/glsl/mask.glsl',
]
gsk_public_sources = files([

View File

@@ -0,0 +1,15 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform sampler2D u_mask;
void main() {
vec4 source = GskTexture(u_source, vUv);
vec4 mask = GskTexture(u_mask, vUv);
gskSetOutputColor(source * mask.a);
}

View File

@@ -260,6 +260,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_MASK_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));

View File

@@ -191,19 +191,23 @@ gtk_application_load_resources (GtkApplication *application)
{
GtkApplicationPrivate *priv = gtk_application_get_instance_private (application);
const char *base_path;
const char *optional_slash = "/";
base_path = g_application_get_resource_base_path (G_APPLICATION (application));
if (base_path == NULL)
return;
if (base_path[strlen (base_path) - 1] == '/')
optional_slash = "";
/* Expand the icon search path */
{
GtkIconTheme *default_theme;
char *iconspath;
default_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
iconspath = g_strconcat (base_path, "/icons/", NULL);
iconspath = g_strconcat (base_path, optional_slash, "icons/", NULL);
gtk_icon_theme_add_resource_path (default_theme, iconspath);
g_free (iconspath);
}
@@ -212,7 +216,7 @@ gtk_application_load_resources (GtkApplication *application)
{
char *menuspath;
menuspath = g_strconcat (base_path, "/gtk/menus.ui", NULL);
menuspath = g_strconcat (base_path, optional_slash, "gtk/menus.ui", NULL);
if (g_resources_get_info (menuspath, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL))
priv->menus_builder = gtk_builder_new_from_resource (menuspath);
g_free (menuspath);
@@ -231,7 +235,7 @@ gtk_application_load_resources (GtkApplication *application)
{
char *path;
path = g_strconcat (base_path, "/gtk/help-overlay.ui", NULL);
path = g_strconcat (base_path, optional_slash, "gtk/help-overlay.ui", NULL);
if (g_resources_get_info (path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL))
{
const char * const accels[] = { "<Control>question", NULL };

View File

@@ -124,6 +124,9 @@ struct _GtkSnapshotState {
struct {
char *message;
} debug;
struct {
GskRenderNode *source_node;
} mask;
} data;
};
@@ -1251,6 +1254,73 @@ gtk_snapshot_push_blend (GtkSnapshot *snapshot,
NULL);
}
static GskRenderNode *
gtk_snapshot_collect_mask_mask (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *source_child, *mask_child, *mask_node;
mask_child = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
source_child = state->data.mask.source_node;
if (source_child == NULL ||
mask_child == NULL)
return NULL;
mask_node = gsk_mask_node_new (source_child, mask_child);
gsk_render_node_unref (source_child);
gsk_render_node_unref (mask_child);
return mask_node;
}
static GskRenderNode *
gtk_snapshot_collect_mask_source (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GtkSnapshotState *prev_state = gtk_snapshot_get_previous_state (snapshot);
g_assert (prev_state->collect_func == gtk_snapshot_collect_mask_mask);
prev_state->data.mask.source_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
return NULL;
}
static void
gtk_snapshot_clear_mask_source (GtkSnapshotState *state)
{
g_clear_pointer (&(state->data.mask.source_node), gsk_render_node_unref);
}
/**
* gtk_snapshot_push_mask:
* @snapshot: a #GtkSnapshot
*
* Calling this function requires 2 subsequent calls to gtk_snapshot_pop().
**/
void
gtk_snapshot_push_mask (GtkSnapshot *snapshot)
{
GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
GtkSnapshotState *source_state;
source_state = gtk_snapshot_push_state (snapshot,
current_state->transform,
gtk_snapshot_collect_mask_source,
gtk_snapshot_clear_mask_source);
gtk_snapshot_push_state (snapshot,
source_state->transform,
gtk_snapshot_collect_mask_mask,
NULL);
}
static GskRenderNode *
gtk_snapshot_collect_cross_fade_end (GtkSnapshot *snapshot,
GtkSnapshotState *state,

View File

@@ -96,6 +96,8 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_blend (GtkSnapshot *snapshot,
GskBlendMode blend_mode);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_mask (GtkSnapshot *snapshot);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot,
double progress);
GDK_AVAILABLE_IN_ALL

View File

@@ -168,6 +168,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);
@@ -294,6 +298,8 @@ node_type_name (GskRenderNodeType type)
return "Blur";
case GSK_GL_SHADER_NODE:
return "GL Shader";
case GSK_MASK_NODE:
return "Mask";
}
}
@@ -323,6 +329,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:
@@ -1136,6 +1143,7 @@ populate_render_node_properties (GtkListStore *store,
break;
case GSK_NOT_A_RENDER_NODE:
case GSK_MASK_NODE:
default:
break;
}