[gimp/gimp-2-10] app: reorganize the line art code inside a GimpLineArt object.



commit a68524d43ba97ec8ba9a468c483cdf65ca67acdb
Author: Jehan <jehan girinstud io>
Date:   Sat Dec 1 18:33:51 2018 +0100

    app: reorganize the line art code inside a GimpLineArt object.
    
    The code was too much spread out, in core and tool code, and also it was
    made too specific to fill. I'll want to reuse this code at least in the
    fuzzy select tool. This will avoid code duplication, and also make this
    new process more self-contained and simpler to review later (the
    algorithm also has a lot of settings and it is much cleaner to have them
    as properties rather than passing these as parameters through many
    functions).
    
    The refactoring may not be finished; that's at least a first step.
    
    (cherry picked from commit db18c679f382f5e0647e8204760f95659ea96126)

 app/core/core-types.h                     |   1 +
 app/core/gimpchannel-select.c             |   3 +-
 app/core/gimpdrawable-bucket-fill.c       |  27 +-
 app/core/gimpdrawable-bucket-fill.h       |  14 +-
 app/core/gimplineart.c                    | 750 +++++++++++++++++++++++++++---
 app/core/gimplineart.h                    |  52 ++-
 app/core/gimppickable-contiguous-region.c | 272 +----------
 app/core/gimppickable-contiguous-region.h |  25 +-
 app/pdb/drawable-edit-cmds.c              |   3 +-
 app/tools/gimpbucketfilltool.c            | 295 ++++--------
 app/tools/gimpfuzzyselecttool.c           |   3 +-
 pdb/groups/drawable_edit.pdb              |   3 +-
 12 files changed, 826 insertions(+), 622 deletions(-)
---
diff --git a/app/core/core-types.h b/app/core/core-types.h
index aedd5e37f8..050ca8181e 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -187,6 +187,7 @@ typedef struct _GimpHistogram                   GimpHistogram;
 typedef struct _GimpIdTable                     GimpIdTable;
 typedef struct _GimpImagefile                   GimpImagefile;
 typedef struct _GimpInterpreterDB               GimpInterpreterDB;
+typedef struct _GimpLineArt                     GimpLineArt;
 typedef struct _GimpObjectQueue                 GimpObjectQueue;
 typedef struct _GimpParasiteList                GimpParasiteList;
 typedef struct _GimpPdbProgress                 GimpPdbProgress;
