diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c index 6a0b2a2aaf..8cbfa48418 100644 --- a/gsk/broadway/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -270,6 +270,7 @@ collect_reused_child_nodes (GskRenderer *renderer, case GSK_CROSS_FADE_NODE: case GSK_BLUR_NODE: case GSK_FILL_NODE: + case GSK_STROKE_NODE: default: @@ -857,6 +858,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, case GSK_BLUR_NODE: case GSK_GL_SHADER_NODE: case GSK_FILL_NODE: + case GSK_STROKE_NODE: default: break; /* Fallback */ } diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index ef4b61d32b..aa2c0cf26e 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -3768,6 +3768,7 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job, break; case GSK_FILL_NODE: + case GSK_STROKE_NODE: gsk_gl_render_job_visit_as_fallback (job, node); break; diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 0bd84b843d..bb7fd166c5 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -74,6 +74,7 @@ typedef enum { GSK_CLIP_NODE, GSK_ROUNDED_CLIP_NODE, GSK_FILL_NODE, + GSK_STROKE_NODE, GSK_SHADOW_NODE, GSK_BLEND_NODE, GSK_CROSS_FADE_NODE, diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index c80e9ec890..e5243e5143 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -159,6 +159,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes #define GSK_TYPE_CLIP_NODE (gsk_clip_node_get_type()) #define GSK_TYPE_ROUNDED_CLIP_NODE (gsk_rounded_clip_node_get_type()) #define GSK_TYPE_FILL_NODE (gsk_fill_node_get_type()) +#define GSK_TYPE_STROKE_NODE (gsk_stroke_node_get_type()) #define GSK_TYPE_SHADOW_NODE (gsk_shadow_node_get_type()) #define GSK_TYPE_BLEND_NODE (gsk_blend_node_get_type()) #define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type()) @@ -186,6 +187,7 @@ typedef struct _GskRepeatNode GskRepeatNode; typedef struct _GskClipNode GskClipNode; typedef struct _GskRoundedClipNode GskRoundedClipNode; typedef struct _GskFillNode GskFillNode; +typedef struct _GskStrokeNode GskStrokeNode; typedef struct _GskShadowNode GskShadowNode; typedef struct _GskBlendNode GskBlendNode; typedef struct _GskCrossFadeNode GskCrossFadeNode; @@ -460,6 +462,19 @@ GskPath * gsk_fill_node_get_path (const GskRender GDK_AVAILABLE_IN_ALL GskFillRule gsk_fill_node_get_fill_rule (const GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +GType gsk_stroke_node_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GskRenderNode * gsk_stroke_node_new (GskRenderNode *child, + GskPath *path, + const GskStroke *stroke); +GDK_AVAILABLE_IN_ALL +GskRenderNode * gsk_stroke_node_get_child (const GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +GskPath * gsk_stroke_node_get_path (const GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +const GskStroke * gsk_stroke_node_get_stroke (const GskRenderNode *node); + GDK_AVAILABLE_IN_ALL GType gsk_shadow_node_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 6e9322a8de..b3dd1348bf 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -26,6 +26,7 @@ #include "gskpath.h" #include "gskrendererprivate.h" #include "gskroundedrectprivate.h" +#include "gskstrokeprivate.h" #include "gsktransformprivate.h" #include "gdk/gdktextureprivate.h" @@ -3934,6 +3935,168 @@ gsk_fill_node_get_fill_rule (const GskRenderNode *node) return self->fill_rule; } +/*** GSK_STROKE_NODE ***/ + +struct _GskStrokeNode +{ + GskRenderNode render_node; + + GskRenderNode *child; + GskPath *path; + GskStroke stroke; +}; + +static void +gsk_stroke_node_finalize (GskRenderNode *node) +{ + GskStrokeNode *self = (GskStrokeNode *) node; + GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_STROKE_NODE)); + + gsk_render_node_unref (self->child); + gsk_path_unref (self->path); + gsk_stroke_clear (&self->stroke); + + parent_class->finalize (node); +} + +static void +gsk_stroke_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskStrokeNode *self = (GskStrokeNode *) node; + + cairo_save (cr); + + gsk_cairo_rectangle (cr, &self->child->bounds); + cairo_clip (cr); + + cairo_push_group (cr); + gsk_render_node_draw (self->child, cr); + cairo_pop_group_to_source (cr); + + gsk_stroke_to_cairo (&self->stroke, cr); + + gsk_path_to_cairo (self->path, cr); + cairo_stroke (cr); + + cairo_restore (cr); +} + +static void +gsk_stroke_node_diff (GskRenderNode *node1, + GskRenderNode *node2, + cairo_region_t *region) +{ + GskStrokeNode *self1 = (GskStrokeNode *) node1; + GskStrokeNode *self2 = (GskStrokeNode *) node2; + + if (self1->path == self2->path && + gsk_stroke_equal (&self1->stroke, &self2->stroke)) + { + cairo_region_t *sub; + + sub = cairo_region_create(); + gsk_render_node_diff (self1->child, self2->child, sub); + cairo_region_union (region, sub); + cairo_region_destroy (sub); + } + else + { + gsk_render_node_diff_impossible (node1, node2, region); + } +} + +/** + * gsk_stroke_node_new: + * @child: The node to stroke the area with + * @path: (transfer none): The path describing the area to stroke + * @stroke: (transfer none): The stroke attributes to use + * + * Creates a `GskRenderNode` that will stroke the @child along the given + * @path using the attributes defined in @stroke. + * + * Returns: (transfer none) (type GskStrokeNode): A new `GskRenderNode` + */ +GskRenderNode * +gsk_stroke_node_new (GskRenderNode *child, + GskPath *path, + const GskStroke *stroke) +{ + GskStrokeNode *self; + GskRenderNode *node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (stroke != NULL, NULL); + + self = gsk_render_node_alloc (GSK_STROKE_NODE); + node = (GskRenderNode *) self; + + self->child = gsk_render_node_ref (child); + self->path = gsk_path_ref (path); + gsk_stroke_init_copy (&self->stroke, stroke); + + /* XXX: Figure out a way to compute bounds from the path */ + graphene_rect_init_from_rect (&node->bounds, &child->bounds); + + return node; +} + +/** + * gsk_stroke_node_get_child: + * @node: (type GskStrokeNode): a stroke `GskRenderNode` + * + * Gets the child node that is getting drawn by the given @node. + * + * Returns: (transfer none): The child that is getting drawn + **/ +GskRenderNode * +gsk_stroke_node_get_child (const GskRenderNode *node) +{ + const GskStrokeNode *self = (const GskStrokeNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL); + + return self->child; +} + +/** + * gsk_stroke_node_get_path: + * @node: (type GskStrokeNode): a stroke `GskRenderNode` + * + * Retrievs the path that will be stroked with the contents of + * the @node. + * + * Returns: (transfer none): a `GskPath` + */ +GskPath * +gsk_stroke_node_get_path (const GskRenderNode *node) +{ + const GskStrokeNode *self = (const GskStrokeNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL); + + return self->path; +} + +/** + * gsk_stroke_node_get_stroke: + * @node: (type GskStrokeNode): a stroke `GskRenderNode` + * + * Retrievs the stroke attributes used in this @node. + * + * Returns: a `GskStroke` + */ +const GskStroke * +gsk_stroke_node_get_stroke (const GskRenderNode *node) +{ + const GskStrokeNode *self = (const GskStrokeNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL); + + return &self->stroke; +} + /*** GSK_SHADOW_NODE ***/ /** @@ -5457,6 +5620,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE) +GSK_DEFINE_RENDER_NODE_TYPE (gsk_stroke_node, GSK_STROKE_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE) @@ -5772,6 +5936,22 @@ gsk_render_node_init_types_once (void) gsk_render_node_types[GSK_FILL_NODE] = node_type; } + { + const GskRenderNodeTypeInfo node_info = + { + GSK_STROKE_NODE, + sizeof (GskStrokeNode), + NULL, + gsk_stroke_node_finalize, + gsk_stroke_node_draw, + NULL, + gsk_stroke_node_diff, + }; + + GType node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"), &node_info); + gsk_render_node_types[GSK_STROKE_NODE] = node_type; + } + { const GskRenderNodeTypeInfo node_info = { diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index a755e0e87a..a36f91c8b7 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -26,6 +26,7 @@ #include "gskpath.h" #include "gskroundedrectprivate.h" #include "gskrendernodeprivate.h" +#include "gskstroke.h" #include "gsktransformprivate.h" #include "gdk/gdkrgbaprivate.h" @@ -2101,7 +2102,7 @@ append_float_param (Printer *p, float value, float default_value) { - /* Don't approximate-compare here, better be topo verbose */ + /* Don't approximate-compare here, better be too verbose */ if (value == default_value) return; @@ -2559,6 +2560,25 @@ render_node_print (Printer *p, } break; + case GSK_STROKE_NODE: + { + const GskStroke *stroke; + char *path_str; + + start_node (p, "stroke"); + + append_node_param (p, "child", gsk_stroke_node_get_child (node)); + path_str = gsk_path_to_string (gsk_stroke_node_get_path (node)); + append_string_param (p, "path", path_str); + g_free (path_str); + + stroke = gsk_stroke_node_get_stroke (node); + append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0); + + end_node (p); + } + break; + case GSK_TRANSFORM_NODE: { GskTransform *transform = gsk_transform_node_get_transform (node); diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index c3d517e9f2..dc01be70d1 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -261,6 +261,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, case GSK_REPEATING_RADIAL_GRADIENT_NODE: case GSK_CONIC_GRADIENT_NODE: case GSK_FILL_NODE: + case GSK_STROKE_NODE: default: FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node)); diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 0f109b8fe3..fda54aada5 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -167,6 +167,9 @@ create_list_model_for_render_node (GskRenderNode *node) case GSK_FILL_NODE: return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1); + case GSK_STROKE_NODE: + return create_render_node_list_model ((GskRenderNode *[1]) { gsk_stroke_node_get_child (node) }, 1); + case GSK_SHADOW_NODE: return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1); @@ -290,6 +293,8 @@ node_type_name (GskRenderNodeType type) return "Rounded Clip"; case GSK_FILL_NODE: return "Fill"; + case GSK_STROKE_NODE: + return "Stroke"; case GSK_SHADOW_NODE: return "Shadow"; case GSK_BLEND_NODE: @@ -330,6 +335,7 @@ node_name (GskRenderNode *node) case GSK_CLIP_NODE: case GSK_ROUNDED_CLIP_NODE: case GSK_FILL_NODE: + case GSK_STROKE_NODE: case GSK_SHADOW_NODE: case GSK_BLEND_NODE: case GSK_CROSS_FADE_NODE: @@ -611,6 +617,20 @@ add_float_row (GtkListStore *store, g_free (text); } +static const char * +enum_to_nick (GType type, + int value) +{ + GEnumClass *class; + GEnumValue *v; + + class = g_type_class_ref (type); + v = g_enum_get_value (class, value); + g_type_class_unref (class); + + return v->value_nick; +} + static void populate_render_node_properties (GtkListStore *store, GskRenderNode *node) @@ -873,9 +893,7 @@ populate_render_node_properties (GtkListStore *store, case GSK_BLEND_NODE: { GskBlendMode mode = gsk_blend_node_get_blend_mode (node); - tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode); - add_text_row (store, "Blendmode", tmp); - g_free (tmp); + add_text_row (store, "Blendmode", enum_to_nick (GSK_TYPE_BLEND_MODE, mode)); } break; @@ -1111,10 +1129,33 @@ populate_render_node_properties (GtkListStore *store, case GSK_FILL_NODE: { GskPath *path = gsk_fill_node_get_path (node); + GskFillRule fill_rule = gsk_fill_node_get_fill_rule (node); tmp = gsk_path_to_string (path); add_text_row (store, "Path", tmp); g_free (tmp); + + add_text_row (store, "Fill rule", enum_to_nick (GSK_TYPE_FILL_RULE, fill_rule)); + } + break; + + case GSK_STROKE_NODE: + { + GskPath *path = gsk_stroke_node_get_path (node); + const GskStroke *stroke = gsk_stroke_node_get_stroke (node); + GskLineCap line_cap = gsk_stroke_get_line_cap (stroke); + GskLineJoin line_join = gsk_stroke_get_line_join (stroke); + + tmp = gsk_path_to_string (path); + add_text_row (store, "Path", tmp); + g_free (tmp); + + tmp = g_strdup_printf ("%.2f", gsk_stroke_get_line_width (stroke)); + add_text_row (store, "Line width", tmp); + g_free (tmp); + + add_text_row (store, "Line cap", enum_to_nick (GSK_TYPE_LINE_CAP, line_cap)); + add_text_row (store, "Line join", enum_to_nick (GSK_TYPE_LINE_JOIN, line_join)); } break;