[gimp] app: move painting to a separate thread



commit 8e7a34297fe993c1e8de9d85ecd61c73a1e4afd4
Author: Ell <ell_se yahoo com>
Date:   Sun Apr 8 09:28:23 2018 -0400

    app: move painting to a separate thread
    
    Add gimppainttool-paint.[ch], which takes care of painting during
    motion events in GimpPaintTool.  Perform the actual painting in a
    separate thread, so that display updates, which can have a
    significant synchronization overhead, don't stall painting.
    
    Allow specific paint tools to opt-out of a separate paint thread,
    and avoid it in GimpMybrushTool, since it doesn't seem to work.
    
    The separate paint thread can be explicitly disabled by setting the
    GIMP_NO_PAINT_THREAD environment variable.

 app/tools/Makefile.am           |    2 +
 app/tools/gimpmybrushtool.c     |    5 +-
 app/tools/gimppainttool-paint.c |  312 +++++++++++++++++++++++++++++++++++++++
 app/tools/gimppainttool-paint.h |   30 ++++
 app/tools/gimppainttool.c       |   19 ++-
 app/tools/gimppainttool.h       |    2 +
 6 files changed, 359 insertions(+), 11 deletions(-)
---
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index 4b33a4c..b4e3cbe 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -150,6 +150,8 @@ libapptools_a_sources = \
        gimppaintoptions-gui.h          \
        gimppainttool.c                 \
        gimppainttool.h                 \
+       gimppainttool-paint.c           \
+       gimppainttool-paint.h           \
        gimppenciltool.c                \
        gimppenciltool.h                \
        gimpperspectiveclonetool.c      \
diff --git a/app/tools/gimpmybrushtool.c b/app/tools/gimpmybrushtool.c
index 5203681..a6944e0 100644
--- a/app/tools/gimpmybrushtool.c
+++ b/app/tools/gimpmybrushtool.c
@@ -84,9 +84,10 @@ gimp_mybrush_tool_class_init (GimpMybrushToolClass *klass)
   GimpToolClass      *tool_class       = GIMP_TOOL_CLASS (klass);
   GimpPaintToolClass *paint_tool_class = GIMP_PAINT_TOOL_CLASS (klass);
 
-  tool_class->options_notify    = gimp_mybrush_tool_options_notify;
+  tool_class->options_notify         = gimp_mybrush_tool_options_notify;
 
-  paint_tool_class->get_outline = gimp_mybrush_tool_get_outline;
+  paint_tool_class->get_outline      = gimp_mybrush_tool_get_outline;
+  paint_tool_class->use_paint_thread = FALSE;
 }
 
 static void