diff --git a/app/core/gimpchannel-select.c b/app/core/gimpchannel-select.c
index d23cadcb05..dd6c433aee 100644
--- a/app/core/gimpchannel-select.c
+++ b/app/core/gimpchannel-select.c
@@ -516,13 +516,12 @@ gimp_channel_select_fuzzy (GimpChannel         *channel,
     pickable = GIMP_PICKABLE (drawable);
 
   add_on = gimp_pickable_contiguous_region_by_seed (pickable,
-                                                    NULL, NULL,
+                                                    NULL,
                                                     antialias,
                                                     threshold,
                                                     select_transparent,
                                                     select_criterion,
                                                     diagonal_neighbors,
-                                                    0.92, 3, 20, 60, /* TODO */
                                                     x, y);
 
   if (! sample_merged)
diff --git a/app/core/gimpdrawable-bucket-fill.c b/app/core/gimpdrawable-bucket-fill.c
index a455b9606a..3e1ada2d60 100644
--- a/app/core/gimpdrawable-bucket-fill.c
+++ b/app/core/gimpdrawable-bucket-fill.c
@@ -50,18 +50,13 @@
 
 void
 gimp_drawable_bucket_fill (GimpDrawable         *drawable,
-                           GeglBuffer           *line_art,
-                           gfloat               *distmap,
+                           GimpLineArt          *line_art,
                            GimpFillOptions      *options,
                            gboolean              fill_transparent,
                            GimpSelectCriterion   fill_criterion,
                            gdouble               threshold,
                            gboolean              sample_merged,
                            gboolean              diagonal_neighbors,
-                           gfloat                line_art_stroke_threshold,
-                           gint                  line_art_max_grow,
-                           gint                  line_art_segment_max_length,
-                           gint                  line_art_spline_max_length,
                            gdouble               seed_x,
                            gdouble               seed_y)
 {
@@ -77,15 +72,10 @@ gimp_drawable_bucket_fill (GimpDrawable         *drawable,
 
   image = gimp_item_get_image (GIMP_ITEM (drawable));
   gimp_set_busy (image->gimp);
-  buffer = gimp_drawable_get_bucket_fill_buffer (drawable, line_art,
-                                                 distmap, options,
+  buffer = gimp_drawable_get_bucket_fill_buffer (drawable, line_art, options,
                                                  fill_transparent, fill_criterion,
                                                  threshold, sample_merged,
                                                  diagonal_neighbors,
-                                                 line_art_stroke_threshold,
-                                                 line_art_max_grow,
-                                                 line_art_segment_max_length,
-                                                 line_art_spline_max_length,
                                                  seed_x, seed_y, NULL,
                                                  &mask_x, &mask_y, &width, &height);
 
@@ -142,18 +132,13 @@ gimp_drawable_bucket_fill (GimpDrawable         *drawable,
  */
 GeglBuffer *
 gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
-                                      GeglBuffer           *line_art,
-                                      gfloat               *distmap,
+                                      GimpLineArt          *line_art,
                                       GimpFillOptions      *options,
                                       gboolean              fill_transparent,
                                       GimpSelectCriterion   fill_criterion,
                                       gdouble               threshold,
                                       gboolean              sample_merged,
                                       gboolean              diagonal_neighbors,
-                                      gfloat                stroke_threshold,
-                                      gint                  max_grow,
-                                      gint                  segment_max_length,
-                                      gint                  spline_max_length,
                                       gdouble               seed_x,
                                       gdouble               seed_y,
                                       GeglBuffer          **mask_buffer,
@@ -209,16 +194,12 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
    *  contiguous region.
    */
   new_mask = gimp_pickable_contiguous_region_by_seed (pickable,
-                                                      line_art, distmap,
+                                                      line_art,
                                                       antialias,
                                                       threshold,
                                                       fill_transparent,
                                                       fill_criterion,
                                                       diagonal_neighbors,
-                                                      stroke_threshold,
-                                                      max_grow,
-                                                      segment_max_length,
-                                                      spline_max_length,
                                                       (gint) seed_x,
                                                       (gint) seed_y);
   if (mask_buffer && *mask_buffer)
diff --git a/app/core/gimpdrawable-bucket-fill.h b/app/core/gimpdrawable-bucket-fill.h
index 4019d82780..e2b2525d45 100644
--- a/app/core/gimpdrawable-bucket-fill.h
+++ b/app/core/gimpdrawable-bucket-fill.h
@@ -20,33 +20,23 @@
 
 
 void         gimp_drawable_bucket_fill            (GimpDrawable         *drawable,
-                                                   GeglBuffer           *line_art,
-                                                   gfloat               *distmap,
+                                                   GimpLineArt          *line_art,
                                                    GimpFillOptions      *options,
                                                    gboolean              fill_transparent,
                                                    GimpSelectCriterion   fill_criterion,
                                                    gdouble               threshold,
                                                    gboolean              sample_merged,
                                                    gboolean              diagonal_neighbors,
-                                                   gfloat                line_art_stroke_threshold,
-                                                   gint                  line_art_max_grow,
-                                                   gint                  line_art_segment_max_length,
-                                                   gint                  line_art_spline_max_length,
                                                    gdouble               x,
                                                    gdouble               y);
 GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
-                                                   GeglBuffer           *line_art,
-                                                   gfloat               *distmap,
+                                                   GimpLineArt          *line_art,
                                                    GimpFillOptions      *options,
                                                    gboolean              fill_transparent,
                                                    GimpSelectCriterion   fill_criterion,
                                                    gdouble               threshold,
                                                    gboolean              sample_merged,
                                                    gboolean              diagonal_neighbors,
-                                                   gfloat                line_art_stroke_threshold,
-                                                   gint                  line_art_max_grow,
-                                                   gint                  line_art_segment_max_length,
-                                                   gint                  line_art_spline_max_length,
                                                    gdouble               seed_x,
                                                    gdouble               seed_y,
                                                    GeglBuffer          **mask_buffer,
diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c
index 5fa9d8267f..ae0027ee6e 100644
--- a/app/core/gimplineart.c
+++ b/app/core/gimplineart.c
@@ -20,14 +20,74 @@
 
 #include "config.h"
 
-#define GEGL_ITERATOR2_API
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gegl.h>
 
 #include "libgimpmath/gimpmath.h"
 
 #include "core-types.h"
 
+#include "gimp-parallel.h"
+#include "gimp-utils.h" /* GIMP_TIMER */
+#include "gimpasync.h"
+#include "gimpcancelable.h"
+#include "gimpdrawable.h"
+#include "gimpimage.h"
 #include "gimplineart.h"
+#include "gimppickable.h"
+#include "gimpprojection.h"
+#include "gimpwaitable.h"
+
+#include "gimp-intl.h"
+
+enum
+{
+  PROP_0,
+  PROP_SELECT_TRANSPARENT,
+  PROP_MAX_GROW,
+  PROP_THRESHOLD,
+  PROP_SPLINE_MAX_LEN,
+  PROP_SEGMENT_MAX_LEN,
+};
+
+typedef struct _GimpLineArtPrivate GimpLineArtPrivate;
+
+struct _GimpLineArtPrivate
+{
+  gboolean      frozen;
+  gboolean      compute_after_thaw;
+
+  GimpAsync    *async;
+
+  GimpPickable *input;
+  GeglBuffer   *closed;
+  gfloat       *distmap;
+
+  /* Used in the closing step. */
+  gboolean      select_transparent;
+  gdouble       threshold;
+  gint          spline_max_len;
+  gint          segment_max_len;
+
+  /* Used in the grow step. */
+  gint          max_grow;
+};
+
+typedef struct
+{
+  GeglBuffer  *buffer;
+
+  gboolean     select_transparent;
+  gdouble      threshold;
+  gint         spline_max_len;
+  gint         segment_max_len;
+} LineArtData;
+
+typedef struct
+{
+  GeglBuffer *closed;
+  gfloat     *distmap;
+} LineArtResult;
 
 static int DeltaX[4] = {+1, -1, 0, 0};
 static int DeltaY[4] = {0, 0, +1, -1};
@@ -68,56 +128,109 @@ typedef struct _Edgel
   guint     next, previous;
 } Edgel;
 
-static void         gimp_lineart_denoise                    (GeglBuffer             *buffer,
-                                                             int                     size);
-static void         gimp_lineart_compute_normals_curvatures (GeglBuffer             *mask,
-                                                             gfloat                 *normals,
-                                                             gfloat                 *curvatures,
-                                                             gfloat                 *smoothed_curvatures,
-                                                             int                     
normal_estimate_mask_size);
-static gfloat *     gimp_lineart_get_smooth_curvatures      (GArray                 *edgelset);
-static GArray     * gimp_lineart_curvature_extremums        (gfloat                 *curvatures,
-                                                             gfloat                 *smoothed_curvatures,
-                                                             gint                    curvatures_width,
-                                                             gint                    curvatures_height);
-static gint         gimp_spline_candidate_cmp               (const SplineCandidate  *a,
-                                                             const SplineCandidate  *b,
-                                                             gpointer                user_data);
-static GList      * gimp_lineart_find_spline_candidates     (GArray                 *max_positions,
-                                                             gfloat                 *normals,
-                                                             gint                    width,
-                                                             gint                    distance_threshold,
-                                                             gfloat                  max_angle_deg);
-
-static GArray     * gimp_lineart_discrete_spline            (Pixel                   p0,
-                                                             GimpVector2             n0,
-                                                             Pixel                   p1,
-                                                             GimpVector2             n1);
-
-static gint         gimp_number_of_transitions               (GArray                 *pixels,
-                                                              GeglBuffer             *buffer);
-static gboolean     gimp_lineart_curve_creates_region        (GeglBuffer             *mask,
-                                                              GArray                 *pixels,
-                                                              int                     lower_size_limit,
-                                                              int                     upper_size_limit);
-static GArray     * gimp_lineart_line_segment_until_hit      (const GeglBuffer       *buffer,
-                                                              Pixel                   start,
-                                                              GimpVector2             direction,
-                                                              int                     size);
-static gfloat     * gimp_lineart_estimate_strokes_radii      (GeglBuffer             *mask);
+
+static void            gimp_line_art_finalize                  (GObject               *object);
+static void            gimp_line_art_set_property              (GObject                *object,
+                                                                guint                   property_id,
+                                                                const GValue           *value,
+                                                                GParamSpec             *pspec);
+static void            gimp_line_art_get_property              (GObject                *object,
+                                                                guint                   property_id,
+                                                                GValue                 *value,
+                                                                GParamSpec             *pspec);
+
+/* Functions for asynchronous computation. */
+
+static void            gimp_line_art_compute                   (GimpLineArt            *line_art);
+static void            gimp_line_art_compute_cb                (GimpAsync              *async,
+                                                                GimpLineArt            *line_art);
+
+static GimpAsync     * gimp_line_art_prepare_async             (GimpLineArt            *line_art,
+                                                                gint                    priority);
+static void            gimp_line_art_prepare_async_func        (GimpAsync              *async,
+                                                                LineArtData            *data);
+static LineArtData   * line_art_data_new                       (GeglBuffer             *buffer,
+                                                                GimpLineArt            *line_art);
+static void            line_art_data_free                      (LineArtData            *data);
+static LineArtResult * line_art_result_new                     (GeglBuffer             *line_art,
+                                                                gfloat                 *distmap);
+static void            line_art_result_free                    (LineArtResult          *result);
+
+static void            gimp_line_art_projection_rendered       (GimpProjection         *proj,
+                                                                GimpLineArt            *line_art);
+static void            gimp_line_art_drawable_painted          (GimpDrawable           *drawable,
+                                                                GimpLineArt            *line_art);
+
+
+/* All actual computation functions. */
+
+static GeglBuffer    * gimp_line_art_close                     (GeglBuffer             *buffer,
+                                                                gboolean                select_transparent,
+                                                                gdouble                 stroke_threshold,
+                                                                gint                    spline_max_length,
+                                                                gint                    segment_max_length,
+                                                                gint                    minimal_lineart_area,
+                                                                gint                    
normal_estimate_mask_size,
+                                                                gfloat                  end_point_rate,
+                                                                gfloat                  spline_max_angle,
+                                                                gint                    
end_point_connectivity,
+                                                                gfloat                  spline_roundness,
+                                                                gboolean                
allow_self_intersections,
+                                                                gint                    
created_regions_significant_area,
+                                                                gint                    
created_regions_minimum_area,
+                                                                gboolean                
small_segments_from_spline_sources,
+                                                                gfloat                **lineart_distmap);
+
+static void            gimp_lineart_denoise                    (GeglBuffer             *buffer,
+                                                                int                     size);
+static void            gimp_lineart_compute_normals_curvatures (GeglBuffer             *mask,
+                                                                gfloat                 *normals,
+                                                                gfloat                 *curvatures,
+                                                                gfloat                 *smoothed_curvatures,
+                                                                int                     
normal_estimate_mask_size);
+static gfloat        * gimp_lineart_get_smooth_curvatures      (GArray                 *edgelset);
+static GArray        * gimp_lineart_curvature_extremums        (gfloat                 *curvatures,
+                                                                gfloat                 *smoothed_curvatures,
+                                                                gint                    curvatures_width,
+                                                                gint                    curvatures_height);
+static gint            gimp_spline_candidate_cmp               (const SplineCandidate  *a,
+                                                                const SplineCandidate  *b,
+                                                                gpointer                user_data);
+static GList         * gimp_lineart_find_spline_candidates     (GArray                 *max_positions,
+                                                                gfloat                 *normals,
+                                                                gint                    width,
+                                                                gint                    distance_threshold,
+                                                                gfloat                  max_angle_deg);
+
+static GArray        * gimp_lineart_discrete_spline            (Pixel                   p0,
+                                                                GimpVector2             n0,
+                                                                Pixel                   p1,
+                                                                GimpVector2             n1);
+
+static gint            gimp_number_of_transitions               (GArray                 *pixels,
+                                                                 GeglBuffer             *buffer);
+static gboolean        gimp_lineart_curve_creates_region        (GeglBuffer             *mask,
+                                                                 GArray                 *pixels,
+                                                                 int                     lower_size_limit,
+                                                                 int                     upper_size_limit);
+static GArray        * gimp_lineart_line_segment_until_hit      (const GeglBuffer       *buffer,
+                                                                 Pixel                   start,
+                                                                 GimpVector2             direction,
+                                                                 int                     size);
+static gfloat        * gimp_lineart_estimate_strokes_radii      (GeglBuffer             *mask);
 
 /* Some callback-type functions. */
 
-static guint        visited_hash_fun                         (Pixel                  *key);
-static gboolean     visited_equal_fun                        (Pixel                  *e1,
-                                                              Pixel                  *e2);
+static guint           visited_hash_fun                         (Pixel                  *key);
+static gboolean        visited_equal_fun                        (Pixel                  *e1,
+                                                                 Pixel                  *e2);
 
-static inline gboolean    border_in_direction (GeglBuffer *mask,
-                                               Pixel       p,
-                                               int         direction);
-static inline GimpVector2 pair2normal         (Pixel       p,
-                                               gfloat     *normals,
-                                               gint        width);
+static inline gboolean border_in_direction                      (GeglBuffer             *mask,
+                                                                 Pixel                   p,
+                                                                 int                     direction);
+static inline GimpVector2 pair2normal                           (Pixel                   p,
+                                                                 gfloat                 *normals,
+                                                                 gint                    width);
 
 /* Edgel */
 
@@ -158,22 +271,518 @@ static void       gimp_edgelset_next8             (const GeglBuffer  *buffer,
                                                    Edgel             *it,
                                                    Edgel             *n);
 
+G_DEFINE_TYPE_WITH_CODE (GimpLineArt, gimp_line_art, GIMP_TYPE_OBJECT,
+                         G_ADD_PRIVATE (GimpLineArt))
+
+static void
+gimp_line_art_class_init (GimpLineArtClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize     = gimp_line_art_finalize;
+  object_class->set_property = gimp_line_art_set_property;
+  object_class->get_property = gimp_line_art_get_property;
+
+  g_object_class_install_property (object_class, PROP_SELECT_TRANSPARENT,
+                                   g_param_spec_boolean ("select-transparent",
+                                                         _("Select transparent pixels instead of gray ones"),
+                                                         _("Select transparent pixels instead of gray ones"),
+                                                         TRUE,
+                                                         G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_THRESHOLD,
+                                   g_param_spec_double ("threshold",
+                                                        _("Line art detection threshold"),
+                                                        _("Threshold to detect contour (higher values will 
include more pixels)"),
+                                                        0.0, 1.0, 0.92,
+                                                        G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_MAX_GROW,
+                                   g_param_spec_int ("max-grow",
+                                                     _("Maximum growing size"),
+                                                     _("Maximum number of pixels grown under the line art"),
+                                                     1, 100, 3,
+                                                     G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_SPLINE_MAX_LEN,
+                                   g_param_spec_int ("spline-max-length",
+                                                     _("Maximum curved closing length"),
+                                                     _("Maximum curved length (in pixels) to close the line 
art"),
+                                                     1, 1000, 60,
+                                                     G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_SEGMENT_MAX_LEN,
+                                   g_param_spec_int ("segment-max-length",
+                                                     _("Maximum straight closing length"),
+                                                     _("Maximum straight length (in pixels) to close the 
line art"),
+                                                     1, 1000, 20,
+                                                     G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_line_art_init (GimpLineArt *line_art)
+{
+  line_art->priv = gimp_line_art_get_instance_private (line_art);
+}
+
+static void
+gimp_line_art_finalize (GObject *object)
+{
+  GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+  if (line_art->priv->input)
+    {
+      if (GIMP_IS_IMAGE (line_art->priv->input))
+        g_signal_handlers_disconnect_by_data (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+                                              line_art);
+      else
+        g_signal_handlers_disconnect_by_data (line_art->priv->input,
+                                              line_art);
+    }
+  if (line_art->priv->async)
+    {
+      /* we cancel the async, but don't wait for it to finish, since
+       * it can't actually be interrupted.  instead
+       * gimp_bucket_fill_compute_line_art_cb() bails if the async has
+       * been canceled, to avoid accessing the dead tool.
+       */
+      gimp_cancelable_cancel (GIMP_CANCELABLE (line_art->priv->async));
+      g_clear_object (&line_art->priv->async);
+    }
+
+  g_clear_object (&line_art->priv->closed);
+  g_clear_pointer (&line_art->priv->distmap, g_free);
+}
+
+static void
+gimp_line_art_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+  switch (property_id)
+    {
+    case PROP_SELECT_TRANSPARENT:
+      line_art->priv->select_transparent = g_value_get_boolean (value);
+      gimp_line_art_compute (line_art);
+      break;
+    case PROP_MAX_GROW:
+      line_art->priv->max_grow = g_value_get_int (value);
+      break;
+    case PROP_THRESHOLD:
+      line_art->priv->threshold = g_value_get_double (value);
+      gimp_line_art_compute (line_art);
+      break;
+    case PROP_SPLINE_MAX_LEN:
+      line_art->priv->spline_max_len = g_value_get_int (value);
+      gimp_line_art_compute (line_art);
+      break;
+    case PROP_SEGMENT_MAX_LEN:
+      line_art->priv->segment_max_len = g_value_get_int (value);
+      gimp_line_art_compute (line_art);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_line_art_get_property (GObject    *object,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+  switch (property_id)
+    {
+    case PROP_SELECT_TRANSPARENT:
+      g_value_set_boolean (value, line_art->priv->select_transparent);
+      break;
+    case PROP_MAX_GROW:
+      g_value_set_int (value, line_art->priv->max_grow);
+      break;
+    case PROP_THRESHOLD:
+      g_value_set_double (value, line_art->priv->threshold);
+      break;
+    case PROP_SPLINE_MAX_LEN:
+      g_value_set_int (value, line_art->priv->spline_max_len);
+      break;
+    case PROP_SEGMENT_MAX_LEN:
+      g_value_set_int (value, line_art->priv->segment_max_len);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
 /* Public functions */
 
+GimpLineArt *
+gimp_line_art_new (void)
+{
+  return g_object_new (GIMP_TYPE_LINE_ART,
+                       NULL);
+}
+
+void
+gimp_line_art_set_input (GimpLineArt  *line_art,
+                         GimpPickable *pickable)
+{
+  if (line_art->priv->input)
+    {
+      if (GIMP_IS_IMAGE (line_art->priv->input))
+        g_signal_handlers_disconnect_by_data (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+                                              line_art);
+      else
+        g_signal_handlers_disconnect_by_data (line_art->priv->input,
+                                              line_art);
+    }
+
+  line_art->priv->input = pickable;
+
+  gimp_line_art_compute (line_art);
+  if (pickable)
+    {
+      if (GIMP_IS_IMAGE (pickable))
+        g_signal_connect (gimp_image_get_projection (GIMP_IMAGE (pickable)), "rendered",
+                          G_CALLBACK (gimp_line_art_projection_rendered),
+                          line_art);
+      else if (GIMP_IS_DRAWABLE (pickable))
+        g_signal_connect (pickable, "painted",
+                          G_CALLBACK (gimp_line_art_drawable_painted),
+                          line_art);
+      else
+        g_return_if_reached ();
+    }
+}
+
+void
+gimp_line_art_freeze (GimpLineArt *line_art)
+{
+  g_return_if_fail (! line_art->priv->frozen);
+
+  line_art->priv->frozen             = TRUE;
+  line_art->priv->compute_after_thaw = FALSE;
+}
+
+void
+gimp_line_art_thaw (GimpLineArt *line_art)
+{
+  g_return_if_fail (line_art->priv->frozen);
+
+  line_art->priv->frozen = FALSE;
+  if (line_art->priv->compute_after_thaw)
+    {
+      gimp_line_art_compute (line_art);
+      line_art->priv->compute_after_thaw = FALSE;
+    }
+}
+
+GeglBuffer *
+gimp_line_art_get (GimpLineArt  *line_art,
+                   gfloat      **distmap)
+{
+  g_return_val_if_fail (line_art->priv->input, NULL);
+
+  if (line_art->priv->async)
+    {
+      gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async));
+    }
+  else if (! line_art->priv->closed)
+    {
+      gimp_line_art_compute (line_art);
+      if (line_art->priv->async)
+        gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async));
+    }
+
+  g_return_val_if_fail (line_art->priv->closed, NULL);
+
+  if (distmap)
+    *distmap = line_art->priv->distmap;
+
+  return line_art->priv->closed;
+}
+
+/* Functions for asynchronous computation. */
+
+static void
+gimp_line_art_compute (GimpLineArt *line_art)
+{
+  if (line_art->priv->frozen)
+    {
+      line_art->priv->compute_after_thaw = TRUE;
+      return;
+    }
+
+  if (line_art->priv->async)
+    {
+      gimp_cancelable_cancel (GIMP_CANCELABLE (line_art->priv->async));
+      g_clear_object (&line_art->priv->async);
+    }
+
+  g_clear_object (&line_art->priv->closed);
+  g_clear_pointer (&line_art->priv->distmap, g_free);
+
+  if (line_art->priv->input)
+    {
+      /* gimp_line_art_prepare_async() will flush the pickable, which
+       * may trigger this signal handler, and will leak a line art (as
+       * line_art->priv->async has not been set yet).
+       */
+      if (GIMP_IS_IMAGE (line_art->priv->input))
+        g_signal_handlers_block_by_func (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+                                         G_CALLBACK (gimp_line_art_projection_rendered),
+                                         line_art);
+      else
+        g_signal_handlers_block_by_func (line_art->priv->input,
+                                         G_CALLBACK (gimp_line_art_drawable_painted),
+                                         line_art);
+      line_art->priv->async = gimp_line_art_prepare_async (line_art, +1);
+      if (GIMP_IS_IMAGE (line_art->priv->input))
+        g_signal_handlers_unblock_by_func (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+                                           G_CALLBACK (gimp_line_art_projection_rendered),
+                                           line_art);
+      else
+        g_signal_handlers_unblock_by_func (line_art->priv->input,
+                                           G_CALLBACK (gimp_line_art_drawable_painted),
+                                           line_art);
+
+      gimp_async_add_callback_for_object (line_art->priv->async,
+                                          (GimpAsyncCallback) gimp_line_art_compute_cb,
+                                          line_art, line_art);
+    }
+}
+
+static void
+gimp_line_art_compute_cb (GimpAsync   *async,
+                          GimpLineArt *line_art)
+{
+  if (gimp_async_is_canceled (async))
+    return;
+
+  if (gimp_async_is_finished (async))
+    {
+      LineArtResult *result;
+
+      result = gimp_async_get_result (async);
+
+      line_art->priv->closed  = g_object_ref (result->closed);
+      line_art->priv->distmap = result->distmap;
+      result->distmap  = NULL;
+    }
+
+  g_clear_object (&line_art->priv->async);
+}
+
+static GimpAsync *
+gimp_line_art_prepare_async (GimpLineArt *line_art,
+                             gint         priority)
+{
+  GeglBuffer  *buffer;
+  GimpAsync   *async;
+  LineArtData *data;
+
+  g_return_val_if_fail (GIMP_IS_PICKABLE (line_art->priv->input), NULL);
+
+  gimp_pickable_flush (line_art->priv->input);
+
+  buffer = gegl_buffer_dup (gimp_pickable_get_buffer (line_art->priv->input));
+
+  data  = line_art_data_new (buffer, line_art);
+
+  g_object_unref (buffer);
+
+  async = gimp_parallel_run_async_full (
+    priority,
+    (GimpParallelRunAsyncFunc) gimp_line_art_prepare_async_func,
+    data, (GDestroyNotify) line_art_data_free);
+
+  return async;
+}
+
+static void
+gimp_line_art_prepare_async_func (GimpAsync   *async,
+                                  LineArtData *data)
+{
+  GeglBuffer *closed;
+  gfloat     *distmap;
+  gboolean    has_alpha;
+  gboolean    select_transparent = FALSE;
+
+  has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
+
+  if (has_alpha)
+    {
+      if (data->select_transparent)
+        {
+          /*  don't select transparent regions if there are no fully
+           *  transparent pixels.
+           */
+          GeglBufferIterator *gi;
+
+          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)
+                    {
+                      select_transparent = TRUE;
+                      break;
+                    }
+                  p++;
+                }
+              if (select_transparent)
+                break;
+            }
+          if (select_transparent)
+            gegl_buffer_iterator_stop (gi);
+        }
+    }
+
+  /* For smart selection, we generate a binarized image with close
+   * regions, then run a composite selection with no threshold on
+   * this intermediate buffer.
+   */
+  GIMP_TIMER_START();
+
+  closed = gimp_line_art_close (data->buffer,
+                                select_transparent,
+                                data->threshold,
+                                data->spline_max_len,
+                                data->segment_max_len,
+                                /*minimal_lineart_area,*/
+                                5,
+                                /*normal_estimate_mask_size,*/
+                                5,
+                                /*end_point_rate,*/
+                                0.85,
+                                /*spline_max_angle,*/
+                                90.0,
+                                /*end_point_connectivity,*/
+                                2,
+                                /*spline_roundness,*/
+                                1.0,
+                                /*allow_self_intersections,*/
+                                TRUE,
+                                /*created_regions_significant_area,*/
+                                4,
+                                /*created_regions_minimum_area,*/
+                                100,
+                                /*small_segments_from_spline_sources,*/
+                                TRUE,
+                                &distmap);
+
+  GIMP_TIMER_END("close line-art");
+
+  gimp_async_finish_full (async,
+                          line_art_result_new (closed, distmap),
+                          (GDestroyNotify) line_art_result_free);
+
+  line_art_data_free (data);
+}
+
+static LineArtData *
+line_art_data_new (GeglBuffer  *buffer,
+                   GimpLineArt *line_art)
+{
+  LineArtData *data = g_slice_new (LineArtData);
+
+  data->buffer             = g_object_ref (buffer);
+  data->select_transparent = line_art->priv->select_transparent;
+  data->threshold          = line_art->priv->threshold;
+  data->spline_max_len     = line_art->priv->spline_max_len;
+  data->segment_max_len    = line_art->priv->segment_max_len;
+
+  return data;
+}
+
+static void
+line_art_data_free (LineArtData *data)
+{
+  g_object_unref (data->buffer);
+
+  g_slice_free (LineArtData, data);
+}
+
+static LineArtResult *
+line_art_result_new (GeglBuffer *closed,
+                     gfloat     *distmap)
+{
+  LineArtResult *data;
+
+  data = g_slice_new (LineArtResult);
+  data->closed  = closed;
+  data->distmap = distmap;
+
+  return data;
+}
+
+static void
+line_art_result_free (LineArtResult *data)
+{
+  g_object_unref (data->closed);
+  g_clear_pointer (&data->distmap, g_free);
+
+  g_slice_free (LineArtResult, data);
+}
+
+static void
+gimp_line_art_projection_rendered (GimpProjection *proj,
+                                   GimpLineArt    *line_art)
+{
+  gimp_line_art_compute (line_art);
+}
+
+static void
+gimp_line_art_drawable_painted (GimpDrawable *drawable,
+                                GimpLineArt  *line_art)
+{
+  gimp_line_art_compute (line_art);
+}
+
+/* All actual computation functions. */
+
 /**
- * gimp_lineart_close:
+ * gimp_line_art_close:
  * @buffer: the input #GeglBuffer.
  * @select_transparent: whether we binarize the alpha channel or the
  *                      luminosity.
  * @stroke_threshold: [0-1] threshold value for detecting stroke pixels
  *                    (higher values will detect more stroke pixels).
+ * @spline_max_length: the maximum length for creating splines between
+ *                     end points.
+ * @segment_max_length: the maximum length for creating segments
+ *                      between end points. Unlike splines, segments
+ *                      are straight lines.
  * @minimal_lineart_area: the minimum size in number pixels for area to
  *                        be considered as line art.
  * @normal_estimate_mask_size:
  * @end_point_rate: threshold to estimate if a curvature is an end-point
  *                  in [0-1] range value.
- * @spline_max_length: the maximum length for creating splines between
- *                     end points.
  * @spline_max_angle: the maximum angle between end point normals for
  *                    creating splines between them.
  * @end_point_connectivity:
@@ -183,9 +792,6 @@ static void       gimp_edgelset_next8             (const GeglBuffer  *buffer,
  * @created_regions_significant_area:
  * @created_regions_minimum_area:
  * @small_segments_from_spline_sources:
- * @segments_max_length: the maximum length for creating segments
- *                       between end points. Unlike splines, segments
- *                       are straight lines.
  * @closed_distmap: a distance map of the closed line art pixels.
  *
  * Creates a binarized version of the strokes of @buffer, detected either
@@ -205,23 +811,23 @@ static void       gimp_edgelset_next8             (const GeglBuffer  *buffer,
  *          newly allocated float buffer is returned, which can be used
  *          for overflowing created masks later.
  */
-GeglBuffer *
-gimp_lineart_close (GeglBuffer  *buffer,
-                    gboolean     select_transparent,
-                    gfloat       stroke_threshold,
-                    gint         minimal_lineart_area,
-                    gint         normal_estimate_mask_size,
-                    gfloat       end_point_rate,
-                    gint         spline_max_length,
-                    gfloat       spline_max_angle,
-                    gint         end_point_connectivity,
-                    gfloat       spline_roundness,
-                    gboolean     allow_self_intersections,
-                    gint         created_regions_significant_area,
-                    gint         created_regions_minimum_area,
-                    gboolean     small_segments_from_spline_sources,
-                    gint         segments_max_length,
-                    gfloat     **closed_distmap)
+static GeglBuffer *
+gimp_line_art_close (GeglBuffer  *buffer,
+                     gboolean     select_transparent,
+                     gdouble      stroke_threshold,
+                     gint         spline_max_length,
+                     gint         segment_max_length,
+                     gint         minimal_lineart_area,
+                     gint         normal_estimate_mask_size,
+                     gfloat       end_point_rate,
+                     gfloat       spline_max_angle,
+                     gint         end_point_connectivity,
+                     gfloat       spline_roundness,
+                     gboolean     allow_self_intersections,
+                     gint         created_regions_significant_area,
+                     gint         created_regions_minimum_area,
+                     gboolean     small_segments_from_spline_sources,
+                     gfloat     **closed_distmap)
 {
   const Babl         *gray_format;
   gfloat             *normals;
@@ -422,7 +1028,7 @@ gimp_lineart_close (GeglBuffer  *buffer,
         {
           GArray *segment = gimp_lineart_line_segment_until_hit (closed, *point,
                                                                  pair2normal (*point, normals, width),
-                                                                 segments_max_length);
+                                                                 segment_max_length);
 
           if (segment->len &&
               ! gimp_lineart_curve_creates_region (closed, segment,
@@ -488,8 +1094,6 @@ gimp_lineart_close (GeglBuffer  *buffer,
   return closed;
 }
 
-/* Private functions */
-
 static void
 gimp_lineart_denoise (GeglBuffer *buffer,
                       int         minimum_area)
diff --git a/app/core/gimplineart.h b/app/core/gimplineart.h
index 5ab579800b..1a867b598a 100644
--- a/app/core/gimplineart.h
+++ b/app/core/gimplineart.h
@@ -22,22 +22,42 @@
 #define __GIMP_LINEART__
 
 
-GeglBuffer * gimp_lineart_close (GeglBuffer  *buffer,
-                                 gboolean     select_transparent,
-                                 gfloat       stroke_threshold,
-                                 gint         minimal_lineart_area,
-                                 gint         normal_estimate_mask_size,
-                                 gfloat       end_point_rate,
-                                 gint         spline_max_length,
-                                 gfloat       spline_max_angle,
-                                 gint         end_point_connectivity,
-                                 gfloat       spline_roundness,
-                                 gboolean     allow_self_intersections,
-                                 gint         created_regions_significant_area,
-                                 gint         created_regions_minimum_area,
-                                 gboolean     small_segments_from_spline_sources,
-                                 gint         segments_max_length,
-                                 gfloat     **lineart_distmap);
+#include "gimpobject.h"
 
+#define GIMP_TYPE_LINE_ART            (gimp_line_art_get_type ())
+#define GIMP_LINE_ART(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINE_ART, GimpLineArt))
+#define GIMP_LINE_ART_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINE_ART, 
GimpLineArtClass))
+#define GIMP_IS_LINE_ART(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINE_ART))
+#define GIMP_IS_LINE_ART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINE_ART))
+#define GIMP_LINE_ART_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINE_ART, 
GimpLineArtClass))
+
+
+typedef struct _GimpLineArtClass   GimpLineArtClass;
+typedef struct _GimpLineArtPrivate GimpLineArtPrivate;
+
+struct _GimpLineArt
+{
+  GimpObject          parent_instance;
+
+  GimpLineArtPrivate *priv;
+};
+
+struct _GimpLineArtClass
+{
+  GimpObjectClass     parent_class;
+};
+
+
+GType                gimp_line_art_get_type         (void) G_GNUC_CONST;
+
+GimpLineArt *        gimp_line_art_new              (void);
+
+void                 gimp_line_art_set_input        (GimpLineArt  *line_art,
+                                                     GimpPickable *pickable);
+void                 gimp_line_art_freeze           (GimpLineArt  *line_art);
+void                 gimp_line_art_thaw             (GimpLineArt  *line_art);
+
+GeglBuffer *         gimp_line_art_get              (GimpLineArt  *line_art,
+                                                     gfloat      **distmap);
 
 #endif /* __GIMP_LINEART__ */
diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.c
index 472e5e32f3..06b4095646 100644
--- a/app/core/gimppickable-contiguous-region.c
+++ b/app/core/gimppickable-contiguous-region.c
@@ -38,15 +38,6 @@
 #include "gimppickable-contiguous-region.h"
 
 
-typedef struct
-{
-  GeglBuffer *buffer;
-  gboolean    select_transparent;
-  gfloat      stroke_threshold;
-  gint        segment_max_length;
-  gint        spline_max_length;
-} LineArtData;
-
 typedef struct
 {
   gint   x;
@@ -114,17 +105,6 @@ 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,
-                                             gint                 segment_max_length,
-                                             gint                 spline_max_length);
-static void            line_art_data_free   (LineArtData         *data);
-static GimpPickableLineArtAsyncResult *
-                       line_art_result_new  (GeglBuffer          *line_art,
-                                             gfloat              *distmap);
-static void            line_art_result_free (GimpPickableLineArtAsyncResult
-                                                                 *data);
 static void            line_art_queue_pixel (GQueue              *queue,
                                              gint                 x,
                                              gint                 y,
@@ -133,220 +113,46 @@ static void            line_art_queue_pixel (GQueue              *queue,
 
 /*  public functions  */
 
-static void
-gimp_pickable_contiguous_region_prepare_line_art_async_func (GimpAsync   *async,
-                                                             LineArtData *data)
-{
-  GeglBuffer *lineart;
-  gfloat     *distmap;
-  gboolean    has_alpha;
-  gboolean    select_transparent = FALSE;
-
-  has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
-
-  if (has_alpha)
-    {
-      if (data->select_transparent)
-        {
-          /*  don't select transparent regions if there are no fully
-           *  transparent pixels.
-           */
-          GeglBufferIterator *gi;
-
-          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)
-                    {
-                      select_transparent = TRUE;
-                      break;
-                    }
-                  p++;
-                }
-              if (select_transparent)
-                break;
-            }
-          if (select_transparent)
-            gegl_buffer_iterator_stop (gi);
-        }
-    }
-
-  /* For smart selection, we generate a binarized image with close
-   * regions, then run a composite selection with no threshold on
-   * this intermediate buffer.
-   */
-  GIMP_TIMER_START();
-
-  lineart = gimp_lineart_close (data->buffer,
-                                select_transparent,
-                                data->stroke_threshold,
-                                /*minimal_lineart_area,*/
-                                5,
-                                /*normal_estimate_mask_size,*/
-                                5,
-                                /*end_point_rate,*/
-                                0.85,
-                                data->spline_max_length,
-                                /*spline_max_angle,*/
-                                90.0,
-                                /*end_point_connectivity,*/
-                                2,
-                                /*spline_roundness,*/
-                                1.0,
-                                /*allow_self_intersections,*/
-                                TRUE,
-                                /*created_regions_significant_area,*/
-                                4,
-                                /*created_regions_minimum_area,*/
-                                100,
-                                /*small_segments_from_spline_sources,*/
-                                TRUE,
-                                data->segment_max_length,
-                                &distmap);
-
-  GIMP_TIMER_END("close line-art");
-
-  gimp_async_finish_full (async,
-                          line_art_result_new (lineart, distmap),
-                          (GDestroyNotify) line_art_result_free);
-
-  line_art_data_free (data);
-}
-
-GeglBuffer *
-gimp_pickable_contiguous_region_prepare_line_art (GimpPickable  *pickable,
-                                                  gboolean       select_transparent,
-                                                  gfloat         stroke_threshold,
-                                                  gint           segment_max_length,
-                                                  gint           spline_max_length,
-                                                  gfloat       **distmap)
-{
-  GimpAsync                      *async;
-  LineArtData                    *data;
-  GimpPickableLineArtAsyncResult *result;
-  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,
-                             segment_max_length, spline_max_length);
-
-  gimp_pickable_contiguous_region_prepare_line_art_async_func (async, data);
-
-  result = gimp_async_get_result (async);
-
-  lineart   = g_object_ref (result->line_art);
-  *distmap  = result->distmap;
-  result->distmap  = NULL;
-
-  g_object_unref (async);
-
-  return lineart;
-}
-
-GimpAsync *
-gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
-                                                        gboolean      select_transparent,
-                                                        gfloat        stroke_threshold,
-                                                        gint          segment_max_length,
-                                                        gint          spline_max_length,
-                                                        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,
-                             segment_max_length, spline_max_length);
-
-  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,
-                                         gfloat              *distmap,
+                                         GimpLineArt         *line_art,
                                          gboolean             antialias,
                                          gfloat               threshold,
                                          gboolean             select_transparent,
                                          GimpSelectCriterion  select_criterion,
                                          gboolean             diagonal_neighbors,
-                                         gfloat               stroke_threshold,
-                                         gint                 flooding_max,
-                                         gint                 segment_max_length,
-                                         gint                 spline_max_length,
                                          gint                 x,
                                          gint                 y)
 {
   GeglBuffer    *src_buffer;
   GeglBuffer    *mask_buffer;
   const Babl    *format;
+  gfloat        *distmap = NULL;
   GeglRectangle  extent;
   gint           n_components;
   gboolean       has_alpha;
   gfloat         start_col[MAX_CHANNELS];
   gboolean       smart_line_art = FALSE;
   gboolean       free_line_art  = FALSE;
+  gint           line_art_max_grow;
 
   g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
 
   if (select_criterion == GIMP_SELECT_CRITERION_LINE_ART)
     {
-      g_return_val_if_fail ((line_art && distmap) ||
-                            (! line_art && ! distmap),
-                            NULL);
-      if (line_art == NULL)
+      if (! line_art)
         {
           /* It is much better experience to pre-compute the line art,
            * but it may not be always possible (for instance when
            * selecting/filling through a PDB call).
            */
-          line_art      = gimp_pickable_contiguous_region_prepare_line_art (pickable, select_transparent,
-                                                                            stroke_threshold,
-                                                                            segment_max_length,
-                                                                            spline_max_length,
-                                                                            &distmap);
+          line_art = gimp_line_art_new ();
+          gimp_line_art_set_input (line_art, pickable);
           free_line_art = TRUE;
         }
 
-      src_buffer = line_art;
+      src_buffer = gimp_line_art_get (line_art, &distmap);
+      g_return_val_if_fail (src_buffer && distmap, NULL);
 
       smart_line_art     = TRUE;
       antialias          = FALSE;
@@ -427,8 +233,8 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
        */
       gfloat *mask;
       GQueue *queue  = g_queue_new ();
-      gint    width  = gegl_buffer_get_width (line_art);
-      gint    height = gegl_buffer_get_height (line_art);
+      gint    width  = gegl_buffer_get_width (src_buffer);
+      gint    height = gegl_buffer_get_height (src_buffer);
       gint    nx, ny;
 
       GIMP_TIMER_START();
@@ -520,6 +326,9 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
               }
           }
 
+      g_object_get (line_art,
+                    "max-grow", &line_art_max_grow,
+                    NULL);
       while (! g_queue_is_empty (queue))
         {
           BorderPixel *c = g_queue_pop_head (queue);
@@ -527,7 +336,7 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
           if (mask[c->x + c->y * width] != 1.0)
             {
               mask[c->x + c->y * width] = 1.0;
-              if (c->level >= flooding_max)
+              if (c->level >= line_art_max_grow)
                 /* Do not overflood under line arts. */
                 continue;
               if (c->x > 0)
@@ -600,10 +409,7 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
       GIMP_TIMER_END("watershed line art");
 
       if (free_line_art)
-        {
-          g_object_unref (src_buffer);
-          g_free (distmap);
-        }
+        g_clear_object (&line_art);
     }
 
   return mask_buffer;
@@ -1149,54 +955,6 @@ find_contiguous_region (GeglBuffer          *src_buffer,
 #endif
 }
 
-static LineArtData *
-line_art_data_new (GeglBuffer *buffer,
-                   gboolean    select_transparent,
-                   gfloat      stroke_threshold,
-                   gint        segment_max_length,
-                   gint        spline_max_length)
-{
-  LineArtData *data = g_slice_new (LineArtData);
-
-  data->buffer             = g_object_ref (buffer);
-  data->select_transparent = select_transparent;
-  data->stroke_threshold   = stroke_threshold;
-  data->segment_max_length = segment_max_length;
-  data->spline_max_length  = spline_max_length;
-
-  return data;
-}
-
-static void
-line_art_data_free (LineArtData *data)
-{
-  g_object_unref (data->buffer);
-
-  g_slice_free (LineArtData, data);
-}
-
-static GimpPickableLineArtAsyncResult *
-line_art_result_new (GeglBuffer *line_art,
-                     gfloat     *distmap)
-{
-  GimpPickableLineArtAsyncResult *data;
-
-  data = g_slice_new (GimpPickableLineArtAsyncResult);
-  data->line_art = line_art;
-  data->distmap  = distmap;
-
-  return data;
-}
-
-static void
-line_art_result_free (GimpPickableLineArtAsyncResult *data)
-{
-  g_object_unref (data->line_art);
-  g_clear_pointer (&data->distmap, g_free);
-
-  g_slice_free (GimpPickableLineArtAsyncResult, data);
-}
-
 static void
 line_art_queue_pixel (GQueue *queue,
                       gint    x,
diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h
index 772f9d3cdc..9b4ffa544f 100644
--- a/app/core/gimppickable-contiguous-region.h
+++ b/app/core/gimppickable-contiguous-region.h
@@ -18,37 +18,14 @@
 #ifndef __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
 #define __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
 
-typedef struct
-{
-  GeglBuffer *line_art;
-  gfloat     *distmap;
-} GimpPickableLineArtAsyncResult;
-
-GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art       (GimpPickable        *pickable,
-                                                                     gboolean             select_transparent,
-                                                                     gfloat               stroke_threshold,
-                                                                     gint                 segment_max_length,
-                                                                     gint                 spline_max_length,
-                                                                     gfloat             **distmap);
-GimpAsync  * gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable        *pickable,
-                                                                     gboolean             select_transparent,
-                                                                     gfloat               stroke_threshold,
-                                                                     gint                 segment_max_length,
-                                                                     gint                 spline_max_length,
-                                                                     gint                 priority);
 
 GeglBuffer * gimp_pickable_contiguous_region_by_seed                (GimpPickable        *pickable,
-                                                                     GeglBuffer          *line_art,
-                                                                     gfloat              *line_art_distmap,
+                                                                     GimpLineArt         *line_art,
                                                                      gboolean             antialias,
                                                                      gfloat               threshold,
                                                                      gboolean             select_transparent,
                                                                      GimpSelectCriterion  select_criterion,
                                                                      gboolean             diagonal_neighbors,
-                                                                     gfloat               
line_art_stroke_threshold,
-                                                                     gint                 line_art_max_grow,
-                                                                     gint                 
line_art_segment_max_length,
-                                                                     gint                 
line_art_spline_max_length,
                                                                      gint                 x,
                                                                      gint                 y);
 
diff --git a/app/pdb/drawable-edit-cmds.c b/app/pdb/drawable-edit-cmds.c
index 4d7c8a9cfc..a5212bb1cd 100644
--- a/app/pdb/drawable-edit-cmds.c
+++ b/app/pdb/drawable-edit-cmds.c
@@ -165,13 +165,12 @@ drawable_edit_bucket_fill_invoker (GimpProcedure         *procedure,
           if (gimp_fill_options_set_by_fill_type (options, context,
                                                   fill_type, error))
             {
-              gimp_drawable_bucket_fill (drawable, NULL, NULL, options,
+              gimp_drawable_bucket_fill (drawable, NULL, options,
                                          GIMP_PDB_CONTEXT (context)->sample_transparent,
                                          GIMP_PDB_CONTEXT (context)->sample_criterion,
                                          GIMP_PDB_CONTEXT (context)->sample_threshold,
                                          GIMP_PDB_CONTEXT (context)->sample_merged,
                                          GIMP_PDB_CONTEXT (context)->diagonal_neighbors,
-                                         0.92, 3, 20, 60, /* TODO */
                                          x, y);
             }
           else
diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c
index 3b2f1968c0..bf0f0f243f 100644
--- a/app/tools/gimpbucketfilltool.c
+++ b/app/tools/gimpbucketfilltool.c
@@ -62,15 +62,10 @@
 
 struct _GimpBucketFillToolPrivate
 {
-  GimpAsync          *async;
-  GeglBuffer         *line_art;
-  gfloat             *distmap;
+  GimpLineArt        *line_art;
   GWeakRef            cached_image;
   GWeakRef            cached_drawable;
 
-  gboolean            fill_in_progress;
-  gboolean            compute_line_art_after_fill;
-  GeglBuffer         *fill_buffer;
   GeglBuffer         *fill_mask;
 
   /* For preview */
@@ -130,20 +125,17 @@ static void     gimp_bucket_fill_tool_cursor_update    (GimpTool              *t
                                                         GdkModifierType        state,
                                                         GimpDisplay           *display);
 
-static void     gimp_bucket_fill_compute_line_art      (GimpBucketFillTool    *tool);
 static gboolean gimp_bucket_fill_tool_connect_handlers (gpointer               data);
 static void     gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
                                                         GParamSpec            *pspec,
                                                         GimpBucketFillTool    *tool);
+static void     gimp_bucket_fill_reset_line_art        (GimpBucketFillTool    *tool,
+                                                        GimpBucketFillOptions *options);
 static void     gimp_bucket_fill_tool_image_changed    (GimpContext           *context,
                                                         GimpImage             *image,
                                                         GimpBucketFillTool    *tool);
 static void     gimp_bucket_fill_tool_drawable_changed (GimpImage            *image,
                                                         GimpBucketFillTool   *tool);
-static void  gimp_bucket_fill_tool_projection_rendered (GimpProjection     *proj,
-                                                        GimpBucketFillTool *tool);
-static void     gimp_bucket_fill_tool_drawable_painted (GimpDrawable         *drawable,
-                                                        GimpBucketFillTool   *tool);
 
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpBucketFillTool, gimp_bucket_fill_tool, GIMP_TYPE_COLOR_TOOL)
@@ -212,9 +204,28 @@ gimp_bucket_fill_tool_constructed (GObject *object)
   GimpTool              *tool    = GIMP_TOOL (object);
   GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (object);
   Gimp                  *gimp    = GIMP_CONTEXT (options)->gimp;
+  GimpLineArt           *line_art;
 
   G_OBJECT_CLASS (parent_class)->constructed (object);
 
+  line_art = gimp_line_art_new ();
+  g_object_bind_property (options,  "fill-transparent",
+                          line_art, "select-transparent",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  g_object_bind_property (options,  "line-art-threshold",
+                          line_art, "threshold",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  g_object_bind_property (options,  "line-art-max-grow",
+                          line_art, "max-grow",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  g_object_bind_property (options,  "line-art-spline-max-len",
+                          line_art, "spline-max-length",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  g_object_bind_property (options,  "line-art-segment-max-len",
+                          line_art, "segment-max-length",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  GIMP_BUCKET_FILL_TOOL (tool)->priv->line_art = line_art;
+
   /* Avoid computing initial line art several times (for every option
    * property as it gets deserialized) if tool is selected when starting
    * GIMP with an image.
@@ -236,38 +247,16 @@ gimp_bucket_fill_tool_finalize (GObject *object)
   Gimp                  *gimp     = GIMP_CONTEXT (options)->gimp;
   GimpContext           *context  = gimp_get_user_context (gimp);
   GimpImage             *image    = g_weak_ref_get (&tool->priv->cached_image);
-  GimpDrawable          *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
-  if (tool->priv->async)
-    {
-      /* we cancel the async, but don't wait for it to finish, since
-       * it can't actually be interrupted.  instead
-       * gimp_bucket_fill_compute_line_art_cb() bails if the async has
-       * been canceled, to avoid accessing the dead tool.
-       */
-      gimp_cancelable_cancel (GIMP_CANCELABLE (tool->priv->async));
-      g_clear_object (&tool->priv->async);
-    }
 
   g_clear_object (&tool->priv->line_art);
-  g_clear_pointer (&tool->priv->distmap, g_free);
 
   if (image)
     {
       g_signal_handlers_disconnect_by_data (image, tool);
-      g_signal_handlers_disconnect_by_data (gimp_image_get_projection (image),
-                                            tool);
       g_object_unref (image);
     }
-  if (drawable)
-    {
-      g_signal_handlers_disconnect_by_data (drawable, tool);
-      g_object_unref (drawable);
-    }
 
-  g_signal_handlers_disconnect_by_func (options,
-                                        G_CALLBACK (gimp_bucket_fill_tool_options_notified),
-                                        tool);
+  g_signal_handlers_disconnect_by_data (options, tool);
   g_signal_handlers_disconnect_by_data (context, tool);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -324,7 +313,7 @@ gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
 
   g_return_if_fail (! tool->priv->filter);
 
-  tool->priv->fill_in_progress = TRUE;
+  gimp_line_art_freeze (tool->priv->line_art);
 
   GIMP_TOOL (tool)->display  = display;
   GIMP_TOOL (tool)->drawable = drawable;
@@ -351,12 +340,6 @@ gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
   g_signal_connect (tool->priv->filter, "flush",
                     G_CALLBACK (gimp_bucket_fill_tool_filter_flush),
                     tool);
-
-  if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART &&
-      tool->priv->async)
-    {
-      gimp_waitable_wait (GIMP_WAITABLE (tool->priv->async));
-    }
 }
 
 static void
@@ -372,8 +355,6 @@ gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
   if (tool->priv->filter)
     {
       GeglBuffer *fill     = NULL;
-      GeglBuffer *line_art = NULL;
-      gfloat     *distmap  = NULL;
       gdouble     x        = coords->x;
       gdouble     y        = coords->y;
 
@@ -387,29 +368,16 @@ gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
           y -= (gdouble) off_y;
         }
 
-      if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
-        {
-          line_art = g_object_ref (tool->priv->line_art);
-          distmap  = tool->priv->distmap;
-        }
-
       fill = gimp_drawable_get_bucket_fill_buffer (drawable,
-                                                   line_art, distmap,
+                                                   tool->priv->line_art,
                                                    fill_options,
                                                    options->fill_transparent,
                                                    options->fill_criterion,
                                                    options->threshold / 255.0,
                                                    options->sample_merged,
                                                    options->diagonal_neighbors,
-                                                   options->line_art_threshold,
-                                                   options->line_art_max_grow,
-                                                   options->line_art_segment_max_len,
-                                                   options->line_art_spline_max_len,
                                                    x, y, &tool->priv->fill_mask,
                                                    &x, &y, NULL, NULL);
-      if (line_art)
-        g_object_unref (line_art);
-
       if (fill)
         {
           gegl_node_set (tool->priv->fill_node,
@@ -430,12 +398,6 @@ gimp_bucket_fill_tool_commit (GimpBucketFillTool *tool)
 {
   if (tool->priv->filter)
     {
-      GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
-      /* Make sure the drawable will signal being painted. */
-      if (! options->sample_merged)
-        tool->priv->fill_in_progress = FALSE;
-
       gimp_drawable_filter_commit (tool->priv->filter,
                                    GIMP_PROGRESS (tool), FALSE);
       gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tool)->display));
@@ -458,8 +420,7 @@ gimp_bucket_fill_tool_halt (GimpBucketFillTool *tool)
     }
   g_clear_object (&tool->priv->fill_mask);
 
-  tool->priv->fill_in_progress            = FALSE;
-  tool->priv->compute_line_art_after_fill = FALSE;
+  gimp_line_art_thaw (tool->priv->line_art);
 
   GIMP_TOOL (tool)->display  = NULL;
   GIMP_TOOL (tool)->drawable = NULL;
@@ -593,8 +554,6 @@ gimp_bucket_fill_tool_motion (GimpTool         *tool,
       GimpFillOptions *fill_options;
       GError          *error = NULL;
 
-      g_return_if_fail (bucket_tool->priv->fill_in_progress);
-
       fill_options = gimp_fill_options_new (image->gimp, NULL, FALSE);
 
       if (gimp_fill_options_set_by_fill_mode (fill_options, context,
@@ -755,92 +714,6 @@ gimp_bucket_fill_tool_cursor_update (GimpTool         *tool,
   GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
 }
 
-static void
-gimp_bucket_fill_compute_line_art_cb (GimpAsync          *async,
-                                      GimpBucketFillTool *tool)
-{
-  if (gimp_async_is_canceled (async))
-    return;
-
-  if (gimp_async_is_finished (async))
-    {
-      GimpPickableLineArtAsyncResult *result;
-
-      result = gimp_async_get_result (async);
-
-      tool->priv->line_art = g_object_ref (result->line_art);
-      tool->priv->distmap  = result->distmap;
-      result->distmap  = NULL;
-    }
-
-  g_clear_object (&tool->priv->async);
-}
-
-static void
-gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool)
-{
-  GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
-  if (tool->priv->fill_in_progress)
-    {
-      tool->priv->compute_line_art_after_fill = TRUE;
-      return;
-    }
-
-  if (tool->priv->async)
-    {
-      gimp_cancelable_cancel (GIMP_CANCELABLE (tool->priv->async));
-      g_clear_object (&tool->priv->async);
-    }
-
-  g_clear_object (&tool->priv->line_art);
-  g_clear_pointer (&tool->priv->distmap, g_free);
-
-  if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
-    {
-      GimpPickable *pickable = NULL;
-      GimpDrawable *image    = g_weak_ref_get (&tool->priv->cached_image);
-      GimpDrawable *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
-      if (image && options->sample_merged)
-        pickable = GIMP_PICKABLE (image);
-      else if (drawable && ! options->sample_merged)
-        pickable = GIMP_PICKABLE (drawable);
-
-      if (pickable)
-        {
-          /* gimp_pickable_contiguous_region_prepare_line_art_async()
-           * will flush the pickable, which may trigger this signal
-           * handler, and will leak a line art (as tool->priv->async has
-           * not been set yet.
-           */
-          g_signal_handlers_block_by_func (gimp_image_get_projection (GIMP_IMAGE (image)),
-                                           G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
-                                           tool);
-          tool->priv->async =
-            gimp_pickable_contiguous_region_prepare_line_art_async (
-              pickable,
-              options->fill_transparent,
-              options->line_art_threshold,
-              options->line_art_segment_max_len,
-              options->line_art_spline_max_len,
-              +1);
-          g_signal_handlers_unblock_by_func (gimp_image_get_projection (GIMP_IMAGE (image)),
-                                           G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
-                                           tool);
-
-          gimp_async_add_callback_for_object (
-            tool->priv->async,
-            (GimpAsyncCallback) gimp_bucket_fill_compute_line_art_cb,
-            tool,
-            tool);
-        }
-
-      g_clear_object (&image);
-      g_clear_object (&drawable);
-    }
-}
-
 static gboolean
 gimp_bucket_fill_tool_connect_handlers (gpointer data)
 {
@@ -859,13 +732,6 @@ gimp_bucket_fill_tool_connect_handlers (gpointer data)
       g_signal_connect (options, "notify::sample-merged",
                         G_CALLBACK (gimp_bucket_fill_tool_options_notified),
                         tool);
-      g_signal_connect (options, "notify::fill-transparent",
-                        G_CALLBACK (gimp_bucket_fill_tool_options_notified),
-                        tool);
-      g_signal_connect (options, "notify::line-art-threshold",
-                        G_CALLBACK (gimp_bucket_fill_tool_options_notified),
-                        tool);
-
       g_signal_connect (options, "notify::fill-mode",
                         G_CALLBACK (gimp_bucket_fill_tool_options_notified),
                         tool);
@@ -885,13 +751,10 @@ gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
                                         GParamSpec            *pspec,
                                         GimpBucketFillTool    *tool)
 {
-  if ((! strcmp (pspec->name, "fill-criterion")     ||
-       ! strcmp (pspec->name, "fill-transparent")   ||
-       ! strcmp (pspec->name, "line-art-threshold") ||
-       ! strcmp (pspec->name, "sample-merged")) &&
-      options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
+  if (! strcmp (pspec->name, "fill-criterion") ||
+      ! strcmp (pspec->name, "sample-merged"))
     {
-      gimp_bucket_fill_compute_line_art (tool);
+      gimp_bucket_fill_reset_line_art (tool, options);
     }
   else if (! strcmp (pspec->name, "fill-mode"))
     {
@@ -920,6 +783,49 @@ gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
     }
 }
 
+static void
+gimp_bucket_fill_reset_line_art (GimpBucketFillTool    *tool,
+                                 GimpBucketFillOptions *options)
+{
+  GimpImage   *prev_image = g_weak_ref_get (&tool->priv->cached_image);
+  GimpContext *context    = gimp_get_user_context (GIMP_CONTEXT (options)->gimp);
+  GimpImage   *image      = gimp_context_get_image (context);
+
+  if (prev_image)
+    {
+      g_signal_handlers_disconnect_by_data (prev_image, tool);
+      g_object_unref (prev_image);
+    }
+  g_weak_ref_set (&tool->priv->cached_image, image ? image : NULL);
+  g_weak_ref_set (&tool->priv->cached_drawable, NULL);
+
+  if (image && options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
+    {
+      GimpBucketFillOptions *options  = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
+      GimpDrawable          *drawable = gimp_image_get_active_drawable (image);
+
+      g_signal_connect (image, "active-layer-changed",
+                        G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
+                        tool);
+      g_signal_connect (image, "active-channel-changed",
+                        G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
+                        tool);
+
+      g_weak_ref_set (&tool->priv->cached_drawable, drawable ? drawable : NULL);
+
+      if (options->sample_merged)
+        gimp_line_art_set_input (tool->priv->line_art, GIMP_PICKABLE (image));
+      else if (drawable)
+        gimp_line_art_set_input (tool->priv->line_art, GIMP_PICKABLE (drawable));
+      else
+        gimp_line_art_set_input (tool->priv->line_art, NULL);
+    }
+  else
+    {
+      gimp_line_art_set_input (tool->priv->line_art, NULL);
+    }
+}
+
 static void
 gimp_bucket_fill_tool_image_changed (GimpContext        *context,
                                      GimpImage          *image,
@@ -929,37 +835,32 @@ gimp_bucket_fill_tool_image_changed (GimpContext        *context,
 
   if (image != prev_image)
     {
-      GimpImage *prev_drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
-      g_clear_object (&tool->priv->line_art);
-      g_clear_pointer (&tool->priv->distmap, g_free);
-
       if (prev_image)
         {
           g_signal_handlers_disconnect_by_data (prev_image, tool);
-          g_signal_handlers_disconnect_by_data (gimp_image_get_projection (prev_image),
-                                                tool);
-        }
-      if (prev_drawable)
-        {
-          g_signal_handlers_disconnect_by_data (prev_drawable, tool);
-          g_object_unref (prev_drawable);
         }
 
       g_weak_ref_set (&tool->priv->cached_image, image ? image : NULL);
       g_weak_ref_set (&tool->priv->cached_drawable, NULL);
       if (image)
         {
+          GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
+
           g_signal_connect (image, "active-layer-changed",
                             G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
                             tool);
           g_signal_connect (image, "active-channel-changed",
                             G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
                             tool);
-          g_signal_connect (gimp_image_get_projection (image), "rendered",
-                            G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
-                            tool);
           gimp_bucket_fill_tool_drawable_changed (image, tool);
+
+          if (options->sample_merged)
+            gimp_line_art_set_input (tool->priv->line_art,
+                                     GIMP_PICKABLE (image));
+        }
+      else
+        {
+          gimp_line_art_set_input (tool->priv->line_art, NULL);
         }
     }
   if (prev_image)
@@ -975,37 +876,13 @@ gimp_bucket_fill_tool_drawable_changed (GimpImage          *image,
 
   if (drawable != prev_drawable)
     {
-      if (prev_drawable)
-        g_signal_handlers_disconnect_by_data (prev_drawable, tool);
+      GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
 
       g_weak_ref_set (&tool->priv->cached_drawable, drawable ? drawable : NULL);
-      if (drawable)
-        g_signal_connect (drawable, "painted",
-                          G_CALLBACK (gimp_bucket_fill_tool_drawable_painted),
-                          tool);
-
-      gimp_bucket_fill_compute_line_art (tool);
+      if (! options->sample_merged)
+        gimp_line_art_set_input (tool->priv->line_art,
+                                 drawable ? GIMP_PICKABLE (drawable) : NULL);
     }
   if (prev_drawable)
     g_object_unref (prev_drawable);
 }
-
-static void
-gimp_bucket_fill_tool_projection_rendered (GimpProjection     *proj,
-                                           GimpBucketFillTool *tool)
-{
-  GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
-  if (options->sample_merged)
-    gimp_bucket_fill_compute_line_art (tool);
-}
-
-static void
-gimp_bucket_fill_tool_drawable_painted (GimpDrawable       *drawable,
-                                        GimpBucketFillTool *tool)
-{
-  GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
-  if (! options->sample_merged)
-    gimp_bucket_fill_compute_line_art (tool);
-}
diff --git a/app/tools/gimpfuzzyselecttool.c b/app/tools/gimpfuzzyselecttool.c
index de86b3c171..b3eee7d196 100644
--- a/app/tools/gimpfuzzyselecttool.c
+++ b/app/tools/gimpfuzzyselecttool.c
@@ -122,12 +122,11 @@ gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select,
       pickable = GIMP_PICKABLE (image);
     }
 
-  return gimp_pickable_contiguous_region_by_seed (pickable, NULL, NULL,
+  return gimp_pickable_contiguous_region_by_seed (pickable, NULL,
                                                   sel_options->antialias,
                                                   options->threshold / 255.0,
                                                   options->select_transparent,
                                                   options->select_criterion,
                                                   options->diagonal_neighbors,
-                                                  0.92, 3, 20, 60, /* TODO */
                                                   x, y);
 }
diff --git a/pdb/groups/drawable_edit.pdb b/pdb/groups/drawable_edit.pdb
index 2be98f4456..79b8c395c5 100644
--- a/pdb/groups/drawable_edit.pdb
+++ b/pdb/groups/drawable_edit.pdb
@@ -169,13 +169,12 @@ HELP
       if (gimp_fill_options_set_by_fill_type (options, context,
                                               fill_type, error))
         {
-          gimp_drawable_bucket_fill (drawable, NULL, NULL, options,
+          gimp_drawable_bucket_fill (drawable, NULL, options,
                                      GIMP_PDB_CONTEXT (context)->sample_transparent,
                                      GIMP_PDB_CONTEXT (context)->sample_criterion,
                                      GIMP_PDB_CONTEXT (context)->sample_threshold,
                                      GIMP_PDB_CONTEXT (context)->sample_merged,
                                      GIMP_PDB_CONTEXT (context)->diagonal_neighbors,
-                                     0.92, 3, 20, 60, /* TODO */
                                      x, y);
         }
       else



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