From be2bd189cd6d96c63e4a8a41cff98486e740ebf4 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Thu, 26 Aug 2010 20:52:52 +0200 Subject: [PATCH] app: completely switch to cairo-drawing the selection and remove all old selection drawing code. Thanks to Benjamin Otte for pointing out the right optimization. Also fixes bug #479875 - performance problem drawing a complex selection. --- app/display/gimpcanvas.c | 184 --------------- app/display/gimpcanvas.h | 7 - app/display/gimpdisplayshell-draw.c | 26 +- app/display/gimpdisplayshell-draw.h | 7 +- app/display/gimpdisplayshell-selection.c | 289 +++-------------------- 5 files changed, 53 insertions(+), 460 deletions(-) diff --git a/app/display/gimpcanvas.c b/app/display/gimpcanvas.c index 0043ebfaef..9b0402d627 100644 --- a/app/display/gimpcanvas.c +++ b/app/display/gimpcanvas.c @@ -56,7 +56,6 @@ static void gimp_canvas_get_property (GObject *object, GValue *value, GParamSpec *pspec); -static void gimp_canvas_realize (GtkWidget *widget); static void gimp_canvas_unrealize (GtkWidget *widget); static void gimp_canvas_style_set (GtkWidget *widget, GtkStyle *prev_style); @@ -76,91 +75,6 @@ G_DEFINE_TYPE (GimpCanvas, gimp_canvas, GIMP_TYPE_OVERLAY_BOX) #define parent_class gimp_canvas_parent_class -static const guchar stipples[GIMP_CANVAS_NUM_STIPPLES][8] = -{ - { - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - }, - { - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - }, - { - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - }, - { - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - }, - { - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - }, - { - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - }, - { - 0x3C, /* --####-- */ - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - }, - { - 0x78, /* -####--- */ - 0xF0, /* ####---- */ - 0xE1, /* ###----# */ - 0xC3, /* ##----## */ - 0x87, /* #----### */ - 0x0F, /* ----#### */ - 0x1E, /* ---####- */ - 0x3C, /* --####-- */ - }, -}; - - static void gimp_canvas_class_init (GimpCanvasClass *klass) { @@ -170,7 +84,6 @@ gimp_canvas_class_init (GimpCanvasClass *klass) object_class->set_property = gimp_canvas_set_property; object_class->get_property = gimp_canvas_get_property; - widget_class->realize = gimp_canvas_realize; widget_class->unrealize = gimp_canvas_unrealize; widget_class->style_set = gimp_canvas_style_set; widget_class->focus_in_event = gimp_canvas_focus_in_event; @@ -197,9 +110,6 @@ gimp_canvas_init (GimpCanvas *canvas) for (i = 0; i < GIMP_CANVAS_NUM_STYLES; i++) canvas->gc[i] = NULL; - - for (i = 0; i < GIMP_CANVAS_NUM_STIPPLES; i++) - canvas->stipple[i] = NULL; } static void @@ -240,18 +150,6 @@ gimp_canvas_get_property (GObject *object, } } -static void -gimp_canvas_realize (GtkWidget *widget) -{ - GimpCanvas *canvas = GIMP_CANVAS (widget); - - GTK_WIDGET_CLASS (parent_class)->realize (widget); - - canvas->stipple[0] = - gdk_bitmap_create_from_data (gtk_widget_get_window (widget), - (const gchar *) stipples[0], 8, 8); -} - static void gimp_canvas_unrealize (GtkWidget *widget) { @@ -267,15 +165,6 @@ gimp_canvas_unrealize (GtkWidget *widget) } } - for (i = 0; i < GIMP_CANVAS_NUM_STIPPLES; i++) - { - if (canvas->stipple[i]) - { - g_object_unref (canvas->stipple[i]); - canvas->stipple[i] = NULL; - } - } - if (canvas->layout) { g_object_unref (canvas->layout); @@ -369,10 +258,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas, switch (style) { - case GIMP_CANVAS_STYLE_BLACK: - case GIMP_CANVAS_STYLE_WHITE: - break; - case GIMP_CANVAS_STYLE_RENDER: mask |= GDK_GC_EXPOSURES; values.graphics_exposures = TRUE; @@ -396,14 +281,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas, values.join_style = GDK_JOIN_MITER; break; - case GIMP_CANVAS_STYLE_SELECTION_IN: - case GIMP_CANVAS_STYLE_SELECTION_OUT: - mask |= GDK_GC_CAP_STYLE | GDK_GC_FILL | GDK_GC_STIPPLE; - values.cap_style = GDK_CAP_NOT_LAST; - values.fill = GDK_OPAQUE_STIPPLED; - values.stipple = canvas->stipple[0]; - break; - default: return NULL; } @@ -426,33 +303,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas, case GIMP_CANVAS_STYLE_XOR_DASHED: case GIMP_CANVAS_STYLE_XOR: break; - - case GIMP_CANVAS_STYLE_WHITE: - fg.red = 0xffff; - fg.green = 0xffff; - fg.blue = 0xffff; - break; - - case GIMP_CANVAS_STYLE_BLACK: - case GIMP_CANVAS_STYLE_SELECTION_IN: - fg.red = 0x0; - fg.green = 0x0; - fg.blue = 0x0; - - bg.red = 0xffff; - bg.green = 0xffff; - bg.blue = 0xffff; - break; - - case GIMP_CANVAS_STYLE_SELECTION_OUT: - fg.red = 0xffff; - fg.green = 0xffff; - fg.blue = 0xffff; - - bg.red = 0x7f7f; - bg.green = 0x7f7f; - bg.blue = 0x7f7f; - break; } gdk_gc_set_rgb_fg_color (gc, &fg); @@ -885,40 +735,6 @@ gimp_canvas_set_clip_region (GimpCanvas *canvas, gdk_gc_set_clip_region (canvas->gc[style], region); } -/** - * gimp_canvas_set_stipple_index: - * @canvas: a #GimpCanvas widget - * @style: the #GimpCanvasStyle to alter - * @index: the new stipple index - * - * Some styles of the #GimpCanvas do a stipple fill. #GimpCanvas has a - * set of %GIMP_CANVAS_NUM_STIPPLES stipple bitmaps. This function - * allows you to change the bitmap being used. This can be used to - * implement a marching ants effect. An older implementation used to - * use this feature and so it is included since it might be useful in - * the future. All stipple bitmaps but the default one are created on - * the fly. - */ -void -gimp_canvas_set_stipple_index (GimpCanvas *canvas, - GimpCanvasStyle style, - guint index) -{ - if (! gimp_canvas_ensure_style (canvas, style)) - return; - - index = index % GIMP_CANVAS_NUM_STIPPLES; - - if (! canvas->stipple[index]) - { - canvas->stipple[index] = - gdk_bitmap_create_from_data (gtk_widget_get_window (GTK_WIDGET (canvas)), - (const gchar *) stipples[index], 8, 8); - } - - gdk_gc_set_stipple (canvas->gc[style], canvas->stipple[index]); -} - /** * gimp_canvas_set_bg_color: * @canvas: a #GimpCanvas widget diff --git a/app/display/gimpcanvas.h b/app/display/gimpcanvas.h index 52eb9db4b1..39a037ee09 100644 --- a/app/display/gimpcanvas.h +++ b/app/display/gimpcanvas.h @@ -24,14 +24,10 @@ typedef enum { - GIMP_CANVAS_STYLE_BLACK, - GIMP_CANVAS_STYLE_WHITE, GIMP_CANVAS_STYLE_RENDER, GIMP_CANVAS_STYLE_XOR, GIMP_CANVAS_STYLE_XOR_DASHED, GIMP_CANVAS_STYLE_XOR_DOTTED, - GIMP_CANVAS_STYLE_SELECTION_IN, - GIMP_CANVAS_STYLE_SELECTION_OUT, GIMP_CANVAS_NUM_STYLES } GimpCanvasStyle; @@ -147,9 +143,6 @@ void gimp_canvas_set_clip_rect (GimpCanvas *canvas, void gimp_canvas_set_clip_region (GimpCanvas *canvas, GimpCanvasStyle style, const GdkRegion *region); -void gimp_canvas_set_stipple_index (GimpCanvas *canvas, - GimpCanvasStyle style, - guint index); void gimp_canvas_set_bg_color (GimpCanvas *canvas, GimpRGB *color); diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 5a08c2a90d..6dd32aaf98 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -598,11 +598,10 @@ gimp_display_shell_draw_selection_out (GimpDisplayShell *shell, } void -gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, - cairo_t *cr, - GdkSegment *segs, - gint n_segs, - gint index) +gimp_display_shell_draw_selection_segments (GimpDisplayShell *shell, + cairo_t *cr, + GdkSegment *segs, + gint n_segs) { gint i; @@ -610,7 +609,7 @@ gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, g_return_if_fail (cr != NULL); g_return_if_fail (segs != NULL && n_segs > 0); - gimp_display_shell_set_selection_in_style (shell, cr, index); + cairo_set_line_width (cr, 1.0); for (i = 0; i < n_segs; i++) { @@ -629,6 +628,21 @@ gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, cairo_stroke (cr); } +void +gimp_display_shell_draw_selection_in_mask (GimpDisplayShell *shell, + cairo_t *cr, + cairo_pattern_t *mask, + gint index) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (cr != NULL); + g_return_if_fail (mask != NULL); + + gimp_display_shell_set_selection_in_style (shell, cr, index); + + cairo_mask (cr, mask); +} + void gimp_display_shell_draw_vector (GimpDisplayShell *shell, GimpVectors *vectors) diff --git a/app/display/gimpdisplayshell-draw.h b/app/display/gimpdisplayshell-draw.h index 8fed4979b7..782c7faf74 100644 --- a/app/display/gimpdisplayshell-draw.h +++ b/app/display/gimpdisplayshell-draw.h @@ -57,10 +57,13 @@ void gimp_display_shell_draw_selection_out (GimpDisplayShell *shell, cairo_t *cr, GdkSegment *segs, gint n_segs); -void gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, +void gimp_display_shell_draw_selection_segments (GimpDisplayShell *shell, cairo_t *cr, GdkSegment *segs, - gint n_segs, + gint n_segs); +void gimp_display_shell_draw_selection_in_mask (GimpDisplayShell *shell, + cairo_t *cr, + cairo_pattern_t *mask, gint index); void gimp_display_shell_draw_vector (GimpDisplayShell *shell, GimpVectors *vectors); diff --git a/app/display/gimpdisplayshell-selection.c b/app/display/gimpdisplayshell-selection.c index 28b8cfd091..863d220ff8 100644 --- a/app/display/gimpdisplayshell-selection.c +++ b/app/display/gimpdisplayshell-selection.c @@ -41,18 +41,10 @@ #include "gimpdisplayshell-transform.h" -#undef VERBOSE - -#define MAX_POINTS_INC 2048 -#define USE_DRAWPOINTS TRUE - - struct _Selection { GimpDisplayShell *shell; /* shell that owns the selection */ - gboolean use_cairo; /* temp hack */ - GdkSegment *segs_in; /* gdk segments of area boundary */ gint n_segs_in; /* number of segments in segs_in */ @@ -68,8 +60,7 @@ struct _Selection gboolean hidden; /* is the selection hidden? */ gboolean layer_hidden; /* is the layer boundary hidden? */ guint timeout; /* timer for successive draws */ - GdkPoint *points_in[8]; /* points of segs_in for fast ants */ - gint num_points_in[8]; /* number of points in points_in */ + cairo_pattern_t *segs_in_mask; /* cache for rendered segments */ }; @@ -86,12 +77,7 @@ static void selection_undraw (Selection *selection); static void selection_layer_undraw (Selection *selection); static void selection_layer_draw (Selection *selection); -static void selection_add_point (GdkPoint *points[8], - gint max_npoints[8], - gint npoints[8], - gint x, - gint y); -static void selection_render_points (Selection *selection); +static void selection_render_mask (Selection *selection); static void selection_transform_segs (Selection *selection, const BoundSeg *src_segs, @@ -124,7 +110,6 @@ gimp_display_shell_selection_init (GimpDisplayShell *shell) selection = g_slice_new0 (Selection); selection->shell = shell; - selection->use_cairo = g_getenv ("CAIRO_SELECTION") != NULL; selection->visible = TRUE; selection->hidden = ! gimp_display_shell_get_show_selection (shell); selection->layer_hidden = ! gimp_display_shell_get_show_layer (shell); @@ -298,105 +283,21 @@ selection_resume (Selection *selection) selection_start (selection); } -/* #define BENCHMARK 1 */ - static void selection_draw (Selection *selection) { - GimpCanvas *canvas = GIMP_CANVAS (selection->shell->canvas); -#ifdef BENCHMARK - GTimer *timer = g_timer_new (); - gint test; - - for (test = 0; test < 20; test++) + if (selection->segs_in) { -#endif /* BENCHMARK */ - if (selection->use_cairo) - { - if (selection->segs_in) - { - cairo_t *cr; + cairo_t *cr; - cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas)); + cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas)); - gimp_display_shell_draw_selection_in (selection->shell, cr, - selection->segs_in, - selection->n_segs_in, - selection->index % 8); + gimp_display_shell_draw_selection_in_mask (selection->shell, cr, + selection->segs_in_mask, + selection->index % 8); - cairo_destroy (cr); - } + cairo_destroy (cr); } - else - { -#ifdef USE_DRAWPOINTS - -#ifdef VERBOSE - { - gint j, sum; - - sum = 0; - for (j = 0; j < 8; j++) - sum += selection->num_points_in[j]; - - g_print ("%d segments, %d points\n", selection->n_segs_in, sum); - } -#endif - - if (selection->segs_in) - { - gint i; - - if (selection->index == 0) - { - for (i = 0; i < 4; i++) - if (selection->num_points_in[i]) - gimp_canvas_draw_points (canvas, GIMP_CANVAS_STYLE_WHITE, - selection->points_in[i], - selection->num_points_in[i]); - - for (i = 4; i < 8; i++) - if (selection->num_points_in[i]) - gimp_canvas_draw_points (canvas, GIMP_CANVAS_STYLE_BLACK, - selection->points_in[i], - selection->num_points_in[i]); - } - else - { - i = ((selection->index + 3) & 7); - if (selection->num_points_in[i]) - gimp_canvas_draw_points (canvas, GIMP_CANVAS_STYLE_WHITE, - selection->points_in[i], - selection->num_points_in[i]); - - i = ((selection->index + 7) & 7); - if (selection->num_points_in[i]) - gimp_canvas_draw_points (canvas, GIMP_CANVAS_STYLE_BLACK, - selection->points_in[i], - selection->num_points_in[i]); - } - } - -#else /* ! USE_DRAWPOINTS */ - - gimp_canvas_set_stipple_index (canvas, - GIMP_CANVAS_STYLE_SELECTION_IN, - selection->index); - if (selection->segs_in) - gimp_canvas_draw_segments (canvas, GIMP_CANVAS_STYLE_SELECTION_IN, - selection->segs_in, - selection->n_segs_in); - -#endif /* USE_DRAWPOINTS */ - } -#ifdef BENCHMARK - } - - g_printerr ("drawing 20 selections took %f seconds\n", - g_timer_elapsed (timer, NULL)); - - g_timer_destroy (timer); -#endif /* BENCHMARK */ } static void @@ -474,137 +375,21 @@ selection_layer_undraw (Selection *selection) } static void -selection_add_point (GdkPoint *points[8], - gint max_npoints[8], - gint npoints[8], - gint x, - gint y) +selection_render_mask (Selection *selection) { - gint i, j; + cairo_t *cr; - j = (x - y) & 7; + cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas)); - i = npoints[j]++; - if (i == max_npoints[j]) - { - max_npoints[j] += 2048; - points[j] = g_realloc (points[j], sizeof (GdkPoint) * max_npoints[j]); - } + cairo_push_group_with_content (cr, CAIRO_CONTENT_ALPHA); - points[j][i].x = x; - points[j][i].y = y; -} + gimp_display_shell_draw_selection_segments (selection->shell, cr, + selection->segs_in, + selection->n_segs_in); + selection->segs_in_mask = cairo_pop_group (cr); -/* Render the segs_in array into points_in */ - -static void -selection_render_points (Selection *selection) -{ - gint max_npoints[8]; - gint i; - - if (selection->segs_in == NULL) - return; - - for (i = 0; i < 8; i++) - { - max_npoints[i] = MAX_POINTS_INC; - selection->points_in[i] = g_new (GdkPoint, max_npoints[i]); - selection->num_points_in[i] = 0; - } - - for (i = 0; i < selection->n_segs_in; i++) - { - gint x, y; - gint dx, dy; - gint dxa, dya; - gint r; - -#ifdef VERBOSE - g_print ("%2d: (%d, %d) - (%d, %d)\n", i, - selection->segs_in[i].x1, - selection->segs_in[i].y1, - selection->segs_in[i].x2, - selection->segs_in[i].y2); -#endif - - x = selection->segs_in[i].x1; - dxa = selection->segs_in[i].x2 - x; - - if (dxa > 0) - { - dx = 1; - } - else - { - dxa = -dxa; - dx = -1; - } - - y = selection->segs_in[i].y1; - dya = selection->segs_in[i].y2 - y; - - if (dya > 0) - { - dy = 1; - } - else - { - dya = -dya; - dy = -1; - } - - if (dxa > dya) - { - r = dya; - - do - { - selection_add_point (selection->points_in, - max_npoints, - selection->num_points_in, - x, y); - x += dx; - r += dya; - - if (r >= (dxa << 1)) - { - y += dy; - r -= (dxa << 1); - } - } - while (x != selection->segs_in[i].x2); - } - else if (dxa < dya) - { - r = dxa; - - do - { - selection_add_point (selection->points_in, - max_npoints, - selection->num_points_in, - x, y); - y += dy; - r += dxa; - - if (r >= (dya << 1)) - { - x += dx; - r -= (dya << 1); - } - } - while (y != selection->segs_in[i].y2); - } - else - { - selection_add_point (selection->points_in, - max_npoints, - selection->num_points_in, - x, y); - } - } + cairo_destroy (cr); } static void @@ -672,9 +457,7 @@ selection_generate_segs (Selection *selection) selection_transform_segs (selection, segs_in, selection->segs_in, selection->n_segs_in); -#ifdef USE_DRAWPOINTS - selection_render_points (selection); -#endif + selection_render_mask (selection); } else { @@ -718,8 +501,6 @@ selection_generate_segs (Selection *selection) static void selection_free_segs (Selection *selection) { - gint j; - if (selection->segs_in) { g_free (selection->segs_in); @@ -741,14 +522,10 @@ selection_free_segs (Selection *selection) selection->n_segs_layer = 0; } - for (j = 0; j < 8; j++) + if (selection->segs_in_mask) { - if (selection->points_in[j]) - { - g_free (selection->points_in[j]); - selection->points_in[j] = NULL; - selection->num_points_in[j] = 0; - } + cairo_pattern_destroy (selection->segs_in_mask); + selection->segs_in_mask = NULL; } } @@ -771,31 +548,21 @@ selection_start_timeout (Selection *selection) /* Draw the ants */ if (! selection->hidden) { - GimpCanvas *canvas = GIMP_CANVAS (selection->shell->canvas); GimpDisplayConfig *config = selection->shell->display->config; selection_draw (selection); if (selection->segs_out) { - if (selection->use_cairo) - { - cairo_t *cr; + cairo_t *cr; - cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas)); + cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas)); - gimp_display_shell_draw_selection_out (selection->shell, cr, - selection->segs_out, - selection->n_segs_out); + gimp_display_shell_draw_selection_out (selection->shell, cr, + selection->segs_out, + selection->n_segs_out); - cairo_destroy (cr); - } - else - { - gimp_canvas_draw_segments (canvas, GIMP_CANVAS_STYLE_SELECTION_OUT, - selection->segs_out, - selection->n_segs_out); - } + cairo_destroy (cr); } if (selection->segs_in && selection->visible)