[gimp/gimp-2-10] app: add gimp_pickable_contiguous_region_prepare_line_art_async() ...



commit 3c0f1307cce5b204ff22d2ff5cf5479f9cc0db52
Author: Ell <ell_se yahoo com>
Date:   Mon Nov 19 14:48:26 2018 -0500

    app: add gimp_pickable_contiguous_region_prepare_line_art_async() ...
    
    ... and use in bucket-fill tool
    
    Add gimp_pickable_contiguous_region_prepare_line_art_async(), which
    computes a line-art asynchronously, and use it in the bucket-fill
    tool, instead of having the tool create the async op.
    
    This allows the async to keep running even after the pickable dies,
    since we only need the pickable's buffer, and not the pickable
    itself.  Previously, we reffed the pickable for the duration of the
    async, but we could still segfault when unreffing it, if the
    pickable was a drawable, and its parent image had already died.
    
    Furthermore, let the async work on a copy of the pickable's buffer,
    rather than the pickable's buffer directly.  This avoids some race
    conditions when the pickable is the image (i.e., when "sample
    merged" is active), since then we're using image projection's
    buffer, which is generally unsafe to use in different threads
    concurrently.
    
    Also, s/! has_alpha/has_alpha/ when looking for transparent pixels,
    and quit early, at least during this stage, if the async in
    canceled.
    
    (cherry picked from commit b4e12fbbbba8d607abdbe353e424c8386d2a3f11)

 app/core/gimppickable-contiguous-region.c | 140 +++++++++++++++++++++++++-----
 app/core/gimppickable-contiguous-region.h |  43 +++++----
 app/tools/gimpbucketfilltool.c            |  60 +++----------
 3 files changed, 155 insertions(+), 88 deletions(-)
---
diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.c
index a65651373b..a4dbe69d9e 100644
--- a/app/core/gimppickable-contiguous-region.c
+++ b/app/core/gimppickable-contiguous-region.c
@@ -30,12 +30,22 @@
 
 #include "gegl/gimp-babl.h"
 
+#include "gimp-parallel.h"
 #include "gimp-utils.h" /* GIMP_TIMER */
+#include "gimpasync.h"
 #include "gimplineart.h"
 #include "gimppickable.h"
 #include "gimppickable-contiguous-region.h"
 
 
+typedef struct
+{
+  GeglBuffer *buffer;
+  gboolean    select_transparent;
+  gfloat      stroke_threshold;
+} LineArtData;
+
+
 /*  local function prototypes  */
 
 static const Babl * choose_format         (GeglBuffer          *buffer,
@@ -95,41 +105,52 @@ static void     find_contiguous_region    (GeglBuffer          *src_buffer,
                                            gint                 y,
                                            const gfloat        *col);
 
+static LineArtData * line_art_data_new    (GeglBuffer          *buffer,
+                                           gboolean             select_transparent,
+                                           gfloat               stroke_threshold);
+static void          line_art_data_free   (LineArtData         *data);
+
 
 /*  public functions  */
 
-GeglBuffer *
-gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
-                                                  gboolean      select_transparent,
-                                                  gfloat        stroke_threshold)
+static void
+gimp_pickable_contiguous_region_prepare_line_art_async_func (GimpAsync   *async,
+                                                             LineArtData *data)
 {
   GeglBuffer *lineart;
   gboolean    has_alpha;
+  gboolean    select_transparent = FALSE;
 
-  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
-
-  gimp_pickable_flush (pickable);
-
-  lineart = gimp_pickable_get_buffer (pickable);
-  has_alpha = babl_format_has_alpha (gegl_buffer_get_format (lineart));
+  has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
 
-  if (! has_alpha)
+  if (has_alpha)
     {
-      if (select_transparent)
+      if (data->select_transparent)
         {
           /*  don't select transparent regions if there are no fully
            *  transparent pixels.
            */
           GeglBufferIterator *gi;
 
-          select_transparent = FALSE;
-          gi = gegl_buffer_iterator_new (lineart, NULL, 0, babl_format ("A u8"),
+          gi = gegl_buffer_iterator_new (data->buffer, NULL, 0,
+                                         babl_format ("A u8"),
                                          GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3);
           while (gegl_buffer_iterator_next (gi))
             {
               guint8 *p = (guint8*) gi->items[0].data;
               gint    k;
 
+              if (gimp_async_is_canceled (async))
+                {
+                  gegl_buffer_iterator_stop (gi);
+
+                  gimp_async_abort (async);
+
+                  line_art_data_free (data);
+
+                  return;
+                }
+
               for (k = 0; k < gi->length; k++)
                 {
                   if (! *p)
@@ -146,10 +167,6 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
             gegl_buffer_iterator_stop (gi);
         }
     }
-  else
-    {
-      select_transparent = FALSE;
-    }
 
   /* For smart selection, we generate a binarized image with close
    * regions, then run a composite selection with no threshold on
@@ -157,9 +174,9 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
    */
   GIMP_TIMER_START();
 
-  lineart = gimp_lineart_close (lineart,
+  lineart = gimp_lineart_close (data->buffer,
                                 select_transparent,
-                                stroke_threshold,
+                                data->stroke_threshold,
                                 /*minimal_lineart_area,*/
                                 5,
                                 /*normal_estimate_mask_size,*/
@@ -187,9 +204,70 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
 
   GIMP_TIMER_END("close line-art");
 
+  gimp_async_finish_full (async, lineart, g_object_unref);
+
+  line_art_data_free (data);
+}
+
+GeglBuffer *
+gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
+                                                  gboolean      select_transparent,
+                                                  gfloat        stroke_threshold)
+{
+  GimpAsync   *async;
+  LineArtData *data;
+  GeglBuffer  *lineart;
+
+  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
+
+  gimp_pickable_flush (pickable);
+
+  async = gimp_async_new ();
+  data  = line_art_data_new (gimp_pickable_get_buffer (pickable),
+                             select_transparent,
+                             stroke_threshold);
+
+  gimp_pickable_contiguous_region_prepare_line_art_async_func (async, data);
+
+  lineart = g_object_ref (gimp_async_get_result (async));
+
+  g_object_unref (async);
+
   return lineart;
 }
 
+GimpAsync *
+gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
+                                                        gboolean      select_transparent,
+                                                        gfloat        stroke_threshold,
+                                                        gint          priority)
+{
+  GeglBuffer  *buffer;
+  GimpAsync   *async;
+  LineArtData *data;
+
+  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
+
+  gimp_pickable_flush (pickable);
+
+  buffer = gegl_buffer_dup (gimp_pickable_get_buffer (pickable));
+
+  data  = line_art_data_new (buffer,
+                             select_transparent,
+                             stroke_threshold);
+
+  g_object_unref (buffer);
+
+  async = gimp_parallel_run_async_full (
+    priority,
+    (GimpParallelRunAsyncFunc)
+      gimp_pickable_contiguous_region_prepare_line_art_async_func,
+    data,
+    (GDestroyNotify) line_art_data_free);
+
+  return async;
+}
+
 GeglBuffer *
 gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
                                          GeglBuffer          *line_art,
@@ -924,3 +1002,25 @@ find_contiguous_region (GeglBuffer          *src_buffer,
   g_free (row);
 #endif
 }
+
+static LineArtData *
+line_art_data_new (GeglBuffer *buffer,
+                   gboolean    select_transparent,
+                   gfloat      stroke_threshold)
+{
+  LineArtData *data = g_slice_new (LineArtData);
+
+  data->buffer             = g_object_ref (buffer);
+  data->select_transparent = select_transparent;
+  data->stroke_threshold   = stroke_threshold;
+
+  return data;
+}
+
+static void
+line_art_data_free (LineArtData *data)
+{
+  g_object_unref (data->buffer);
+
+  g_slice_free (LineArtData, data);
+}
diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h
index b73f9aa1c4..95543a59e5 100644
--- a/app/core/gimppickable-contiguous-region.h
+++ b/app/core/gimppickable-contiguous-region.h
@@ -19,26 +19,31 @@
 #define __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
 
 
-GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art (GimpPickable        *pickable,
-                                                               gboolean             select_transparent,
-                                                               gfloat               stroke_threshold);
-GeglBuffer * gimp_pickable_contiguous_region_by_seed          (GimpPickable        *pickable,
-                                                               GeglBuffer          *line_art,
-                                                               gboolean             antialias,
-                                                               gfloat               threshold,
-                                                               gboolean             select_transparent,
-                                                               GimpSelectCriterion  select_criterion,
-                                                               gboolean             diagonal_neighbors,
-                                                               gfloat               stroke_threshold,
-                                                               gint                 x,
-                                                               gint                 y);
+GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art       (GimpPickable        *pickable,
+                                                                     gboolean             select_transparent,
+                                                                     gfloat               stroke_threshold);
+GimpAsync  * gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable        *pickable,
+                                                                     gboolean             select_transparent,
+                                                                     gfloat               stroke_threshold,
+                                                                     gint                 priority);
 
-GeglBuffer * gimp_pickable_contiguous_region_by_color         (GimpPickable        *pickable,
-                                                               gboolean             antialias,
-                                                               gfloat               threshold,
-                                                               gboolean             select_transparent,
-                                                               GimpSelectCriterion  select_criterion,
-                                                               const GimpRGB       *color);
+GeglBuffer * gimp_pickable_contiguous_region_by_seed                (GimpPickable        *pickable,
+                                                                     GeglBuffer          *line_art,
+                                                                     gboolean             antialias,
+                                                                     gfloat               threshold,
+                                                                     gboolean             select_transparent,
+                                                                     GimpSelectCriterion  select_criterion,
+                                                                     gboolean             diagonal_neighbors,
+                                                                     gfloat               stroke_threshold,
+                                                                     gint                 x,
+                                                                     gint                 y);
+
+GeglBuffer * gimp_pickable_contiguous_region_by_color               (GimpPickable        *pickable,
+                                                                     gboolean             antialias,
+                                                                     gfloat               threshold,
+                                                                     gboolean             select_transparent,
+                                                                     GimpSelectCriterion  select_criterion,
+                                                                     const GimpRGB       *color);
 
 
 #endif  /*  __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ */
diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c
index 63bd2f646a..a4484e5e71 100644
--- a/app/tools/gimpbucketfilltool.c
+++ b/app/tools/gimpbucketfilltool.c
@@ -35,7 +35,6 @@
 #include "core/gimpimage.h"
 #include "core/gimpitem.h"
 #include "core/gimplineart.h"
-#include "core/gimp-parallel.h"
 #include "core/gimppickable.h"
 #include "core/gimppickable-contiguous-region.h"
 #include "core/gimpprogress.h"
@@ -692,33 +691,6 @@ gimp_bucket_fill_tool_cursor_update (GimpTool         *tool,
   GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
 }
 
-typedef struct
-{
-  GimpPickable *pickable;
-  gboolean      fill_transparent;
-  gdouble       line_art_threshold;
-} PrecomputeData;
-
-static void
-precompute_data_free (PrecomputeData *data)
-{
-  g_object_unref (data->pickable);
-  g_slice_free (PrecomputeData, data);
-}
-
-static void
-gimp_bucket_fill_compute_line_art_async  (GimpAsync      *async,
-                                          PrecomputeData *data)
-{
-  GeglBuffer *line_art;
-
-  line_art = gimp_pickable_contiguous_region_prepare_line_art (data->pickable,
-                                                               data->fill_transparent,
-                                                               data->line_art_threshold);
-  precompute_data_free (data);
-  gimp_async_finish_full (async, line_art, g_object_unref);
-}
-
 static void
 gimp_bucket_fill_compute_line_art_cb (GimpAsync          *async,
                                       GimpBucketFillTool *tool)
@@ -757,36 +729,26 @@ gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool)
       GimpDrawable *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
 
       if (image && options->sample_merged)
-        {
-          pickable = GIMP_PICKABLE (image);
-          g_clear_object (&drawable);
-        }
+        pickable = GIMP_PICKABLE (image);
       else if (drawable && ! options->sample_merged)
-        {
-          pickable = GIMP_PICKABLE (drawable);
-          g_clear_object (&image);
-        }
-      else
-        {
-          g_clear_object (&image);
-          g_clear_object (&drawable);
-        }
+        pickable = GIMP_PICKABLE (drawable);
 
       if (pickable)
         {
-          PrecomputeData *data = g_slice_new (PrecomputeData);
+          tool->priv->async =
+            gimp_pickable_contiguous_region_prepare_line_art_async (
+              pickable,
+              options->fill_transparent,
+              options->line_art_threshold,
+              +1);
 
-          data->pickable           = pickable;
-          data->fill_transparent   = options->fill_transparent;
-          data->line_art_threshold = options->line_art_threshold;
-
-          tool->priv->async = gimp_parallel_run_async_full (1,
-                                                            (GimpParallelRunAsyncFunc) 
gimp_bucket_fill_compute_line_art_async,
-                                                            data, (GDestroyNotify) precompute_data_free);
           gimp_async_add_callback (tool->priv->async,
                                    (GimpAsyncCallback) gimp_bucket_fill_compute_line_art_cb,
                                    tool);
         }
+
+      g_clear_object (&image);
+      g_clear_object (&drawable);
     }
 }
 


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