Bug 795081 - Crash when using a brush combined with a dynamics

In GimpPaintTool, brush outline generation took place during
gimp_paint_tool_draw() even while painting.  This function is run
concurrently with the paint thread.  When using dynamics, this
introduced a race conidition between updating the brush mask in the
paint thread, and updating the brush boundary in the main thread.

Move brush outline generation during painting to
gimppainttool-paint.c, and perform it in the display-update
timeout, while the main thread and the paint thread are
synchronized.
This commit is contained in:
Ell 2018-04-09 14:25:57 -04:00
parent e98506b000
commit f5cb1fed85
5 changed files with 89 additions and 27 deletions

View File

@ -1669,3 +1669,11 @@ gimp_drawable_flush_paint (GimpDrawable *drawable)
return FALSE;
}
gboolean
gimp_drawable_is_painting (GimpDrawable *drawable)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
return drawable->private->paint_count > 0;
}

View File

@ -231,6 +231,7 @@ const guchar * gimp_drawable_get_colormap (GimpDrawable *drawable)
void gimp_drawable_start_paint (GimpDrawable *drawable);
gboolean gimp_drawable_end_paint (GimpDrawable *drawable);
gboolean gimp_drawable_flush_paint (GimpDrawable *drawable);
gboolean gimp_drawable_is_painting (GimpDrawable *drawable);
#endif /* __GIMP_DRAWABLE_H__ */

View File

@ -68,10 +68,12 @@ typedef struct
/* local function prototypes */
static gboolean gimp_paint_tool_paint_use_thread (GimpPaintTool *paint_tool);
static gpointer gimp_paint_tool_paint_thread (gpointer data);
static gboolean gimp_paint_tool_paint_use_thread (GimpPaintTool *paint_tool);
static gpointer gimp_paint_tool_paint_thread (gpointer data);
static gboolean gimp_paint_tool_paint_timeout (GimpPaintTool *paint_tool);
static gboolean gimp_paint_tool_paint_timeout (GimpPaintTool *paint_tool);
static void gimp_paint_tool_paint_update_outline (GimpPaintTool *paint_tool);
/* static variables */
@ -180,6 +182,9 @@ gimp_paint_tool_paint_timeout (GimpPaintTool *paint_tool)
update = gimp_drawable_flush_paint (drawable);
if (update)
gimp_paint_tool_paint_update_outline (paint_tool);
paint_timeout_pending = FALSE;
g_cond_signal (&paint_cond);
@ -202,6 +207,36 @@ gimp_paint_tool_paint_timeout (GimpPaintTool *paint_tool)
return G_SOURCE_CONTINUE;
}
static void
gimp_paint_tool_paint_update_outline (GimpPaintTool *paint_tool)
{
if (gimp_paint_tool_paint_use_thread (paint_tool))
{
gimp_paint_tool_set_draw_fallback (paint_tool, FALSE, 0.0);
if (paint_tool->draw_brush)
{
GimpPaintCore *core = paint_tool->core;
GimpDisplay *display = paint_tool->display;
GimpDrawable *drawable = paint_tool->drawable;
gint off_x, off_y;
gdouble x, y;
gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
x = core->cur_coords.x + off_x;
y = core->cur_coords.y + off_y;
if (paint_tool->outline)
g_object_unref (paint_tool->outline);
paint_tool->outline =
GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->get_outline (paint_tool,
display, x, y);
}
}
}
/* public functions */
@ -303,6 +338,9 @@ gimp_paint_tool_paint_start (GimpPaintTool *paint_tool,
GIMP_PAINT_STATE_MOTION, time);
}
/* Update the brush outline */
gimp_paint_tool_paint_update_outline (paint_tool);
gimp_projection_flush_now (gimp_image_get_projection (image));
gimp_display_flush_now (display);
@ -384,6 +422,9 @@ gimp_paint_tool_paint_end (GimpPaintTool *paint_tool,
else
gimp_paint_core_finish (core, drawable, TRUE);
/* Clear the brush outline */
g_clear_object (&paint_tool->outline);
/* Exit paint mode */
if (gimp_paint_tool_paint_use_thread (paint_tool))
gimp_drawable_end_paint (drawable);

View File

@ -672,12 +672,9 @@ gimp_paint_tool_draw (GimpDrawTool *draw_tool)
line_drawn = TRUE;
}
gimp_paint_tool_set_draw_fallback (paint_tool, FALSE, 0.0);
if (paint_tool->draw_brush)
outline = gimp_paint_tool_get_outline (paint_tool,
draw_tool->display,
cur_x, cur_y);
outline = gimp_paint_tool_get_outline (paint_tool,
draw_tool->display,
cur_x, cur_y);
if (outline)
{
@ -770,9 +767,22 @@ gimp_paint_tool_get_outline (GimpPaintTool *paint_tool,
gdouble x,
gdouble y)
{
if (GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->get_outline)
return GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->get_outline (paint_tool,
display, x, y);
if (paint_tool->drawable && gimp_drawable_is_painting (paint_tool->drawable))
{
if (paint_tool->outline)
return g_object_ref (paint_tool->outline);
}
else
{
gimp_paint_tool_set_draw_fallback (paint_tool, FALSE, 0.0);
if (paint_tool->draw_brush &&
GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->get_outline)
{
return GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->get_outline (
paint_tool, display, x, y);
}
}
return NULL;
}

View File

@ -39,26 +39,28 @@ typedef struct _GimpPaintToolClass GimpPaintToolClass;
struct _GimpPaintTool
{
GimpColorTool parent_instance;
GimpColorTool parent_instance;
gboolean pick_colors; /* pick color if ctrl is pressed */
gboolean draw_line;
gboolean pick_colors; /* pick color if ctrl is pressed */
gboolean draw_line;
gboolean show_cursor;
gboolean draw_brush;
gboolean draw_fallback;
gint fallback_size;
gboolean draw_circle;
gint circle_size;
gboolean show_cursor;
gboolean draw_brush;
gboolean draw_fallback;
gint fallback_size;
gboolean draw_circle;
gint circle_size;
const gchar *status; /* status message */
const gchar *status_line; /* status message when drawing a line */
const gchar *status_ctrl; /* additional message for the ctrl modifier */
const gchar *status; /* status message */
const gchar *status_line; /* status message when drawing a line */
const gchar *status_ctrl; /* additional message for the ctrl modifier */
GimpPaintCore *core;
GimpPaintCore *core;
GimpDisplay *display;
GimpDrawable *drawable;
GimpDisplay *display;
GimpDrawable *drawable;
GimpCanvasItem *outline;
};
struct _GimpPaintToolClass