mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-20 21:00:10 +01:00
Ran a script to align the formal parameters of functions and collapse spaces to tabs in code.
604 lines
15 KiB
C
604 lines
15 KiB
C
/*
|
|
* Copyright © 2008 Chris Wilson
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
* and that both that copyright notice and this permission notice
|
|
* appear in supporting documentation, and that the name of the
|
|
* copyright holders not be used in advertising or publicity
|
|
* pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no
|
|
* representations about the suitability of this software for any
|
|
* purpose. It is provided "as is" without express or implied
|
|
* warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
* SOFTWARE.
|
|
*
|
|
* Authors: Chris Wilson <chris@chris-wilson.co.uk>
|
|
*/
|
|
|
|
#include "cairo-perf.h"
|
|
#include "cairo-perf-graph.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
struct _GraphView {
|
|
GtkWidget widget;
|
|
|
|
test_case_t *cases;
|
|
cairo_perf_report_t *reports;
|
|
int num_reports;
|
|
double ymin, ymax;
|
|
|
|
int selected_report;
|
|
};
|
|
|
|
typedef struct _GraphViewClass {
|
|
GtkWidgetClass parent_class;
|
|
} GraphViewClass;
|
|
|
|
static GType graph_view_get_type (void);
|
|
|
|
enum {
|
|
REPORT_SELECTED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
G_DEFINE_TYPE (GraphView, graph_view, GTK_TYPE_WIDGET)
|
|
|
|
static void
|
|
draw_baseline_performance (test_case_t *cases,
|
|
cairo_perf_report_t *reports,
|
|
int num_reports,
|
|
cairo_t *cr,
|
|
const cairo_matrix_t *m)
|
|
{
|
|
test_report_t **tests;
|
|
double dots[2] = { 0, 1.};
|
|
int i;
|
|
|
|
tests = xmalloc (num_reports * sizeof (test_report_t *));
|
|
for (i = 0; i < num_reports; i++)
|
|
tests[i] = reports[i].tests;
|
|
|
|
while (cases->backend != NULL) {
|
|
test_report_t *min_test;
|
|
double baseline, last_y;
|
|
double x, y;
|
|
|
|
if (! cases->shown) {
|
|
cases++;
|
|
continue;
|
|
}
|
|
|
|
min_test = cases->min_test;
|
|
|
|
for (i = 0; i < num_reports; i++) {
|
|
while (tests[i]->name &&
|
|
test_report_cmp_backend_then_name (tests[i], min_test) < 0)
|
|
{
|
|
tests[i]++;
|
|
}
|
|
}
|
|
|
|
/* first the stroke */
|
|
cairo_save (cr);
|
|
cairo_set_line_width (cr, 2.);
|
|
gdk_cairo_set_source_color (cr, &cases->color);
|
|
for (i = 0; i < num_reports; i++) {
|
|
if (tests[i]->name &&
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
{
|
|
baseline = tests[i]->stats.min_ticks;
|
|
|
|
x = i; y = 0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
y = floor (y);
|
|
cairo_move_to (cr, x, y);
|
|
last_y = y;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (++i; i < num_reports; i++) {
|
|
if (tests[i]->name &&
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
{
|
|
x = i, y = tests[i]->stats.min_ticks / baseline;
|
|
|
|
if (y < 1.)
|
|
y = -1./y + 1;
|
|
else
|
|
y -= 1;
|
|
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
y = floor (y);
|
|
cairo_line_to (cr, x, last_y);
|
|
cairo_line_to (cr, x, y);
|
|
last_y = y;
|
|
}
|
|
}
|
|
{
|
|
x = num_reports, y = 0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
cairo_line_to (cr, x, last_y);
|
|
}
|
|
|
|
cairo_set_line_width (cr, 1.);
|
|
cairo_stroke (cr);
|
|
|
|
/* then draw the points */
|
|
for (i = 0; i < num_reports; i++) {
|
|
if (tests[i]->name &&
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
{
|
|
baseline = tests[i]->stats.min_ticks;
|
|
|
|
x = i; y = 0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
y = floor (y);
|
|
cairo_move_to (cr, x, y);
|
|
cairo_close_path (cr);
|
|
last_y = y;
|
|
|
|
tests[i]++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (++i; i < num_reports; i++) {
|
|
if (tests[i]->name &&
|
|
test_report_cmp_backend_then_name (tests[i], min_test) == 0)
|
|
{
|
|
x = i, y = tests[i]->stats.min_ticks / baseline;
|
|
|
|
if (y < 1.)
|
|
y = -1./y + 1;
|
|
else
|
|
y -= 1;
|
|
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
y = floor (y);
|
|
cairo_move_to (cr, x, last_y);
|
|
cairo_close_path (cr);
|
|
cairo_move_to (cr, x, y);
|
|
cairo_close_path (cr);
|
|
last_y = y;
|
|
|
|
tests[i]++;
|
|
}
|
|
}
|
|
{
|
|
x = num_reports, y = 0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
x = floor (x);
|
|
cairo_move_to (cr, x, last_y);
|
|
cairo_close_path (cr);
|
|
}
|
|
cairo_set_source_rgba (cr, 0, 0, 0, .5);
|
|
cairo_set_dash (cr, dots, 2, 0.);
|
|
cairo_set_line_width (cr, 3.);
|
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
|
cairo_stroke (cr);
|
|
cairo_restore (cr);
|
|
|
|
cases++;
|
|
}
|
|
free (tests);
|
|
}
|
|
|
|
static void
|
|
draw_hline (cairo_t *cr,
|
|
const cairo_matrix_t *m,
|
|
double y0,
|
|
double xmin,
|
|
double xmax)
|
|
{
|
|
double x, y;
|
|
double py_offset;
|
|
|
|
py_offset = fmod (cairo_get_line_width (cr) / 2., 1.);
|
|
|
|
x = xmin; y = y0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
cairo_move_to (cr, floor (x), floor (y) + py_offset);
|
|
|
|
x = xmax; y = y0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
cairo_line_to (cr, ceil (x), floor (y) + py_offset);
|
|
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
static void
|
|
draw_label (cairo_t *cr,
|
|
const cairo_matrix_t *m,
|
|
double y0,
|
|
double xmin,
|
|
double xmax)
|
|
{
|
|
double x, y;
|
|
char buf[80];
|
|
cairo_text_extents_t extents;
|
|
|
|
snprintf (buf, sizeof (buf), "%.0fx", fabs (y0));
|
|
cairo_text_extents (cr, buf, &extents);
|
|
|
|
x = xmin; y = y0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
cairo_move_to (cr,
|
|
x - extents.width - 4,
|
|
y - (extents.height/2. + extents.y_bearing));
|
|
cairo_show_text (cr, buf);
|
|
|
|
|
|
snprintf (buf, sizeof (buf), "%.0fx", fabs (y0));
|
|
cairo_text_extents (cr, buf, &extents);
|
|
|
|
x = xmax; y = y0;
|
|
cairo_matrix_transform_point (m, &x, &y);
|
|
cairo_move_to (cr,
|
|
x + 4,
|
|
y - (extents.height/2. + extents.y_bearing));
|
|
cairo_show_text (cr, buf);
|
|
}
|
|
|
|
#define ALIGN_X(v) ((v)<<0)
|
|
#define ALIGN_Y(v) ((v)<<2)
|
|
static void
|
|
draw_rotated_label (cairo_t *cr,
|
|
const char *text,
|
|
double x,
|
|
double y,
|
|
double angle,
|
|
int align)
|
|
{
|
|
cairo_text_extents_t extents;
|
|
|
|
cairo_text_extents (cr, text, &extents);
|
|
|
|
cairo_save (cr); {
|
|
cairo_translate (cr, x, y);
|
|
cairo_rotate (cr, angle);
|
|
switch (align) {
|
|
case ALIGN_X(0) | ALIGN_Y(0):
|
|
cairo_move_to (cr,
|
|
-extents.x_bearing,
|
|
-extents.y_bearing);
|
|
break;
|
|
case ALIGN_X(0) | ALIGN_Y(1):
|
|
cairo_move_to (cr,
|
|
-extents.x_bearing,
|
|
- (extents.height/2. + extents.y_bearing));
|
|
break;
|
|
case ALIGN_X(0) | ALIGN_Y(2):
|
|
cairo_move_to (cr,
|
|
-extents.x_bearing,
|
|
- (extents.height + extents.y_bearing));
|
|
break;
|
|
|
|
case ALIGN_X(1) | ALIGN_Y(0):
|
|
cairo_move_to (cr,
|
|
- (extents.width/2. + extents.x_bearing),
|
|
-extents.y_bearing);
|
|
break;
|
|
case ALIGN_X(1) | ALIGN_Y(1):
|
|
cairo_move_to (cr,
|
|
- (extents.width/2. + extents.x_bearing),
|
|
- (extents.height/2. + extents.y_bearing));
|
|
break;
|
|
case ALIGN_X(1) | ALIGN_Y(2):
|
|
cairo_move_to (cr,
|
|
- (extents.width/2. + extents.x_bearing),
|
|
- (extents.height + extents.y_bearing));
|
|
break;
|
|
|
|
case ALIGN_X(2) | ALIGN_Y(0):
|
|
cairo_move_to (cr,
|
|
- (extents.width + extents.x_bearing),
|
|
-extents.y_bearing);
|
|
break;
|
|
case ALIGN_X(2) | ALIGN_Y(1):
|
|
cairo_move_to (cr,
|
|
- (extents.width + extents.x_bearing),
|
|
- (extents.height/2. + extents.y_bearing));
|
|
break;
|
|
case ALIGN_X(2) | ALIGN_Y(2):
|
|
cairo_move_to (cr,
|
|
- (extents.width + extents.x_bearing),
|
|
- (extents.height + extents.y_bearing));
|
|
break;
|
|
}
|
|
cairo_show_text (cr, text);
|
|
} cairo_restore (cr);
|
|
}
|
|
|
|
#define PAD 36
|
|
static void
|
|
graph_view_draw (GraphView *self,
|
|
cairo_t *cr)
|
|
{
|
|
cairo_matrix_t m;
|
|
const double dash[2] = {4, 4};
|
|
double range;
|
|
int i;
|
|
|
|
if (self->widget.allocation.width < 4 *PAD)
|
|
return;
|
|
if (self->widget.allocation.height < 3 *PAD)
|
|
return;
|
|
|
|
range = floor (self->ymax+1) - ceil (self->ymin-1);
|
|
|
|
cairo_matrix_init_translate (&m, PAD, self->widget.allocation.height - PAD);
|
|
cairo_matrix_scale (&m,
|
|
(self->widget.allocation.width-2*PAD)/(self->num_reports),
|
|
-(self->widget.allocation.height-2*PAD)/range);
|
|
cairo_matrix_translate (&m, 0, floor (self->ymax+1));
|
|
|
|
if (self->selected_report != -1) {
|
|
cairo_save (cr); {
|
|
double x0, x1, y;
|
|
x0 = self->selected_report; y = 0;
|
|
cairo_matrix_transform_point (&m, &x0, &y);
|
|
x0 = floor (x0);
|
|
x1 = self->selected_report + 1; y = 0;
|
|
cairo_matrix_transform_point (&m, &x1, &y);
|
|
x1 = ceil (x1);
|
|
y = (x1 - x0) / 8;
|
|
y = MIN (y, PAD / 2);
|
|
x0 -= y;
|
|
x1 += y;
|
|
cairo_rectangle (cr, x0, PAD/2, x1-x0, self->widget.allocation.height-2*PAD + PAD);
|
|
gdk_cairo_set_source_color (cr, &self->widget.style->base[GTK_STATE_SELECTED]);
|
|
cairo_fill (cr);
|
|
} cairo_restore (cr);
|
|
}
|
|
|
|
cairo_save (cr); {
|
|
cairo_pattern_t *linear;
|
|
double x, y;
|
|
|
|
gdk_cairo_set_source_color (cr,
|
|
&self->widget.style->fg[GTK_WIDGET_STATE (self)]);
|
|
cairo_set_line_width (cr, 2.);
|
|
draw_hline (cr, &m, 0, 0, self->num_reports);
|
|
|
|
cairo_set_line_width (cr, 1.);
|
|
cairo_set_dash (cr, NULL, 0, 0);
|
|
|
|
for (i = ceil (self->ymin-1); i <= floor (self->ymax+1); i++) {
|
|
if (i != 0)
|
|
draw_hline (cr, &m, i, 0, self->num_reports);
|
|
}
|
|
|
|
cairo_set_font_size (cr, 11);
|
|
|
|
linear = cairo_pattern_create_linear (0, PAD, 0, self->widget.allocation.height-2*PAD);
|
|
cairo_pattern_add_color_stop_rgb (linear, 0, 0, 1, 0);
|
|
cairo_pattern_add_color_stop_rgb (linear, 1, 1, 0, 0);
|
|
cairo_set_source (cr, linear);
|
|
cairo_pattern_destroy (linear);
|
|
|
|
for (i = ceil (self->ymin-1); i <= floor (self->ymax+1); i++) {
|
|
if (i != 0)
|
|
draw_label (cr, &m, i, 0, self->num_reports);
|
|
}
|
|
|
|
x = 0, y = floor (self->ymax+1);
|
|
cairo_matrix_transform_point (&m, &x, &y);
|
|
draw_rotated_label (cr, "Faster", x - 7, y + 14,
|
|
270./360 * 2 * G_PI,
|
|
ALIGN_X(2) | ALIGN_Y(1));
|
|
x = self->num_reports, y = floor (self->ymax+1);
|
|
cairo_matrix_transform_point (&m, &x, &y);
|
|
draw_rotated_label (cr, "Faster", x + 11, y + 14,
|
|
270./360 * 2 * G_PI,
|
|
ALIGN_X(2) | ALIGN_Y(1));
|
|
|
|
x = 0, y = ceil (self->ymin-1);
|
|
cairo_matrix_transform_point (&m, &x, &y);
|
|
draw_rotated_label (cr, "Slower", x - 7, y - 14,
|
|
90./360 * 2 * G_PI,
|
|
ALIGN_X(2) | ALIGN_Y(1));
|
|
x = self->num_reports, y = ceil (self->ymin-1);
|
|
cairo_matrix_transform_point (&m, &x, &y);
|
|
draw_rotated_label (cr, "Slower", x + 11, y - 14,
|
|
90./360 * 2 * G_PI,
|
|
ALIGN_X(2) | ALIGN_Y(1));
|
|
} cairo_restore (cr);
|
|
|
|
draw_baseline_performance (self->cases,
|
|
self->reports, self->num_reports,
|
|
cr, &m);
|
|
|
|
cairo_save (cr); {
|
|
cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
|
|
cairo_set_line_width (cr, 1.);
|
|
cairo_set_dash (cr, dash, 2, 0);
|
|
draw_hline (cr, &m, 0, 0, self->num_reports);
|
|
} cairo_restore (cr);
|
|
}
|
|
|
|
static gboolean
|
|
graph_view_expose (GtkWidget *w,
|
|
GdkEventExpose *ev)
|
|
{
|
|
GraphView *self = (GraphView *) w;
|
|
cairo_t *cr;
|
|
|
|
cr = gdk_cairo_create (w->window);
|
|
gdk_cairo_set_source_color (cr, &w->style->base[GTK_WIDGET_STATE (w)]);
|
|
cairo_paint (cr);
|
|
|
|
graph_view_draw (self, cr);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
graph_view_button_press (GtkWidget *w,
|
|
GdkEventButton *ev)
|
|
{
|
|
GraphView *self = (GraphView *) w;
|
|
cairo_matrix_t m;
|
|
double x,y;
|
|
int i;
|
|
|
|
cairo_matrix_init_translate (&m, PAD, self->widget.allocation.height-PAD);
|
|
cairo_matrix_scale (&m, (self->widget.allocation.width-2*PAD)/self->num_reports, -(self->widget.allocation.height-2*PAD)/(self->ymax - self->ymin));
|
|
cairo_matrix_translate (&m, 0, -self->ymin);
|
|
cairo_matrix_invert (&m);
|
|
|
|
x = ev->x;
|
|
y = ev->y;
|
|
cairo_matrix_transform_point (&m, &x, &y);
|
|
|
|
i = floor (x);
|
|
if (i < 0 || i >= self->num_reports)
|
|
i = -1;
|
|
|
|
if (i != self->selected_report) {
|
|
self->selected_report = i;
|
|
gtk_widget_queue_draw (w);
|
|
|
|
g_signal_emit (w, signals[REPORT_SELECTED], 0, i);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
graph_view_button_release (GtkWidget *w,
|
|
GdkEventButton *ev)
|
|
{
|
|
GraphView *self = (GraphView *) w;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
graph_view_realize (GtkWidget *widget)
|
|
{
|
|
GdkWindowAttr attributes;
|
|
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
|
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.x = widget->allocation.x;
|
|
attributes.y = widget->allocation.y;
|
|
attributes.width = widget->allocation.width;
|
|
attributes.height = widget->allocation.height;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.colormap = gtk_widget_get_colormap (widget);
|
|
attributes.event_mask = gtk_widget_get_events (widget) |
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_EXPOSURE_MASK;
|
|
|
|
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
|
&attributes,
|
|
GDK_WA_X | GDK_WA_Y |
|
|
GDK_WA_VISUAL | GDK_WA_COLORMAP);
|
|
gdk_window_set_user_data (widget->window, widget);
|
|
|
|
widget->style = gtk_style_attach (widget->style, widget->window);
|
|
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
|
|
}
|
|
|
|
static void
|
|
graph_view_finalize (GObject *obj)
|
|
{
|
|
G_OBJECT_CLASS (graph_view_parent_class)->finalize (obj);
|
|
}
|
|
|
|
static void
|
|
graph_view_class_init (GraphViewClass *klass)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
|
|
|
|
object_class->finalize = graph_view_finalize;
|
|
|
|
widget_class->realize = graph_view_realize;
|
|
widget_class->expose_event = graph_view_expose;
|
|
widget_class->button_press_event = graph_view_button_press;
|
|
widget_class->button_release_event = graph_view_button_release;
|
|
|
|
signals[REPORT_SELECTED] =
|
|
g_signal_new ("report-selected",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
0,//G_STRUCT_OFFSET (GraphView, report_selected),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__INT,
|
|
G_TYPE_NONE, 1, G_TYPE_INT);
|
|
}
|
|
|
|
static void
|
|
graph_view_init (GraphView *self)
|
|
{
|
|
self->selected_report = -1;
|
|
}
|
|
|
|
GtkWidget *
|
|
graph_view_new (void)
|
|
{
|
|
return g_object_new (graph_view_get_type (), NULL);
|
|
}
|
|
|
|
void
|
|
graph_view_update_visible (GraphView *gv)
|
|
{
|
|
double min, max;
|
|
test_case_t *cases;
|
|
|
|
cases = gv->cases;
|
|
|
|
min = max = 1.;
|
|
while (cases->name != NULL) {
|
|
if (cases->shown) {
|
|
if (cases->min < min)
|
|
min = cases->min;
|
|
if (cases->max > max)
|
|
max = cases->max;
|
|
}
|
|
cases++;
|
|
}
|
|
gv->ymin = -1/min + 1;
|
|
gv->ymax = max - 1;
|
|
|
|
gtk_widget_queue_draw (&gv->widget);
|
|
}
|
|
|
|
void
|
|
graph_view_set_reports (GraphView *gv,
|
|
test_case_t *cases,
|
|
cairo_perf_report_t *reports,
|
|
int num_reports)
|
|
{
|
|
/* XXX ownership? */
|
|
gv->cases = cases;
|
|
gv->reports = reports;
|
|
gv->num_reports = num_reports;
|
|
|
|
graph_view_update_visible (gv);
|
|
}
|