From 6adda64123d9048feed6a4019eb03ef6455cb1b0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 26 Jun 2023 12:23:38 -0700 Subject: [PATCH] libsysprof-gtk: add xy layer to allow some code deduplication We can also port ColumnLayer to use this. --- src/libsysprof-gtk/meson.build | 2 + src/libsysprof-gtk/sysprof-line-layer.c | 100 ++----- src/libsysprof-gtk/sysprof-line-layer.h | 9 +- src/libsysprof-gtk/sysprof-xy-layer-private.h | 52 ++++ src/libsysprof-gtk/sysprof-xy-layer.c | 261 ++++++++++++++++++ src/libsysprof-gtk/sysprof-xy-layer.h | 54 ++++ 6 files changed, 394 insertions(+), 84 deletions(-) create mode 100644 src/libsysprof-gtk/sysprof-xy-layer-private.h create mode 100644 src/libsysprof-gtk/sysprof-xy-layer.c create mode 100644 src/libsysprof-gtk/sysprof-xy-layer.h diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 8209ae07..8ffa7405 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -16,6 +16,7 @@ libsysprof_gtk_public_sources = [ 'sysprof-time-series.c', 'sysprof-time-series-item.c', 'sysprof-weighted-callgraph-view.c', + 'sysprof-xy-layer.c', 'sysprof-xy-series.c', 'sysprof-xy-series-item.c', ] @@ -40,6 +41,7 @@ libsysprof_gtk_public_headers = [ 'sysprof-time-series-item.h', 'sysprof-time-span-layer.h', 'sysprof-weighted-callgraph-view.h', + 'sysprof-xy-layer.h', 'sysprof-xy-series.h', 'sysprof-xy-series-item.h', ] diff --git a/src/libsysprof-gtk/sysprof-line-layer.c b/src/libsysprof-gtk/sysprof-line-layer.c index c0fe42b8..fd4628f4 100644 --- a/src/libsysprof-gtk/sysprof-line-layer.c +++ b/src/libsysprof-gtk/sysprof-line-layer.c @@ -22,12 +22,11 @@ #include "config.h" #include "sysprof-line-layer.h" +#include "sysprof-xy-layer-private.h" struct _SysprofLineLayer { - SysprofChartLayer parent_instance; - - SysprofXYSeries *series; + SysprofXYLayer parent_instance; GdkRGBA color; @@ -37,7 +36,12 @@ struct _SysprofLineLayer guint use_curves : 1; }; -G_DEFINE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF_TYPE_CHART_LAYER) +struct _SysprofLineLayerClass +{ + SysprofXYLayerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF_TYPE_XY_LAYER) enum { PROP_0, @@ -45,7 +49,6 @@ enum { PROP_DASHED, PROP_FILL, PROP_FLIP_Y, - PROP_SERIES, PROP_USE_CURVES, N_PROPS }; @@ -63,10 +66,11 @@ sysprof_line_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { SysprofLineLayer *self = (SysprofLineLayer *)widget; - const SysprofXYSeriesValue *values; + const float *x_values; + const float *y_values; cairo_t *cr; - double last_x; - double last_y; + float last_x; + float last_y; guint n_values; int width; int height; @@ -77,10 +81,9 @@ sysprof_line_layer_snapshot (GtkWidget *widget, 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; + _sysprof_xy_layer_get_xy (SYSPROF_XY_LAYER (self), &x_values, &y_values, &n_values); - if (!(values = sysprof_xy_series_get_values (self->series, &n_values))) + if (width == 0 || height == 0 || n_values == 0 || self->color.alpha == 0) return; cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height)); @@ -92,9 +95,8 @@ sysprof_line_layer_snapshot (GtkWidget *widget, cairo_scale (cr, width, height); - last_x = values->x; - last_y = values->y; - + last_x = x_values[0]; + last_y = y_values[0]; if (self->fill) { @@ -110,9 +112,8 @@ sysprof_line_layer_snapshot (GtkWidget *widget, { for (guint i = 1; i < n_values; i++) { - const SysprofXYSeriesValue *v = &values[i]; - double x = v->x; - double y = v->y; + float x = x_values[i]; + float y = y_values[i]; cairo_curve_to (cr, last_x + ((x - last_x)/2), @@ -130,12 +131,13 @@ sysprof_line_layer_snapshot (GtkWidget *widget, { for (guint i = 1; i < n_values; i++) { - const SysprofXYSeriesValue *v = &values[i]; + float x = x_values[i]; + float y = y_values[i]; - cairo_line_to (cr, v->x, v->y); + cairo_line_to (cr, x, y); - last_x = v->x; - last_y = v->y; + last_x = x; + last_y = y; } } @@ -159,16 +161,6 @@ sysprof_line_layer_snapshot (GtkWidget *widget, 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, @@ -195,10 +187,6 @@ sysprof_line_layer_get_property (GObject *object, g_value_set_boolean (value, sysprof_line_layer_get_flip_y (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; @@ -234,10 +222,6 @@ sysprof_line_layer_set_property (GObject *object, sysprof_line_layer_set_flip_y (self, g_value_get_boolean (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; @@ -253,7 +237,6 @@ 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; @@ -279,11 +262,6 @@ sysprof_line_layer_class_init (SysprofLineLayerClass *klass) FALSE, (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, @@ -325,38 +303,6 @@ sysprof_line_layer_set_color (SysprofLineLayer *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_dashed (SysprofLineLayer *self) { diff --git a/src/libsysprof-gtk/sysprof-line-layer.h b/src/libsysprof-gtk/sysprof-line-layer.h index a8a3a1f2..bb1c23dd 100644 --- a/src/libsysprof-gtk/sysprof-line-layer.h +++ b/src/libsysprof-gtk/sysprof-line-layer.h @@ -23,14 +23,14 @@ #include -#include "sysprof-chart-layer.h" +#include "sysprof-xy-layer.h" G_BEGIN_DECLS #define SYSPROF_TYPE_LINE_LAYER (sysprof_line_layer_get_type()) SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF, LINE_LAYER, SysprofChartLayer) +G_DECLARE_FINAL_TYPE (SysprofLineLayer, sysprof_line_layer, SYSPROF, LINE_LAYER, SysprofXYLayer) SYSPROF_AVAILABLE_IN_ALL SysprofChartLayer *sysprof_line_layer_new (void); @@ -40,11 +40,6 @@ 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_dashed (SysprofLineLayer *self); SYSPROF_AVAILABLE_IN_ALL void sysprof_line_layer_set_dashed (SysprofLineLayer *self, diff --git a/src/libsysprof-gtk/sysprof-xy-layer-private.h b/src/libsysprof-gtk/sysprof-xy-layer-private.h new file mode 100644 index 00000000..ddaa9ded --- /dev/null +++ b/src/libsysprof-gtk/sysprof-xy-layer-private.h @@ -0,0 +1,52 @@ +/* sysprof-xy-layer-private.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 "sysprof-normalized-series.h" +#include "sysprof-xy-layer.h" + +G_BEGIN_DECLS + +struct _SysprofXYLayer +{ + SysprofChartLayer parent_instance; + + GBindingGroup *series_bindings; + SysprofXYSeries *series; + + SysprofAxis *x_axis; + SysprofAxis *y_axis; + + SysprofNormalizedSeries *normal_x; + SysprofNormalizedSeries *normal_y; +}; + +struct _SysprofXYLayerClass +{ + SysprofChartLayerClass parent_instance; +}; + +void _sysprof_xy_layer_get_xy (SysprofXYLayer *self, + const float **x_values, + const float **y_values, + guint *n_values); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-xy-layer.c b/src/libsysprof-gtk/sysprof-xy-layer.c new file mode 100644 index 00000000..50a96457 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-xy-layer.c @@ -0,0 +1,261 @@ +/* sysprof-xy-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-xy-layer-private.h" + +enum { + PROP_0, + PROP_SERIES, + PROP_X_AXIS, + PROP_Y_AXIS, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofXYLayer, sysprof_xy_layer, SYSPROF_TYPE_CHART_LAYER) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_xy_layer_dispose (GObject *object) +{ + SysprofXYLayer *self = (SysprofXYLayer *)object; + + g_clear_object (&self->series_bindings); + g_clear_object (&self->x_axis); + g_clear_object (&self->y_axis); + g_clear_object (&self->normal_x); + g_clear_object (&self->normal_y); + g_clear_object (&self->series); + + G_OBJECT_CLASS (sysprof_xy_layer_parent_class)->dispose (object); +} + +static void +sysprof_xy_layer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofXYLayer *self = SYSPROF_XY_LAYER (object); + + switch (prop_id) + { + case PROP_SERIES: + g_value_set_object (value, sysprof_xy_layer_get_series (self)); + break; + + case PROP_X_AXIS: + g_value_set_object (value, sysprof_xy_layer_get_x_axis (self)); + break; + + case PROP_Y_AXIS: + g_value_set_object (value, sysprof_xy_layer_get_y_axis (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_xy_layer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofXYLayer *self = SYSPROF_XY_LAYER (object); + + switch (prop_id) + { + case PROP_SERIES: + sysprof_xy_layer_set_series (self, g_value_get_object (value)); + break; + + case PROP_X_AXIS: + sysprof_xy_layer_set_x_axis (self, g_value_get_object (value)); + break; + + case PROP_Y_AXIS: + sysprof_xy_layer_set_y_axis (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_xy_layer_class_init (SysprofXYLayerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = sysprof_xy_layer_dispose; + object_class->get_property = sysprof_xy_layer_get_property; + object_class->set_property = sysprof_xy_layer_set_property; + + properties[PROP_SERIES] = + g_param_spec_object ("series", NULL, NULL, + SYSPROF_TYPE_XY_SERIES, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_X_AXIS] = + g_param_spec_object ("x-axis", NULL, NULL, + SYSPROF_TYPE_AXIS, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_Y_AXIS] = + g_param_spec_object ("y-axis", NULL, NULL, + SYSPROF_TYPE_AXIS, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_xy_layer_init (SysprofXYLayer *self) +{ + self->normal_x = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, NULL); + g_signal_connect_object (self->normal_x, + "items-changed", + G_CALLBACK (gtk_widget_queue_draw), + self, + G_CONNECT_SWAPPED); + + self->normal_y = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, NULL); + g_signal_connect_object (self->normal_y, + "items-changed", + G_CALLBACK (gtk_widget_queue_draw), + self, + G_CONNECT_SWAPPED); + + self->series_bindings = g_binding_group_new (); + g_binding_group_bind (self->series_bindings, "x-expression", + self->normal_x, "expression", + G_BINDING_SYNC_CREATE); + g_binding_group_bind (self->series_bindings, "y-expression", + self->normal_y, "expression", + G_BINDING_SYNC_CREATE); +} + +void +_sysprof_xy_layer_get_xy (SysprofXYLayer *self, + const float **x_values, + const float **y_values, + guint *n_values) +{ + guint n_x_values = 0; + guint n_y_values = 0; + + g_return_if_fail (SYSPROF_IS_XY_LAYER (self)); + g_return_if_fail (x_values != NULL); + g_return_if_fail (y_values != NULL); + g_return_if_fail (n_values != NULL); + + *x_values = sysprof_normalized_series_get_values (self->normal_x, &n_x_values); + *y_values = sysprof_normalized_series_get_values (self->normal_y, &n_y_values); + *n_values = MIN (n_x_values, n_y_values); +} + +/** + * sysprof_xy_layer_get_series: + * @self: a #SysprofXYLayer + * + * Returns: (transfer none) (nullable): a #SysprofXYSeries or %NULL + */ +SysprofXYSeries * +sysprof_xy_layer_get_series (SysprofXYLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_XY_LAYER (self), NULL); + + return self->series; +} + +void +sysprof_xy_layer_set_series (SysprofXYLayer *self, + SysprofXYSeries *series) +{ + g_return_if_fail (SYSPROF_IS_XY_LAYER (self)); + g_return_if_fail (!series || SYSPROF_IS_XY_SERIES (series)); + + if (!g_set_object (&self->series, series)) + return; + + g_binding_group_set_source (self->series_bindings, series); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SERIES]); +} + +/** + * sysprof_xy_layer_get_x_axis: + * @self: a #SysprofXYLayer + * + * Returns: (transfer none) (nullable): a #SysprofAxis or %NULL + */ +SysprofAxis * +sysprof_xy_layer_get_x_axis (SysprofXYLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_XY_LAYER (self), NULL); + + return self->x_axis; +} + +void +sysprof_xy_layer_set_x_axis (SysprofXYLayer *self, + SysprofAxis *x_axis) +{ + g_return_if_fail (SYSPROF_IS_XY_LAYER (self)); + g_return_if_fail (!x_axis || SYSPROF_IS_AXIS (x_axis)); + + if (!g_set_object (&self->x_axis, x_axis)) + return; + + sysprof_normalized_series_set_axis (self->normal_x, x_axis); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_X_AXIS]); +} + +/** + * sysprof_xy_layer_get_y_axis: + * @self: a #SysprofXYLayer + * + * Returns: (transfer none) (nullable): a #SysprofAxis or %NULL + */ +SysprofAxis * +sysprof_xy_layer_get_y_axis (SysprofXYLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_XY_LAYER (self), NULL); + + return self->y_axis; +} + +void +sysprof_xy_layer_set_y_axis (SysprofXYLayer *self, + SysprofAxis *y_axis) +{ + g_return_if_fail (SYSPROF_IS_XY_LAYER (self)); + g_return_if_fail (!y_axis || SYSPROF_IS_AXIS (y_axis)); + + if (!g_set_object (&self->y_axis, y_axis)) + return; + + sysprof_normalized_series_set_axis (self->normal_y, y_axis); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_Y_AXIS]); +} diff --git a/src/libsysprof-gtk/sysprof-xy-layer.h b/src/libsysprof-gtk/sysprof-xy-layer.h new file mode 100644 index 00000000..be4fc869 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-xy-layer.h @@ -0,0 +1,54 @@ +/* sysprof-xy-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-axis.h" +#include "sysprof-chart-layer.h" +#include "sysprof-xy-series.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_XY_LAYER (sysprof_xy_layer_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofXYLayer, sysprof_xy_layer, SYSPROF, XY_LAYER, SysprofChartLayer) + +SYSPROF_AVAILABLE_IN_ALL +SysprofChartLayer *sysprof_xy_layer_new (void); +SYSPROF_AVAILABLE_IN_ALL +SysprofXYSeries *sysprof_xy_layer_get_series (SysprofXYLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_layer_set_series (SysprofXYLayer *self, + SysprofXYSeries *series); +SYSPROF_AVAILABLE_IN_ALL +SysprofAxis *sysprof_xy_layer_get_x_axis (SysprofXYLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_layer_set_x_axis (SysprofXYLayer *self, + SysprofAxis *x_axis); +SYSPROF_AVAILABLE_IN_ALL +SysprofAxis *sysprof_xy_layer_get_y_axis (SysprofXYLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_layer_set_y_axis (SysprofXYLayer *self, + SysprofAxis *y_axis); + +G_END_DECLS