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.
This commit is contained in:
Michael Natterer
2010-08-26 20:52:52 +02:00
parent d745ab3871
commit be2bd189cd
5 changed files with 53 additions and 460 deletions

View File

@ -56,7 +56,6 @@ static void gimp_canvas_get_property (GObject *object,
GValue *value, GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
static void gimp_canvas_realize (GtkWidget *widget);
static void gimp_canvas_unrealize (GtkWidget *widget); static void gimp_canvas_unrealize (GtkWidget *widget);
static void gimp_canvas_style_set (GtkWidget *widget, static void gimp_canvas_style_set (GtkWidget *widget,
GtkStyle *prev_style); GtkStyle *prev_style);
@ -76,91 +75,6 @@ G_DEFINE_TYPE (GimpCanvas, gimp_canvas, GIMP_TYPE_OVERLAY_BOX)
#define parent_class gimp_canvas_parent_class #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 static void
gimp_canvas_class_init (GimpCanvasClass *klass) 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->set_property = gimp_canvas_set_property;
object_class->get_property = gimp_canvas_get_property; object_class->get_property = gimp_canvas_get_property;
widget_class->realize = gimp_canvas_realize;
widget_class->unrealize = gimp_canvas_unrealize; widget_class->unrealize = gimp_canvas_unrealize;
widget_class->style_set = gimp_canvas_style_set; widget_class->style_set = gimp_canvas_style_set;
widget_class->focus_in_event = gimp_canvas_focus_in_event; 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++) for (i = 0; i < GIMP_CANVAS_NUM_STYLES; i++)
canvas->gc[i] = NULL; canvas->gc[i] = NULL;
for (i = 0; i < GIMP_CANVAS_NUM_STIPPLES; i++)
canvas->stipple[i] = NULL;
} }
static void 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 static void
gimp_canvas_unrealize (GtkWidget *widget) 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) if (canvas->layout)
{ {
g_object_unref (canvas->layout); g_object_unref (canvas->layout);
@ -369,10 +258,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas,
switch (style) switch (style)
{ {
case GIMP_CANVAS_STYLE_BLACK:
case GIMP_CANVAS_STYLE_WHITE:
break;
case GIMP_CANVAS_STYLE_RENDER: case GIMP_CANVAS_STYLE_RENDER:
mask |= GDK_GC_EXPOSURES; mask |= GDK_GC_EXPOSURES;
values.graphics_exposures = TRUE; values.graphics_exposures = TRUE;
@ -396,14 +281,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas,
values.join_style = GDK_JOIN_MITER; values.join_style = GDK_JOIN_MITER;
break; 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: default:
return NULL; return NULL;
} }
@ -426,33 +303,6 @@ gimp_canvas_gc_new (GimpCanvas *canvas,
case GIMP_CANVAS_STYLE_XOR_DASHED: case GIMP_CANVAS_STYLE_XOR_DASHED:
case GIMP_CANVAS_STYLE_XOR: case GIMP_CANVAS_STYLE_XOR:
break; 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); 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); 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: * gimp_canvas_set_bg_color:
* @canvas: a #GimpCanvas widget * @canvas: a #GimpCanvas widget

View File

@ -24,14 +24,10 @@
typedef enum typedef enum
{ {
GIMP_CANVAS_STYLE_BLACK,
GIMP_CANVAS_STYLE_WHITE,
GIMP_CANVAS_STYLE_RENDER, GIMP_CANVAS_STYLE_RENDER,
GIMP_CANVAS_STYLE_XOR, GIMP_CANVAS_STYLE_XOR,
GIMP_CANVAS_STYLE_XOR_DASHED, GIMP_CANVAS_STYLE_XOR_DASHED,
GIMP_CANVAS_STYLE_XOR_DOTTED, GIMP_CANVAS_STYLE_XOR_DOTTED,
GIMP_CANVAS_STYLE_SELECTION_IN,
GIMP_CANVAS_STYLE_SELECTION_OUT,
GIMP_CANVAS_NUM_STYLES GIMP_CANVAS_NUM_STYLES
} GimpCanvasStyle; } GimpCanvasStyle;
@ -147,9 +143,6 @@ void gimp_canvas_set_clip_rect (GimpCanvas *canvas,
void gimp_canvas_set_clip_region (GimpCanvas *canvas, void gimp_canvas_set_clip_region (GimpCanvas *canvas,
GimpCanvasStyle style, GimpCanvasStyle style,
const GdkRegion *region); const GdkRegion *region);
void gimp_canvas_set_stipple_index (GimpCanvas *canvas,
GimpCanvasStyle style,
guint index);
void gimp_canvas_set_bg_color (GimpCanvas *canvas, void gimp_canvas_set_bg_color (GimpCanvas *canvas,
GimpRGB *color); GimpRGB *color);

View File

@ -598,11 +598,10 @@ gimp_display_shell_draw_selection_out (GimpDisplayShell *shell,
} }
void void
gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, gimp_display_shell_draw_selection_segments (GimpDisplayShell *shell,
cairo_t *cr, cairo_t *cr,
GdkSegment *segs, GdkSegment *segs,
gint n_segs, gint n_segs)
gint index)
{ {
gint i; gint i;
@ -610,7 +609,7 @@ gimp_display_shell_draw_selection_in (GimpDisplayShell *shell,
g_return_if_fail (cr != NULL); g_return_if_fail (cr != NULL);
g_return_if_fail (segs != NULL && n_segs > 0); 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++) for (i = 0; i < n_segs; i++)
{ {
@ -629,6 +628,21 @@ gimp_display_shell_draw_selection_in (GimpDisplayShell *shell,
cairo_stroke (cr); 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 void
gimp_display_shell_draw_vector (GimpDisplayShell *shell, gimp_display_shell_draw_vector (GimpDisplayShell *shell,
GimpVectors *vectors) GimpVectors *vectors)

View File

@ -57,10 +57,13 @@ void gimp_display_shell_draw_selection_out (GimpDisplayShell *shell,
cairo_t *cr, cairo_t *cr,
GdkSegment *segs, GdkSegment *segs,
gint n_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, cairo_t *cr,
GdkSegment *segs, 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); gint index);
void gimp_display_shell_draw_vector (GimpDisplayShell *shell, void gimp_display_shell_draw_vector (GimpDisplayShell *shell,
GimpVectors *vectors); GimpVectors *vectors);

View File

@ -41,18 +41,10 @@
#include "gimpdisplayshell-transform.h" #include "gimpdisplayshell-transform.h"
#undef VERBOSE
#define MAX_POINTS_INC 2048
#define USE_DRAWPOINTS TRUE
struct _Selection struct _Selection
{ {
GimpDisplayShell *shell; /* shell that owns the selection */ GimpDisplayShell *shell; /* shell that owns the selection */
gboolean use_cairo; /* temp hack */
GdkSegment *segs_in; /* gdk segments of area boundary */ GdkSegment *segs_in; /* gdk segments of area boundary */
gint n_segs_in; /* number of segments in segs_in */ gint n_segs_in; /* number of segments in segs_in */
@ -68,8 +60,7 @@ struct _Selection
gboolean hidden; /* is the selection hidden? */ gboolean hidden; /* is the selection hidden? */
gboolean layer_hidden; /* is the layer boundary hidden? */ gboolean layer_hidden; /* is the layer boundary hidden? */
guint timeout; /* timer for successive draws */ guint timeout; /* timer for successive draws */
GdkPoint *points_in[8]; /* points of segs_in for fast ants */ cairo_pattern_t *segs_in_mask; /* cache for rendered segments */
gint num_points_in[8]; /* number of points in points_in */
}; };
@ -86,12 +77,7 @@ static void selection_undraw (Selection *selection);
static void selection_layer_undraw (Selection *selection); static void selection_layer_undraw (Selection *selection);
static void selection_layer_draw (Selection *selection); static void selection_layer_draw (Selection *selection);
static void selection_add_point (GdkPoint *points[8], static void selection_render_mask (Selection *selection);
gint max_npoints[8],
gint npoints[8],
gint x,
gint y);
static void selection_render_points (Selection *selection);
static void selection_transform_segs (Selection *selection, static void selection_transform_segs (Selection *selection,
const BoundSeg *src_segs, const BoundSeg *src_segs,
@ -124,7 +110,6 @@ gimp_display_shell_selection_init (GimpDisplayShell *shell)
selection = g_slice_new0 (Selection); selection = g_slice_new0 (Selection);
selection->shell = shell; selection->shell = shell;
selection->use_cairo = g_getenv ("CAIRO_SELECTION") != NULL;
selection->visible = TRUE; selection->visible = TRUE;
selection->hidden = ! gimp_display_shell_get_show_selection (shell); selection->hidden = ! gimp_display_shell_get_show_selection (shell);
selection->layer_hidden = ! gimp_display_shell_get_show_layer (shell); selection->layer_hidden = ! gimp_display_shell_get_show_layer (shell);
@ -298,105 +283,21 @@ selection_resume (Selection *selection)
selection_start (selection); selection_start (selection);
} }
/* #define BENCHMARK 1 */
static void static void
selection_draw (Selection *selection) 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++)
{
#endif /* BENCHMARK */
if (selection->use_cairo)
{
if (selection->segs_in) 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, gimp_display_shell_draw_selection_in_mask (selection->shell, cr,
selection->segs_in, selection->segs_in_mask,
selection->n_segs_in,
selection->index % 8); 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 static void
@ -474,137 +375,21 @@ selection_layer_undraw (Selection *selection)
} }
static void static void
selection_add_point (GdkPoint *points[8], selection_render_mask (Selection *selection)
gint max_npoints[8],
gint npoints[8],
gint x,
gint y)
{ {
gint i, j; cairo_t *cr;
j = (x - y) & 7; cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas));
i = npoints[j]++; cairo_push_group_with_content (cr, CAIRO_CONTENT_ALPHA);
if (i == max_npoints[j])
{
max_npoints[j] += 2048;
points[j] = g_realloc (points[j], sizeof (GdkPoint) * max_npoints[j]);
}
points[j][i].x = x; gimp_display_shell_draw_selection_segments (selection->shell, cr,
points[j][i].y = y; selection->segs_in,
} selection->n_segs_in);
selection->segs_in_mask = cairo_pop_group (cr);
/* Render the segs_in array into points_in */ cairo_destroy (cr);
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);
}
}
} }
static void static void
@ -672,9 +457,7 @@ selection_generate_segs (Selection *selection)
selection_transform_segs (selection, segs_in, selection_transform_segs (selection, segs_in,
selection->segs_in, selection->n_segs_in); selection->segs_in, selection->n_segs_in);
#ifdef USE_DRAWPOINTS selection_render_mask (selection);
selection_render_points (selection);
#endif
} }
else else
{ {
@ -718,8 +501,6 @@ selection_generate_segs (Selection *selection)
static void static void
selection_free_segs (Selection *selection) selection_free_segs (Selection *selection)
{ {
gint j;
if (selection->segs_in) if (selection->segs_in)
{ {
g_free (selection->segs_in); g_free (selection->segs_in);
@ -741,14 +522,10 @@ selection_free_segs (Selection *selection)
selection->n_segs_layer = 0; selection->n_segs_layer = 0;
} }
for (j = 0; j < 8; j++) if (selection->segs_in_mask)
{ {
if (selection->points_in[j]) cairo_pattern_destroy (selection->segs_in_mask);
{ selection->segs_in_mask = NULL;
g_free (selection->points_in[j]);
selection->points_in[j] = NULL;
selection->num_points_in[j] = 0;
}
} }
} }
@ -771,14 +548,11 @@ selection_start_timeout (Selection *selection)
/* Draw the ants */ /* Draw the ants */
if (! selection->hidden) if (! selection->hidden)
{ {
GimpCanvas *canvas = GIMP_CANVAS (selection->shell->canvas);
GimpDisplayConfig *config = selection->shell->display->config; GimpDisplayConfig *config = selection->shell->display->config;
selection_draw (selection); selection_draw (selection);
if (selection->segs_out) if (selection->segs_out)
{
if (selection->use_cairo)
{ {
cairo_t *cr; cairo_t *cr;
@ -790,13 +564,6 @@ selection_start_timeout (Selection *selection)
cairo_destroy (cr); cairo_destroy (cr);
} }
else
{
gimp_canvas_draw_segments (canvas, GIMP_CANVAS_STYLE_SELECTION_OUT,
selection->segs_out,
selection->n_segs_out);
}
}
if (selection->segs_in && selection->visible) if (selection->segs_in && selection->visible)
selection->timeout = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, selection->timeout = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,