From 985bd35221140423151a431b406e25666a307a60 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 20 Jun 2023 15:35:25 -0700 Subject: [PATCH] libsysprof-gtk: add line layer which can line_to and curve_to --- src/libsysprof-gtk/meson.build | 2 + src/libsysprof-gtk/sysprof-gtk.h | 2 + src/libsysprof-gtk/sysprof-line-layer.c | 304 ++++++++++++++++++++++++ src/libsysprof-gtk/sysprof-line-layer.h | 52 ++++ src/libsysprof-gtk/tests/test-charts.c | 13 + 5 files changed, 373 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-line-layer.c create mode 100644 src/libsysprof-gtk/sysprof-line-layer.h diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 338222e9..02a1031f 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -3,6 +3,7 @@ libsysprof_gtk_public_sources = [ 'sysprof-chart.c', 'sysprof-chart-layer.c', 'sysprof-column-layer.c', + 'sysprof-line-layer.c', 'sysprof-mark-chart.c', 'sysprof-mark-table.c', 'sysprof-session.c', @@ -16,6 +17,7 @@ libsysprof_gtk_public_headers = [ 'sysprof-chart.h', 'sysprof-chart-layer.h', 'sysprof-column-layer.h', + 'sysprof-line-layer.h', 'sysprof-mark-chart.h', 'sysprof-mark-table.h', 'sysprof-session.h', diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h index 98e7e29b..4f0e06de 100644 --- a/src/libsysprof-gtk/sysprof-gtk.h +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS # include "sysprof-chart.h" # include "sysprof-chart-layer.h" # include "sysprof-column-layer.h" +# include "sysprof-line-layer.h" # include "sysprof-mark-chart.h" # include "sysprof-mark-table.h" # include "sysprof-session.h" @@ -34,3 +35,4 @@ G_BEGIN_DECLS #undef SYSPROF_GTK_INSIDE G_END_DECLS + diff --git a/src/libsysprof-gtk/sysprof-line-layer.c b/src/libsysprof-gtk/sysprof-line-layer.c new file mode 100644 index 00000000..c000cbbf --- /dev/null +++ b/src/libsysprof-gtk/sysprof-line-layer.c @@ -0,0 +1,304 @@ +/* + * sysprof-line-layer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-line-layer.h" + +struct _SysprofLineLayer +{ + SysprofChartLayer parent_instance; + SysprofXYSeries *series; + GdkRGBA color; + guint use_curves : 1; +}; + +G_DEFINE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF_TYPE_CHART_LAYER) + +enum { + PROP_0, + PROP_COLOR, + PROP_SERIES, + PROP_USE_CURVES, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +SysprofChartLayer * +sysprof_line_layer_new (void) +{ + return g_object_new (SYSPROF_TYPE_LINE_LAYER, NULL); +} + +static void +sysprof_line_layer_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + SysprofLineLayer *self = (SysprofLineLayer *)widget; + const SysprofXYSeriesValue *values; + cairo_t *cr; + double last_x; + double last_y; + guint n_values; + int width; + int height; + + g_assert (SYSPROF_IS_LINE_LAYER (self)); + g_assert (GTK_IS_SNAPSHOT (snapshot)); + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + if (width == 0 || height == 0 || self->series == NULL || self->color.alpha == 0) + return; + + if (!(values = sysprof_xy_series_get_values (self->series, &n_values))) + return; + + cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height)); + + gdk_cairo_set_source_rgba (cr, &self->color); + cairo_set_line_width (cr, 1./width); + cairo_set_matrix (cr, &(cairo_matrix_t) {1, 0, 0, -1, 0, height}); + cairo_scale (cr, width, height); + + last_x = values->x; + last_y = values->y; + + cairo_move_to (cr, last_x, last_y); + + if (self->use_curves) + { + for (guint i = 1; i < n_values; i++) + { + const SysprofXYSeriesValue *v = &values[i]; + double x = v->x; + double y = v->y; + + cairo_curve_to (cr, + last_x + ((x - last_x)/2), + last_y, + last_x + ((x - last_x)/2), + y, + x, + y); + + last_x = x; + last_y = y; + } + } + else + { + for (guint i = 1; i < n_values; i++) + { + const SysprofXYSeriesValue *v = &values[i]; + + cairo_line_to (cr, v->x, v->y); + } + } + + cairo_stroke (cr); + cairo_destroy (cr); +} + +static void +sysprof_line_layer_finalize (GObject *object) +{ + SysprofLineLayer *self = (SysprofLineLayer *)object; + + g_clear_pointer (&self->series, sysprof_xy_series_unref); + + G_OBJECT_CLASS (sysprof_line_layer_parent_class)->finalize (object); +} + +static void +sysprof_line_layer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofLineLayer *self = SYSPROF_LINE_LAYER (object); + + switch (prop_id) + { + case PROP_COLOR: + g_value_set_boxed (value, sysprof_line_layer_get_color (self)); + break; + + case PROP_SERIES: + g_value_set_boxed (value, sysprof_line_layer_get_series (self)); + break; + + case PROP_USE_CURVES: + g_value_set_boolean (value, sysprof_line_layer_get_use_curves (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_line_layer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofLineLayer *self = SYSPROF_LINE_LAYER (object); + + switch (prop_id) + { + case PROP_COLOR: + sysprof_line_layer_set_color (self, g_value_get_boxed (value)); + break; + + case PROP_SERIES: + sysprof_line_layer_set_series (self, g_value_get_boxed (value)); + break; + + case PROP_USE_CURVES: + sysprof_line_layer_set_use_curves (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_line_layer_class_init (SysprofLineLayerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sysprof_line_layer_finalize; + object_class->get_property = sysprof_line_layer_get_property; + object_class->set_property = sysprof_line_layer_set_property; + + widget_class->snapshot = sysprof_line_layer_snapshot; + + properties[PROP_COLOR] = + g_param_spec_boxed ("color", NULL, NULL, + GDK_TYPE_RGBA, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SERIES] = + g_param_spec_boxed ("series", NULL, NULL, + SYSPROF_TYPE_XY_SERIES, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_USE_CURVES] = + g_param_spec_boolean ("use-curves", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_line_layer_init (SysprofLineLayer *self) +{ + self->color.alpha = 1; +} + +const GdkRGBA * +sysprof_line_layer_get_color (SysprofLineLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_LINE_LAYER (self), NULL); + + return &self->color; +} + +void +sysprof_line_layer_set_color (SysprofLineLayer *self, + const GdkRGBA *color) +{ + static const GdkRGBA black = {0,0,0,1}; + + g_return_if_fail (SYSPROF_IS_LINE_LAYER (self)); + + if (color == NULL) + color = &black; + + if (!gdk_rgba_equal (&self->color, color)) + { + self->color = *color; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]); + gtk_widget_queue_draw (GTK_WIDGET (self)); + } +} + +/** + * sysprof_line_layer_get_series: + * @self: a #SysprofLineLayer + * + * + * Returns: (transfer none) (nullable): a #SysprofXYSeries or %NULL + */ +SysprofXYSeries * +sysprof_line_layer_get_series (SysprofLineLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_LINE_LAYER (self), NULL); + + return self->series; +} + +void +sysprof_line_layer_set_series (SysprofLineLayer *self, + SysprofXYSeries *series) +{ + g_return_if_fail (SYSPROF_IS_LINE_LAYER (self)); + + if (series == self->series) + return; + + g_clear_pointer (&self->series, sysprof_xy_series_unref); + self->series = series ? sysprof_xy_series_ref (series) : NULL; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SERIES]); + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +gboolean +sysprof_line_layer_get_use_curves (SysprofLineLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_LINE_LAYER (self), FALSE); + + return self->use_curves; +} + +void +sysprof_line_layer_set_use_curves (SysprofLineLayer *self, + gboolean use_curves) +{ + g_return_if_fail (SYSPROF_IS_LINE_LAYER (self)); + + use_curves = !!use_curves; + + if (use_curves != self->use_curves) + { + self->use_curves = use_curves; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_CURVES]); + gtk_widget_queue_draw (GTK_WIDGET (self)); + } +} diff --git a/src/libsysprof-gtk/sysprof-line-layer.h b/src/libsysprof-gtk/sysprof-line-layer.h new file mode 100644 index 00000000..4a06b852 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-line-layer.h @@ -0,0 +1,52 @@ +/* + * sysprof-line-layer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-chart-layer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_LINE_LAYER (sysprof_line_layer_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF, LINE_LAYER, SysprofChartLayer) + +SYSPROF_AVAILABLE_IN_ALL +SysprofChartLayer *sysprof_line_layer_new (void); +SYSPROF_AVAILABLE_IN_ALL +const GdkRGBA *sysprof_line_layer_get_color (SysprofLineLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_line_layer_set_color (SysprofLineLayer *self, + const GdkRGBA *color); +SYSPROF_AVAILABLE_IN_ALL +SysprofXYSeries *sysprof_line_layer_get_series (SysprofLineLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_line_layer_set_series (SysprofLineLayer *self, + SysprofXYSeries *series); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_line_layer_get_use_curves (SysprofLineLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_line_layer_set_use_curves (SysprofLineLayer *self, + gboolean use_curves); + +G_END_DECLS diff --git a/src/libsysprof-gtk/tests/test-charts.c b/src/libsysprof-gtk/tests/test-charts.c index 25fb376c..b3990d6a 100644 --- a/src/libsysprof-gtk/tests/test-charts.c +++ b/src/libsysprof-gtk/tests/test-charts.c @@ -143,6 +143,7 @@ main (int argc, sysprof_xy_series_add (samples_series, time, depth, i); } + sysprof_xy_series_sort (samples_series); window = g_object_new (GTK_TYPE_WINDOW, "default-width", 800, @@ -175,6 +176,18 @@ main (int argc, SYSPROF_CHART_LAYER (layer)); gtk_box_append (GTK_BOX (box), chart); + chart = g_object_new (SYSPROF_TYPE_CHART, + "session", session, + "height-request", 128, + NULL); + layer = g_object_new (SYSPROF_TYPE_LINE_LAYER, + "series", samples_series, + "title", "Stack Depth as Line", + NULL); + sysprof_chart_add_layer (SYSPROF_CHART (chart), + SYSPROF_CHART_LAYER (layer)); + gtk_box_append (GTK_BOX (box), chart); + gtk_window_present (window); g_main_loop_run (main_loop);