app: make the airbrush tool thread-safe w.r.t. paint thread

GimpAirbrush currently performs painting and flushes the image on
its own during the airbrush timeout.  This is unsafe w.r.t. the
paint thread, since the timeout is run on the main thread, while
paint commands should run on the paint thread.

Add a "timeout" signal to GimpAirbrush, and simply emit this signal
during the airbrush timeout, rather than actually painting.

Connect to this signal in GimpAirbrushTool, and use
gimppaintool-paint to perform the actual painting, in a thread-safe
manner (see the previous commit.)
This commit is contained in:
Ell
2018-04-16 09:49:15 -04:00
parent e02a339e01
commit ddfc7715cb
3 changed files with 57 additions and 24 deletions

View File

@ -36,6 +36,13 @@
#include "gimp-intl.h" #include "gimp-intl.h"
enum
{
TIMEOUT,
LAST_SIGNAL
};
static void gimp_airbrush_finalize (GObject *object); static void gimp_airbrush_finalize (GObject *object);
static void gimp_airbrush_paint (GimpPaintCore *paint_core, static void gimp_airbrush_paint (GimpPaintCore *paint_core,
@ -56,6 +63,8 @@ G_DEFINE_TYPE (GimpAirbrush, gimp_airbrush, GIMP_TYPE_PAINTBRUSH)
#define parent_class gimp_airbrush_parent_class #define parent_class gimp_airbrush_parent_class
static guint airbrush_signals[LAST_SIGNAL] = { 0 };
void void
gimp_airbrush_register (Gimp *gimp, gimp_airbrush_register (Gimp *gimp,
@ -78,6 +87,15 @@ gimp_airbrush_class_init (GimpAirbrushClass *klass)
object_class->finalize = gimp_airbrush_finalize; object_class->finalize = gimp_airbrush_finalize;
paint_core_class->paint = gimp_airbrush_paint; paint_core_class->paint = gimp_airbrush_paint;
airbrush_signals[TIMEOUT] =
g_signal_new ("timeout",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpAirbrushClass, timeout),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
} }
static void static void
@ -96,8 +114,6 @@ gimp_airbrush_finalize (GObject *object)
airbrush->timeout_id = 0; airbrush->timeout_id = 0;
} }
g_clear_object (&airbrush->sym);
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
@ -142,13 +158,6 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
fade_point = gimp_paint_options_get_fade (paint_options, image, fade_point = gimp_paint_options_get_fade (paint_options, image,
paint_core->pixel_dist); paint_core->pixel_dist);
airbrush->drawable = drawable;
airbrush->paint_options = paint_options;
if (airbrush->sym)
g_object_unref (airbrush->sym);
airbrush->sym = g_object_ref (sym);
/* Base our timeout on the original stroke. */ /* Base our timeout on the original stroke. */
coords = gimp_symmetry_get_origin (sym); coords = gimp_symmetry_get_origin (sym);
@ -171,8 +180,6 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
paint_options, paint_options,
sym, sym,
paint_state, time); paint_state, time);
g_clear_object (&airbrush->sym);
break; break;
} }
} }
@ -212,13 +219,9 @@ gimp_airbrush_timeout (gpointer data)
{ {
GimpAirbrush *airbrush = GIMP_AIRBRUSH (data); GimpAirbrush *airbrush = GIMP_AIRBRUSH (data);
gimp_airbrush_paint (GIMP_PAINT_CORE (airbrush), airbrush->timeout_id = 0;
airbrush->drawable,
airbrush->paint_options,
airbrush->sym,
GIMP_PAINT_STATE_MOTION, 0);
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (airbrush->drawable))); g_signal_emit (airbrush, airbrush_signals[TIMEOUT], 0);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }

View File

@ -34,18 +34,17 @@ typedef struct _GimpAirbrushClass GimpAirbrushClass;
struct _GimpAirbrush struct _GimpAirbrush
{ {
GimpPaintbrush parent_instance; GimpPaintbrush parent_instance;
guint timeout_id; guint timeout_id;
GimpSymmetry *sym;
GimpDrawable *drawable;
GimpPaintOptions *paint_options;
}; };
struct _GimpAirbrushClass struct _GimpAirbrushClass
{ {
GimpPaintbrushClass parent_class; GimpPaintbrushClass parent_class;
/* signals */
void (* timeout) (GimpAirbrush *airbrush);
}; };

View File

@ -31,16 +31,24 @@
#include "gimpairbrushtool.h" #include "gimpairbrushtool.h"
#include "gimppaintoptions-gui.h" #include "gimppaintoptions-gui.h"
#include "gimppainttool-paint.h"
#include "gimptoolcontrol.h" #include "gimptoolcontrol.h"
#include "gimp-intl.h" #include "gimp-intl.h"
static GtkWidget * gimp_airbrush_options_gui (GimpToolOptions *tool_options); static void gimp_airbrush_tool_constructed (GObject *object);
static void gimp_airbrush_tool_airbrush_timeout (GimpAirbrush *airbrush,
GimpAirbrushTool *airbrush_tool);
static GtkWidget * gimp_airbrush_options_gui (GimpToolOptions *tool_options);
G_DEFINE_TYPE (GimpAirbrushTool, gimp_airbrush_tool, GIMP_TYPE_PAINTBRUSH_TOOL) G_DEFINE_TYPE (GimpAirbrushTool, gimp_airbrush_tool, GIMP_TYPE_PAINTBRUSH_TOOL)
#define parent_class gimp_airbrush_tool_parent_class
void void
gimp_airbrush_tool_register (GimpToolRegisterCallback callback, gimp_airbrush_tool_register (GimpToolRegisterCallback callback,
@ -63,6 +71,9 @@ gimp_airbrush_tool_register (GimpToolRegisterCallback callback,
static void static void
gimp_airbrush_tool_class_init (GimpAirbrushToolClass *klass) gimp_airbrush_tool_class_init (GimpAirbrushToolClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_airbrush_tool_constructed;
} }
static void static void
@ -73,6 +84,26 @@ gimp_airbrush_tool_init (GimpAirbrushTool *airbrush)
gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_AIRBRUSH); gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_AIRBRUSH);
} }
static void
gimp_airbrush_tool_constructed (GObject *object)
{
GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
g_signal_connect_object (paint_tool->core, "timeout",
G_CALLBACK (gimp_airbrush_tool_airbrush_timeout),
object, 0);
}
static void
gimp_airbrush_tool_airbrush_timeout (GimpAirbrush *airbrush,
GimpAirbrushTool *airbrush_tool)
{
gimp_paint_tool_paint_core_paint (GIMP_PAINT_TOOL (airbrush_tool),
GIMP_PAINT_STATE_MOTION, 0);
}
/* tool options stuff */ /* tool options stuff */