diff --git a/tools/gtk-path-tool-render.c b/tools/gtk-path-tool-render.c index 7146f591d8..11a93b2980 100644 --- a/tools/gtk-path-tool-render.c +++ b/tools/gtk-path-tool-render.c @@ -147,41 +147,9 @@ do_render (int *argc, stroke = gsk_stroke_new (line_width); gsk_stroke_set_line_cap (stroke, line_cap); gsk_stroke_set_line_join (stroke, line_join); - gsk_stroke_set_miter_limit (stroke, miter_limit); - - if (dashes != NULL) - { - GArray *d = g_array_new (FALSE, FALSE, sizeof (float)); - char **strings; - - strings = g_strsplit (dashes, ",", 0); - - for (unsigned int i = 0; strings[i]; i++) - { - char *end = NULL; - float f; - - f = (float) g_ascii_strtod (strings[i], &end); - - if (*end != '\0') - { - char *msg = g_strdup_printf (_("Failed to parse '%s' as number"), strings[i]); - g_printerr ("%s\n", msg); - exit (1); - } - - g_array_append_val (d, f); - } - - g_strfreev (strings); - - gsk_stroke_set_dash (stroke, (const float *)d->data, d->len); - - g_array_unref (d); - } - gsk_stroke_set_dash_offset (stroke, dash_offset); + _gsk_stroke_set_dashes (stroke, dashes); if (do_stroke) gsk_path_get_stroke_bounds (path, stroke, &bounds); diff --git a/tools/gtk-path-tool-show.c b/tools/gtk-path-tool-show.c index c1eeab5f76..cde7e3ce07 100644 --- a/tools/gtk-path-tool-show.c +++ b/tools/gtk-path-tool-show.c @@ -35,7 +35,9 @@ static void show_path_fill (GskPath *path, GskFillRule fill_rule, const GdkRGBA *fg_color, - const GdkRGBA *bg_color) + const GdkRGBA *bg_color, + gboolean show_points, + gboolean show_controls) { GtkWidget *window, *sw, *child; @@ -55,7 +57,10 @@ show_path_fill (GskPath *path, "fill-rule", fill_rule, "fg-color", fg_color, "bg-color", bg_color, + "show-points", show_points, + "show-controls", show_controls, NULL); + gtk_widget_set_hexpand (child, TRUE); gtk_widget_set_vexpand (child, TRUE); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child); @@ -70,7 +75,9 @@ static void show_path_stroke (GskPath *path, GskStroke *stroke, const GdkRGBA *fg_color, - const GdkRGBA *bg_color) + const GdkRGBA *bg_color, + gboolean show_points, + gboolean show_controls) { GtkWidget *window, *sw, *child; @@ -90,7 +97,10 @@ show_path_stroke (GskPath *path, "stroke", stroke, "fg-color", fg_color, "bg-color", bg_color, + "show-points", show_points, + "show-controls", show_controls, NULL); + gtk_widget_set_hexpand (child, TRUE); gtk_widget_set_vexpand (child, TRUE); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child); @@ -107,6 +117,8 @@ do_show (int *argc, { GError *error = NULL; gboolean do_stroke = FALSE; + gboolean show_points = FALSE; + gboolean show_controls = FALSE; const char *fill = "winding"; const char *fg_color = "black"; const char *bg_color = "white"; @@ -122,6 +134,8 @@ do_show (int *argc, const GOptionEntry entries[] = { { "fill", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &do_stroke, N_("Fill the path (the default)"), NULL }, { "stroke", 0, 0, G_OPTION_ARG_NONE, &do_stroke, N_("Stroke the path"), NULL }, + { "points", 0, 0, G_OPTION_ARG_NONE, &show_points, N_("Show points"), NULL }, + { "controls", 0, 0, G_OPTION_ARG_NONE, &show_controls, N_("Show controls"), NULL }, { "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") }, { "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH") }, @@ -209,46 +223,14 @@ do_show (int *argc, stroke = gsk_stroke_new (line_width); gsk_stroke_set_line_cap (stroke, line_cap); gsk_stroke_set_line_join (stroke, line_join); - gsk_stroke_set_miter_limit (stroke, miter_limit); - - if (dashes != NULL) - { - GArray *d = g_array_new (FALSE, FALSE, sizeof (float)); - char **strings; - - strings = g_strsplit (dashes, ",", 0); - - for (unsigned int i = 0; strings[i]; i++) - { - char *end = NULL; - float f; - - f = (float) g_ascii_strtod (strings[i], &end); - - if (*end != '\0') - { - char *msg = g_strdup_printf (_("Failed to parse '%s' as number"), strings[i]); - g_printerr ("%s\n", msg); - exit (1); - } - - g_array_append_val (d, f); - } - - g_strfreev (strings); - - gsk_stroke_set_dash (stroke, (const float *)d->data, d->len); - - g_array_unref (d); - } - gsk_stroke_set_dash_offset (stroke, dash_offset); + _gsk_stroke_set_dashes (stroke, dashes); if (do_stroke) - show_path_stroke (path, stroke, &fg, &bg); + show_path_stroke (path, stroke, &fg, &bg, show_points, show_controls); else - show_path_fill (path, fill_rule, &fg, &bg); + show_path_fill (path, fill_rule, &fg, &bg, show_points, show_controls); gsk_path_unref (path); diff --git a/tools/gtk-path-tool-utils.c b/tools/gtk-path-tool-utils.c index 76888cadd6..ca5a95f145 100644 --- a/tools/gtk-path-tool-utils.c +++ b/tools/gtk-path-tool-utils.c @@ -137,3 +137,40 @@ get_color (GdkRGBA *rgba, exit (1); } } + +void +_gsk_stroke_set_dashes (GskStroke *stroke, + const char *dashes) +{ + GArray *d; + char **strings; + + if (!dashes) + return; + + d = g_array_new (FALSE, FALSE, sizeof (float)); + strings = g_strsplit (dashes, ",", 0); + + for (unsigned int i = 0; strings[i]; i++) + { + char *end = NULL; + float f; + + f = (float) g_ascii_strtod (strings[i], &end); + + if (*end != '\0') + { + char *msg = g_strdup_printf (_("Failed to parse '%s' as number"), strings[i]); + g_printerr ("%s\n", msg); + exit (1); + } + + g_array_append_val (d, f); + } + + g_strfreev (strings); + + gsk_stroke_set_dash (stroke, (const float *)d->data, d->len); + + g_array_unref (d); +} diff --git a/tools/gtk-path-tool.h b/tools/gtk-path-tool.h index 505d5c2bd3..aa4236b86c 100644 --- a/tools/gtk-path-tool.h +++ b/tools/gtk-path-tool.h @@ -13,3 +13,6 @@ int get_enum_value (GType type, const char *str); void get_color (GdkRGBA *rgba, const char *str); + +void _gsk_stroke_set_dashes (GskStroke *stroke, + const char *dashes); diff --git a/tools/path-view.c b/tools/path-view.c index 8e6f35477e..7d4c4991cd 100644 --- a/tools/path-view.c +++ b/tools/path-view.c @@ -33,6 +33,10 @@ struct _PathView GdkRGBA bg; int padding; gboolean do_fill; + gboolean show_points; + gboolean show_controls; + GskPath *line_path; + GskPath *point_path; }; enum { @@ -42,6 +46,8 @@ enum { PROP_FILL_RULE, PROP_FG_COLOR, PROP_BG_COLOR, + PROP_SHOW_POINTS, + PROP_SHOW_CONTROLS, N_PROPERTIES }; @@ -72,6 +78,8 @@ path_view_dispose (GObject *object) g_clear_pointer (&self->path, gsk_path_unref); g_clear_pointer (&self->stroke, gsk_stroke_free); + g_clear_pointer (&self->line_path, gsk_path_unref); + g_clear_pointer (&self->point_path, gsk_path_unref); G_OBJECT_CLASS (path_view_parent_class)->dispose (object); } @@ -110,6 +118,14 @@ path_view_get_property (GObject *object, g_value_set_boxed (value, &self->bg); break; + case PROP_SHOW_POINTS: + g_value_set_boolean (value, self->show_points); + break; + + case PROP_SHOW_CONTROLS: + g_value_set_boolean (value, self->show_controls); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -123,6 +139,124 @@ update_bounds (PathView *self) gsk_path_get_bounds (self->path, &self->bounds); else gsk_path_get_stroke_bounds (self->path, self->stroke, &self->bounds); + + if (self->line_path) + { + graphene_rect_t bounds; + + gsk_path_get_stroke_bounds (self->line_path, self->stroke, &bounds); + graphene_rect_union (&bounds, &self->bounds, &self->bounds); + } + + if (self->point_path) + { + graphene_rect_t bounds; + + gsk_path_get_stroke_bounds (self->point_path, self->stroke, &bounds); + graphene_rect_union (&bounds, &self->bounds, &self->bounds); + } +} + +typedef struct +{ + PathView *self; + GskPathBuilder *line_builder; + GskPathBuilder *point_builder; +} ControlData; + +static gboolean +collect_cb (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer data) +{ + ControlData *cd = data; + + switch (op) + { + case GSK_PATH_MOVE: + if (cd->point_builder) + gsk_path_builder_add_circle (cd->point_builder, &pts[0], 4); + if (cd->line_builder) + gsk_path_builder_move_to (cd->line_builder, pts[0].x, pts[0].y); + break; + + case GSK_PATH_LINE: + case GSK_PATH_CLOSE: + if (cd->point_builder) + gsk_path_builder_add_circle (cd->point_builder, &pts[1], 4); + if (cd->line_builder) + gsk_path_builder_line_to (cd->line_builder, pts[1].x, pts[1].y); + break; + + case GSK_PATH_QUAD: + case GSK_PATH_CONIC: + if (cd->point_builder) + { + if (cd->self->show_controls) + gsk_path_builder_add_circle (cd->point_builder, &pts[1], 3); + gsk_path_builder_add_circle (cd->point_builder, &pts[2], 4); + } + if (cd->line_builder) + { + gsk_path_builder_line_to (cd->line_builder, pts[1].x, pts[1].y); + gsk_path_builder_line_to (cd->line_builder, pts[2].x, pts[2].y); + } + break; + + case GSK_PATH_CUBIC: + if (cd->point_builder) + { + if (cd->self->show_controls) + { + gsk_path_builder_add_circle (cd->point_builder, &pts[1], 3); + gsk_path_builder_add_circle (cd->point_builder, &pts[2], 3); + } + gsk_path_builder_add_circle (cd->point_builder, &pts[3], 4); + } + if (cd->line_builder) + { + gsk_path_builder_line_to (cd->line_builder, pts[1].x, pts[1].y); + gsk_path_builder_line_to (cd->line_builder, pts[2].x, pts[2].y); + gsk_path_builder_line_to (cd->line_builder, pts[3].x, pts[3].y); + } + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static void +update_controls (PathView *self) +{ + ControlData data = { 0, }; + + data.self = self; + + g_clear_pointer (&self->line_path, gsk_path_unref); + g_clear_pointer (&self->point_path, gsk_path_unref); + + if (self->path && self->show_controls) + data.line_builder = gsk_path_builder_new (); + + if (self->path && (self->show_points || self->show_controls)) + data.point_builder = gsk_path_builder_new (); + + if (data.line_builder || data.point_builder) + { + gsk_path_foreach (self->path, -1, collect_cb, &data); + + if (data.line_builder) + self->line_path = gsk_path_builder_free_to_path (data.line_builder); + if (data.point_builder) + self->point_path = gsk_path_builder_free_to_path (data.point_builder); + } + + update_bounds (self); } static void @@ -139,6 +273,7 @@ path_view_set_property (GObject *object, case PROP_PATH: g_clear_pointer (&self->path, gsk_path_unref); self->path = g_value_dup_boxed (value); + update_controls (self); update_bounds (self); gtk_widget_queue_resize (GTK_WIDGET (self)); break; @@ -171,6 +306,18 @@ path_view_set_property (GObject *object, gtk_widget_queue_draw (GTK_WIDGET (self)); break; + case PROP_SHOW_POINTS: + self->show_points = g_value_get_boolean (value); + update_controls (self); + gtk_widget_queue_draw (GTK_WIDGET (self)); + break; + + case PROP_SHOW_CONTROLS: + self->show_controls = g_value_get_boolean (value); + update_controls (self); + gtk_widget_queue_draw (GTK_WIDGET (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -204,14 +351,33 @@ path_view_snapshot (GtkWidget *widget, graphene_rect_inset (&bounds, - self->padding, - self->padding); gtk_snapshot_save (snapshot); + gtk_snapshot_append_color (snapshot, &self->bg, &self->bounds); gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (self->padding, self->padding)); + if (self->do_fill) - gtk_snapshot_push_fill (snapshot, self->path, self->fill_rule); + gtk_snapshot_append_fill (snapshot, self->path, self->fill_rule, &self->fg); else - gtk_snapshot_push_stroke (snapshot, self->path, self->stroke); - gtk_snapshot_append_color (snapshot, &self->fg, &self->bounds); - gtk_snapshot_pop (snapshot); + gtk_snapshot_append_stroke (snapshot, self->path, self->stroke, &self->fg); + + if (self->line_path) + { + GskStroke *stroke = gsk_stroke_new (1); + GdkRGBA gray = (GdkRGBA) { 0, 0, 0, 0.5 }; + + gtk_snapshot_append_stroke (snapshot, self->line_path, stroke, &gray); + } + + if (self->point_path) + { + GskStroke *stroke = gsk_stroke_new (1); + GdkRGBA purple = (GdkRGBA) { 1, 0, 1, 1 }; + GdkRGBA black = (GdkRGBA) { 0, 0, 0, 1 }; + + gtk_snapshot_append_fill (snapshot, self->point_path, GSK_FILL_RULE_WINDING, &purple); + gtk_snapshot_append_stroke (snapshot, self->point_path, stroke, &black); + } + gtk_snapshot_restore (snapshot); } @@ -259,6 +425,16 @@ path_view_class_init (PathViewClass *class) GDK_TYPE_RGBA, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + properties[PROP_SHOW_POINTS] + = g_param_spec_boolean ("show-points", NULL, NULL, + FALSE, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_SHOW_CONTROLS] + = g_param_spec_boolean ("show-controls", NULL, NULL, + FALSE, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_properties (object_class, N_PROPERTIES, properties); }