diff --git a/app/tools/gimppainttool-paint.c b/app/tools/gimppainttool-paint.c
new file mode 100644
index 0000000..1242966
--- /dev/null
+++ b/app/tools/gimppainttool-paint.c
@@ -0,0 +1,312 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "tools-types.h"
+
+#include "core/gimpdrawable.h"
+#include "core/gimpimage.h"
+#include "core/gimpprojection.h"
+
+#include "paint/gimppaintcore.h"
+#include "paint/gimppaintoptions.h"
+
+#include "display/gimpdisplay.h"
+
+#include "gimppainttool.h"
+#include "gimppainttool-paint.h"
+
+
+#define DISPLAY_UPDATE_INTERVAL 10000 /* microseconds */
+
+
+typedef enum
+{
+  PAINT_ITEM_TYPE_INTERPOLATE,
+  PAINT_ITEM_TYPE_FINISH
+} PaintItemType;
+
+
+typedef struct
+{
+  PaintItemType      type;
+
+  union
+  {
+    struct
+    {
+      GimpPaintTool *paint_tool;
+      GimpCoords     coords;
+      guint32        time;
+    };
+
+    gboolean        *finished;
+  };
+} PaintItem;
+
+
+/*  local function prototypes  */
+
+static gboolean   gimp_paint_tool_paint_timeout    (GimpPaintTool *paint_tool);
+
+static gpointer   gimp_paint_tool_paint_thread     (gpointer       data);
+
+static gboolean   gimp_paint_tool_paint_use_thread (GimpPaintTool *paint_tool);
+
+
+/*  static variables  */
+
+static GThread           *paint_thread;
+
+static GMutex             paint_mutex;
+static GCond              paint_cond;
+
+static GQueue             paint_queue = G_QUEUE_INIT;
+static GMutex             paint_queue_mutex;
+static GCond              paint_queue_cond;
+
+static guint              paint_timeout_id;
+static volatile gboolean  paint_timeout_pending;
+
+
+/*  private functions  */
+
+
+static gboolean
+gimp_paint_tool_paint_timeout (GimpPaintTool *paint_tool)
+{
+  GimpTool *tool = GIMP_TOOL (paint_tool);
+  gboolean  update;
+
+  paint_timeout_pending = TRUE;
+
+  g_mutex_lock (&paint_mutex);
+
+  update = gimp_drawable_flush_paint (tool->drawable);
+
+  paint_timeout_pending = FALSE;
+  g_cond_signal (&paint_cond);
+
+  g_mutex_unlock (&paint_mutex);
+
+  if (update)
+    {
+      GimpDisplay *display = tool->display;
+      GimpImage   *image   = gimp_display_get_image (display);
+
+      gimp_draw_tool_pause (GIMP_DRAW_TOOL (paint_tool));
+
+      gimp_projection_flush_now (gimp_image_get_projection (image));
+      gimp_display_flush_now (display);
+
+      gimp_draw_tool_resume (GIMP_DRAW_TOOL (paint_tool));
+    }
+
+  return G_SOURCE_CONTINUE;
+}
+
+static gpointer
+gimp_paint_tool_paint_thread (gpointer data)
+{
+  g_mutex_lock (&paint_queue_mutex);
+
+  while (TRUE)
+    {
+      PaintItem *item;
+
+      while (! (item = g_queue_pop_head (&paint_queue)))
+        g_cond_wait (&paint_queue_cond, &paint_queue_mutex);
+
+      switch (item->type)
+        {
+        case PAINT_ITEM_TYPE_INTERPOLATE:
+          g_mutex_unlock (&paint_queue_mutex);
+          g_mutex_lock (&paint_mutex);
+
+          while (paint_timeout_pending)
+            g_cond_wait (&paint_cond, &paint_mutex);
+
+          gimp_paint_core_interpolate (
+            item->paint_tool->core,
+            GIMP_TOOL (item->paint_tool)->drawable,
+            GIMP_PAINT_TOOL_GET_OPTIONS (item->paint_tool),
+            &item->coords, item->time);
+
+          g_mutex_unlock (&paint_mutex);
+          g_mutex_lock (&paint_queue_mutex);
+
+          break;
+
+        case PAINT_ITEM_TYPE_FINISH:
+          *item->finished = TRUE;
+          g_cond_signal (&paint_queue_cond);
+
+          break;
+        }
+
+      g_slice_free (PaintItem, item);
+    }
+
+  g_mutex_unlock (&paint_queue_mutex);
+
+  return NULL;
+}
+
+static gboolean
+gimp_paint_tool_paint_use_thread (GimpPaintTool *paint_tool)
+{
+  if (GIMP_PAINT_TOOL_GET_CLASS (paint_tool)->use_paint_thread)
+    {
+      if (! paint_thread)
+        {
+          static gint use_paint_thread = -1;
+
+          if (use_paint_thread < 0)
+            use_paint_thread = g_getenv ("GIMP_NO_PAINT_THREAD") == NULL;
+
+          if (use_paint_thread)
+            {
+              paint_thread = g_thread_new ("paint",
+                                           gimp_paint_tool_paint_thread, NULL);
+            }
+        }
+
+      return paint_thread != NULL;
+    }
+
+  return FALSE;
+}
+
+
+/*  public functions  */
+
+
+void
+gimp_paint_tool_paint_start (GimpPaintTool *paint_tool)
+{
+  g_return_if_fail (GIMP_IS_PAINT_TOOL (paint_tool));
+
+  if (gimp_paint_tool_paint_use_thread (paint_tool))
+    {
+      gimp_drawable_start_paint (GIMP_TOOL (paint_tool)->drawable);
+
+      paint_timeout_id = g_timeout_add_full (
+        G_PRIORITY_HIGH_IDLE,
+        DISPLAY_UPDATE_INTERVAL / 1000,
+        (GSourceFunc) gimp_paint_tool_paint_timeout,
+        paint_tool, NULL);
+    }
+}
+
+void
+gimp_paint_tool_paint_end (GimpPaintTool *paint_tool)
+{
+  g_return_if_fail (GIMP_IS_PAINT_TOOL (paint_tool));
+
+  if (gimp_paint_tool_paint_use_thread (paint_tool))
+    {
+      PaintItem *item;
+      gboolean   finished = FALSE;
+      guint64    end_time;
+
+      g_source_remove (paint_timeout_id);
+      paint_timeout_id = 0;
+
+      item = g_slice_new (PaintItem);
+
+      item->type     = PAINT_ITEM_TYPE_FINISH;
+      item->finished = &finished;
+
+      g_mutex_lock (&paint_queue_mutex);
+
+      g_queue_push_tail (&paint_queue, item);
+      g_cond_signal (&paint_queue_cond);
+
+      end_time = g_get_monotonic_time () + DISPLAY_UPDATE_INTERVAL;
+
+      while (! finished)
+        {
+          if (! g_cond_wait_until (&paint_queue_cond, &paint_queue_mutex,
+                                   end_time))
+            {
+              g_mutex_unlock (&paint_queue_mutex);
+
+              gimp_paint_tool_paint_timeout (paint_tool);
+
+              g_mutex_lock (&paint_queue_mutex);
+
+              end_time = g_get_monotonic_time () + DISPLAY_UPDATE_INTERVAL;
+            }
+        }
+
+      g_mutex_unlock (&paint_queue_mutex);
+
+      gimp_drawable_end_paint (GIMP_TOOL (paint_tool)->drawable);
+    }
+}
+
+void
+gimp_paint_tool_paint_interpolate (GimpPaintTool    *paint_tool,
+                                   const GimpCoords *coords,
+                                   guint32           time)
+{
+  g_return_if_fail (GIMP_IS_PAINT_TOOL (paint_tool));
+  g_return_if_fail (coords != NULL);
+
+  if (gimp_paint_tool_paint_use_thread (paint_tool))
+    {
+      PaintItem *item;
+
+      item = g_slice_new (PaintItem);
+
+      item->type       = PAINT_ITEM_TYPE_INTERPOLATE;
+      item->paint_tool = paint_tool;
+      item->coords     = *coords;
+      item->time       = time;
+
+      g_mutex_lock (&paint_queue_mutex);
+
+      g_queue_push_tail (&paint_queue, item);
+      g_cond_signal (&paint_queue_cond);
+
+      g_mutex_unlock (&paint_queue_mutex);
+    }
+  else
+    {
+      GimpTool    *tool    = GIMP_TOOL (paint_tool);
+      GimpDisplay *display = tool->display;
+      GimpImage   *image   = gimp_display_get_image (display);
+
+      gimp_draw_tool_pause (GIMP_DRAW_TOOL (paint_tool));
+
+      gimp_paint_core_interpolate (paint_tool->core,
+                                   tool->drawable,
+                                   GIMP_PAINT_TOOL_GET_OPTIONS (paint_tool),
+                                   coords, time);
+
+      gimp_projection_flush_now (gimp_image_get_projection (image));
+      gimp_display_flush_now (display);
+
+      gimp_draw_tool_resume (GIMP_DRAW_TOOL (paint_tool));
+    }
+}
diff --git a/app/tools/gimppainttool-paint.h b/app/tools/gimppainttool-paint.h
new file mode 100644
index 0000000..05dfade
--- /dev/null
+++ b/app/tools/gimppainttool-paint.h
@@ -0,0 +1,30 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PAINT_TOOL_PAINT_H__
+#define __GIMP_PAINT_TOOL_PAINT_H__
+
+
+void   gimp_paint_tool_paint_start       (GimpPaintTool    *tool);
+void   gimp_paint_tool_paint_end         (GimpPaintTool    *tool);
+
+void   gimp_paint_tool_paint_interpolate (GimpPaintTool    *tool,
+                                          const GimpCoords *coords,
+                                          guint32           time);
+
+
+#endif  /*  __GIMP_PAINT_TOOL_PAINT_H__  */
diff --git a/app/tools/gimppainttool.c b/app/tools/gimppainttool.c
index a0f3f9d..02cd1a0 100644
--- a/app/tools/gimppainttool.c
+++ b/app/tools/gimppainttool.c
@@ -49,6 +49,7 @@
 
 #include "gimpcoloroptions.h"
 #include "gimppainttool.h"
+#include "gimppainttool-paint.h"
 #include "gimptoolcontrol.h"
 
 #include "gimp-intl.h"
@@ -132,6 +133,8 @@ gimp_paint_tool_class_init (GimpPaintToolClass *klass)
   tool_class->oper_update    = gimp_paint_tool_oper_update;
 
   draw_tool_class->draw      = gimp_paint_tool_draw;
+
+  klass->use_paint_thread    = TRUE;
 }
 
 static void
@@ -372,6 +375,9 @@ gimp_paint_tool_button_press (GimpTool            *tool,
   gimp_draw_tool_start (draw_tool, display);
 
   gimp_tool_control_activate (tool->control);
+
+  if (! paint_tool->draw_line)
+    gimp_paint_tool_paint_start (paint_tool);
 }
 
 static void
@@ -397,6 +403,9 @@ gimp_paint_tool_button_release (GimpTool              *tool,
       return;
     }
 
+  if (! paint_tool->draw_line)
+    gimp_paint_tool_paint_end (paint_tool);
+
   gimp_tool_control_halt (tool->control);
 
   gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
@@ -454,15 +463,7 @@ gimp_paint_tool_motion (GimpTool         *tool,
       return;
     }
 
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
-
-  gimp_paint_core_interpolate (core, drawable, paint_options,
-                               &curr_coords, time);
-
-  gimp_projection_flush_now (gimp_image_get_projection (image));
-  gimp_display_flush_now (display);
-
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+  gimp_paint_tool_paint_interpolate (paint_tool, &curr_coords, time);
 }
 
 static void
diff --git a/app/tools/gimppainttool.h b/app/tools/gimppainttool.h
index 9841771..1ca6b5e 100644
--- a/app/tools/gimppainttool.h
+++ b/app/tools/gimppainttool.h
@@ -66,6 +66,8 @@ struct _GimpPaintToolClass
                                     GimpDisplay   *display,
                                     gdouble        x,
                                     gdouble        y);
+
+  gboolean use_paint_thread;
 };
 
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]