[gimp] app: do not make line art bucket fill a GimpSelectCriterion anymore.



commit cd924f453ac149b55b3c8cfbafb9212950551e9e
Author: Jehan <jehan girinstud io>
Date:   Tue Dec 11 18:05:12 2018 +0100

    app: do not make line art bucket fill a GimpSelectCriterion anymore.
    
    This was my initial choice, but the more I think about it, the less I am
    sure this was the right choice. There was some common code (as I was
    making a common composite bucket fill once the line art was generated),
    but there is also a lot of different code and the functions were filled
    of exception when we were doing a line art fill. Also though there is a
    bit of color works (the way we decide whether a pixel is part of a
    stroke or not, though currently this is basic grayscale threshold), this
    is really not the same as other criterions. In particular this was made
    obvious on the Select by Color tool where the line art criterion was
    completely meaningless and would have had to be opted-out!
    
    This commit split a bit the code. Instead of finding the line art in the
    criterion list, I add a third choice to the "Fill whole selection"/"Fill
    similar colors" radio. In turn I create a new GimpBucketFillArea type
    with the 3 choices, and remove line art value from GimpSelectCriterion.
    
    I am not fully happy yet of this code, as it creates a bit of duplicate
    code, and I would appreciate to move some code away from gimpdrawable-*
    and gimppickable-* files. This may happen later. I break the work in
    pieces to not get too messy.
    Also this removes access to the smart colorization from the API, but
    that's probably ok as I prefer to not freeze options too early in the
    process since API needs to be stable. Probably we should get a concept
    of experimental API.

 app/core/gimpchannel-select.c             |   1 -
 app/core/gimpdrawable-bucket-fill.c       | 269 +++++++++++++++++++-----
 app/core/gimpdrawable-bucket-fill.h       |  59 +++---
 app/core/gimppickable-contiguous-region.c | 327 +++++++++++++++---------------
 app/core/gimppickable-contiguous-region.h |   5 +-
 app/pdb/drawable-edit-cmds.c              |   2 +-
 app/tools/gimpbucketfilloptions.c         | 119 +++++------
 app/tools/gimpbucketfilloptions.h         |   2 +-
 app/tools/gimpbucketfilltool.c            | 131 ++++++------
 app/tools/gimpfuzzyselecttool.c           |   2 +-
 data/tool-presets/FX/Fill-Paper.gtp       |   2 +-
 libgimp/gimpenums.c.tail                  |   2 +
 libgimpbase/gimpbaseenums.c               |  34 +++-
 libgimpbase/gimpbaseenums.h               |  22 +-
 libgimpwidgets/gimppropwidgets.c          |   3 +-
 pdb/enums.pl                              |  16 +-
 pdb/groups/drawable_edit.pdb              |   2 +-
 17 files changed, 622 insertions(+), 376 deletions(-)
---
diff --git a/app/core/gimpchannel-select.c b/app/core/gimpchannel-select.c
index dd6c433aee..e0cfc27354 100644
--- a/app/core/gimpchannel-select.c
+++ b/app/core/gimpchannel-select.c
@@ -516,7 +516,6 @@ gimp_channel_select_fuzzy (GimpChannel         *channel,
     pickable = GIMP_PICKABLE (drawable);
 
   add_on = gimp_pickable_contiguous_region_by_seed (pickable,
-                                                    NULL,
                                                     antialias,
                                                     threshold,
                                                     select_transparent,
diff --git a/app/core/gimpdrawable-bucket-fill.c b/app/core/gimpdrawable-bucket-fill.c
index 030c6edcde..e7dcc95970 100644
--- a/app/core/gimpdrawable-bucket-fill.c
+++ b/app/core/gimpdrawable-bucket-fill.c
@@ -49,7 +49,6 @@
 
 void
 gimp_drawable_bucket_fill (GimpDrawable         *drawable,
-                           GimpLineArt          *line_art,
                            GimpFillOptions      *options,
                            gboolean              fill_transparent,
                            GimpSelectCriterion   fill_criterion,
@@ -71,7 +70,7 @@ 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, options,
+  buffer = gimp_drawable_get_bucket_fill_buffer (drawable, options,
                                                  fill_transparent, fill_criterion,
                                                  threshold, sample_merged,
                                                  diagonal_neighbors,
@@ -100,9 +99,7 @@ gimp_drawable_bucket_fill (GimpDrawable         *drawable,
 
 /**
  * gimp_drawable_get_bucket_fill_buffer:
- * @drawable: the @GimpDrawable to edit.
- * @line_art: optional pre-computed line art if @fill_criterion is
- *            GIMP_SELECT_CRITERION_LINE_ART.
+ * @drawable: the #GimpDrawable to edit.
  * @options:
  * @fill_transparent:
  * @fill_criterion:
@@ -131,7 +128,6 @@ gimp_drawable_bucket_fill (GimpDrawable         *drawable,
  */
 GeglBuffer *
 gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
-                                      GimpLineArt          *line_art,
                                       GimpFillOptions      *options,
                                       gboolean              fill_transparent,
                                       GimpSelectCriterion   fill_criterion,
@@ -166,9 +162,7 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
                                   &sel_x, &sel_y, &sel_width, &sel_height))
     return NULL;
 
-  if (mask_buffer && *mask_buffer &&
-      (fill_criterion == GIMP_SELECT_CRITERION_LINE_ART ||
-       threshold      == 0.0))
+  if (mask_buffer && *mask_buffer && threshold == 0.0)
     {
       gfloat pixel;
 
@@ -193,7 +187,6 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
    *  contiguous region.
    */
   new_mask = gimp_pickable_contiguous_region_by_seed (pickable,
-                                                      line_art,
                                                       antialias,
                                                       threshold,
                                                       fill_transparent,
@@ -275,52 +268,224 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
       mask_offset_y = y;
     }
 
-  if (fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
+  buffer = gimp_fill_options_create_buffer (options, drawable,
+                                            GEGL_RECTANGLE (0, 0,
+                                                            width, height),
+                                            -x, -y);
+
+  gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask,
+                           -mask_offset_x, -mask_offset_y, 1.0);
+
+  if (mask_x)
+    *mask_x = x;
+  if (mask_y)
+    *mask_y = y;
+  if (mask_width)
+    *mask_width = width;
+  if (mask_height)
+    *mask_height = height;
+
+  if (! mask_buffer)
+    g_object_unref (new_mask);
+
+  gimp_unset_busy (image->gimp);
+
+  return buffer;
+}
+
+/**
+ * gimp_drawable_get_line_art_fill_buffer:
+ * @drawable: the #GimpDrawable to edit.
+ * @line_art: the #GimpLineArt computed as fill source.
+ * @options: the #GimpFillOptions.
+ * @sample_merged:
+ * @seed_x: X coordinate to start the fill.
+ * @seed_y: Y coordinate to start the fill.
+ * @mask_buffer: mask of the fill in-progress when in an interactive
+ *               filling process. Set to NULL if you need a one-time
+ *               fill.
+ * @mask_x: returned x bound of @mask_buffer.
+ * @mask_y: returned x bound of @mask_buffer.
+ * @mask_width: returned width bound of @mask_buffer.
+ * @mask_height: returned height bound of @mask_buffer.
+ *
+ * Creates the fill buffer for a bucket fill operation on @drawable
+ * based on @line_art and @options, without actually applying it.
+ * If @mask_buffer is not NULL, the intermediate fill mask will also be
+ * returned. This fill mask can later be reused in successive calls to
+ * gimp_drawable_get_bucket_fill_buffer() for interactive filling.
+ *
+ * Returns: a fill buffer which can be directly applied to @drawable, or
+ *          used in a drawable filter as preview.
+ */
+GeglBuffer *
+gimp_drawable_get_line_art_fill_buffer (GimpDrawable     *drawable,
+                                        GimpLineArt      *line_art,
+                                        GimpFillOptions  *options,
+                                        gboolean          sample_merged,
+                                        gdouble           seed_x,
+                                        gdouble           seed_y,
+                                        GeglBuffer      **mask_buffer,
+                                        gdouble          *mask_x,
+                                        gdouble          *mask_y,
+                                        gint             *mask_width,
+                                        gint             *mask_height)
+{
+  GeglBufferIterator *gi;
+  GimpImage    *image;
+  GeglBuffer   *buffer;
+  GeglBuffer   *new_mask;
+  gint          x, y, width, height;
+  gint          mask_offset_x = 0;
+  gint          mask_offset_y = 0;
+  gint          sel_x, sel_y, sel_width, sel_height;
+
+  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
+  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
+  g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL);
+
+  image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+  if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
+                                  &sel_x, &sel_y, &sel_width, &sel_height))
+    return NULL;
+
+  if (mask_buffer && *mask_buffer)
     {
-      /* The smart colorization leaves some very irritating unselected
-       * pixels in some edge cases. Just flood any isolated pixel inside
-       * the final mask.
-       */
-      GeglBufferIterator *gi;
-
-      gi = gegl_buffer_iterator_new (new_mask, GEGL_RECTANGLE (x, y, width, height),
-                                     0, NULL, GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 5);
-      gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x, y - 1, width, height),
-                                0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
-      gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x, y + 1, width, height),
-                                0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
-      gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x - 1, y, width, height),
-                                0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
-      gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x + 1, y, width, height),
-                                0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
-      while (gegl_buffer_iterator_next (gi))
+      gfloat pixel;
+
+      gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel,
+                          babl_format ("Y float"),
+                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+      if (pixel != 0.0)
+        /* Already selected. This seed won't change the selection. */
+        return NULL;
+    }
+
+  gimp_set_busy (image->gimp);
+
+  /*  Do a seed bucket fill...To do this, calculate a new
+   *  contiguous region.
+   */
+  new_mask = gimp_pickable_contiguous_region_by_line_art (NULL, line_art,
+                                                          (gint) seed_x,
+                                                          (gint) seed_y);
+  if (mask_buffer && *mask_buffer)
+    {
+      gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer,
+                                     GIMP_CHANNEL_OP_ADD, 0, 0);
+      g_object_unref (*mask_buffer);
+    }
+  if (mask_buffer)
+    *mask_buffer = new_mask;
+
+  gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height);
+  width  -= x;
+  height -= y;
+
+  /*  If there is a selection, intersect the region bounds
+   *  with the selection bounds, to avoid processing areas
+   *  that are going to be masked out anyway.  The actual
+   *  intersection of the fill region with the mask data
+   *  happens when combining the fill buffer, in
+   *  gimp_drawable_apply_buffer().
+   */
+  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+    {
+      gint off_x = 0;
+      gint off_y = 0;
+
+      if (sample_merged)
+        gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      if (! gimp_rectangle_intersect (x, y, width, height,
+
+                                      sel_x + off_x, sel_y + off_y,
+                                      sel_width,     sel_height,
+
+                                      &x, &y, &width, &height))
         {
-          gfloat *m      = (gfloat*) gi->items[0].data;
-          gfloat *py     = (gfloat*) gi->items[1].data;
-          gfloat *ny     = (gfloat*) gi->items[2].data;
-          gfloat *px     = (gfloat*) gi->items[3].data;
-          gfloat *nx     = (gfloat*) gi->items[4].data;
-          gint    startx = gi->items[0].roi.x;
-          gint    starty = gi->items[0].roi.y;
-          gint    endy   = starty + gi->items[0].roi.height;
-          gint    endx   = startx + gi->items[0].roi.width;
-          gint    i;
-          gint    j;
-
-          for (j = starty; j < endy; j++)
-            for (i = startx; i < endx; i++)
-              {
-                if (! *m && *py && *ny && *px && *nx)
-                  *m = 1.0;
-                m++;
-                py++;
-                ny++;
-                px++;
-                nx++;
-              }
+          if (! mask_buffer)
+            g_object_unref (new_mask);
+          /*  The fill region and the selection are disjoint; bail.  */
+          gimp_unset_busy (image->gimp);
+
+          return NULL;
         }
     }
 
+  /*  make sure we handle the mask correctly if it was sample-merged  */
+  if (sample_merged)
+    {
+      GimpItem *item = GIMP_ITEM (drawable);
+      gint      off_x, off_y;
+
+      /*  Limit the channel bounds to the drawable's extents  */
+      gimp_item_get_offset (item, &off_x, &off_y);
+
+      gimp_rectangle_intersect (x, y, width, height,
+
+                                off_x, off_y,
+                                gimp_item_get_width (item),
+                                gimp_item_get_height (item),
+
+                                &x, &y, &width, &height);
+
+      mask_offset_x = x;
+      mask_offset_y = y;
+
+     /*  translate mask bounds to drawable coords  */
+      x -= off_x;
+      y -= off_y;
+    }
+  else
+    {
+      mask_offset_x = x;
+      mask_offset_y = y;
+    }
+
+  /* The smart colorization leaves some very irritating unselected
+   * pixels in some edge cases. Just flood any isolated pixel inside
+   * the final mask.
+   */
+  gi = gegl_buffer_iterator_new (new_mask, GEGL_RECTANGLE (x, y, width, height),
+                                 0, NULL, GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 5);
+  gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x, y - 1, width, height),
+                            0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
+  gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x, y + 1, width, height),
+                            0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
+  gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x - 1, y, width, height),
+                            0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
+  gegl_buffer_iterator_add (gi, new_mask, GEGL_RECTANGLE (x + 1, y, width, height),
+                            0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_WHITE);
+  while (gegl_buffer_iterator_next (gi))
+    {
+      gfloat *m      = (gfloat*) gi->items[0].data;
+      gfloat *py     = (gfloat*) gi->items[1].data;
+      gfloat *ny     = (gfloat*) gi->items[2].data;
+      gfloat *px     = (gfloat*) gi->items[3].data;
+      gfloat *nx     = (gfloat*) gi->items[4].data;
+      gint    startx = gi->items[0].roi.x;
+      gint    starty = gi->items[0].roi.y;
+      gint    endy   = starty + gi->items[0].roi.height;
+      gint    endx   = startx + gi->items[0].roi.width;
+      gint    i;
+      gint    j;
+
+      for (j = starty; j < endy; j++)
+        for (i = startx; i < endx; i++)
+          {
+            if (! *m && *py && *ny && *px && *nx)
+              *m = 1.0;
+            m++;
+            py++;
+            ny++;
+            px++;
+            nx++;
+          }
+    }
+
   buffer = gimp_fill_options_create_buffer (options, drawable,
                                             GEGL_RECTANGLE (0, 0,
                                                             width, height),
@@ -329,7 +494,7 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
   gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask,
                            -mask_offset_x, -mask_offset_y, 1.0);
 
-  if (fill_criterion == GIMP_SELECT_CRITERION_LINE_ART && antialias)
+  if (gimp_fill_options_get_antialias (options))
     {
       /* Antialias for the line art algorithm is not applied during mask
        * creation because it is not based on individual pixel colors.
diff --git a/app/core/gimpdrawable-bucket-fill.h b/app/core/gimpdrawable-bucket-fill.h
index e2b2525d45..ef9acfb13f 100644
--- a/app/core/gimpdrawable-bucket-fill.h
+++ b/app/core/gimpdrawable-bucket-fill.h
@@ -19,31 +19,40 @@
 #define  __GIMP_DRAWABLE_BUCKET_FILL_H__
 
 
-void         gimp_drawable_bucket_fill            (GimpDrawable         *drawable,
-                                                   GimpLineArt          *line_art,
-                                                   GimpFillOptions      *options,
-                                                   gboolean              fill_transparent,
-                                                   GimpSelectCriterion   fill_criterion,
-                                                   gdouble               threshold,
-                                                   gboolean              sample_merged,
-                                                   gboolean              diagonal_neighbors,
-                                                   gdouble               x,
-                                                   gdouble               y);
-GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
-                                                   GimpLineArt          *line_art,
-                                                   GimpFillOptions      *options,
-                                                   gboolean              fill_transparent,
-                                                   GimpSelectCriterion   fill_criterion,
-                                                   gdouble               threshold,
-                                                   gboolean              sample_merged,
-                                                   gboolean              diagonal_neighbors,
-                                                   gdouble               seed_x,
-                                                   gdouble               seed_y,
-                                                   GeglBuffer          **mask_buffer,
-                                                   gdouble              *mask_x,
-                                                   gdouble              *mask_y,
-                                                   gint                 *mask_width,
-                                                   gint                 *mask_height);
+void         gimp_drawable_bucket_fill              (GimpDrawable         *drawable,
+                                                     GimpFillOptions      *options,
+                                                     gboolean              fill_transparent,
+                                                     GimpSelectCriterion   fill_criterion,
+                                                     gdouble               threshold,
+                                                     gboolean              sample_merged,
+                                                     gboolean              diagonal_neighbors,
+                                                     gdouble               x,
+                                                     gdouble               y);
+GeglBuffer * gimp_drawable_get_bucket_fill_buffer   (GimpDrawable         *drawable,
+                                                     GimpFillOptions      *options,
+                                                     gboolean              fill_transparent,
+                                                     GimpSelectCriterion   fill_criterion,
+                                                     gdouble               threshold,
+                                                     gboolean              sample_merged,
+                                                     gboolean              diagonal_neighbors,
+                                                     gdouble               seed_x,
+                                                     gdouble               seed_y,
+                                                     GeglBuffer          **mask_buffer,
+                                                     gdouble              *mask_x,
+                                                     gdouble              *mask_y,
+                                                     gint                 *mask_width,
+                                                     gint                 *mask_height);
 
+GeglBuffer * gimp_drawable_get_line_art_fill_buffer (GimpDrawable         *drawable,
+                                                     GimpLineArt          *line_art,
+                                                     GimpFillOptions      *options,
+                                                     gboolean              sample_merged,
+                                                     gdouble               seed_x,
+                                                     gdouble               seed_y,
+                                                     GeglBuffer          **mask_buffer,
+                                                     gdouble              *mask_x,
+                                                     gdouble              *mask_y,
+                                                     gint                 *mask_width,
+                                                     gint                 *mask_height);
 
 #endif  /*  __GIMP_DRAWABLE_BUCKET_FILL_H__  */
diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.c
index ebd7b2b530..b326bc287c 100644
--- a/app/core/gimppickable-contiguous-region.c
+++ b/app/core/gimppickable-contiguous-region.c
@@ -115,7 +115,6 @@ static void            line_art_queue_pixel (GQueue              *queue,
 
 GeglBuffer *
 gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
-                                         GimpLineArt         *line_art,
                                          gboolean             antialias,
                                          gfloat               threshold,
                                          gboolean             select_transparent,
@@ -127,103 +126,208 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
   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)
+  gimp_pickable_flush (pickable);
+  src_buffer = gimp_pickable_get_buffer (pickable);
+
+  format = choose_format (src_buffer, select_criterion,
+                          &n_components, &has_alpha);
+  gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format,
+                      GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+  if (has_alpha)
     {
-      if (! line_art)
+      if (select_transparent)
         {
-          /* 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).
+          /*  don't select transparent regions if the start pixel isn't
+           *  fully transparent
            */
-          line_art = gimp_line_art_new ();
-          gimp_line_art_set_input (line_art, pickable);
-          free_line_art = TRUE;
+          if (start_col[n_components - 1] > 0)
+            select_transparent = FALSE;
         }
+    }
+  else
+    {
+      select_transparent = FALSE;
+    }
 
-      src_buffer = gimp_line_art_get (line_art, &distmap);
-      g_return_val_if_fail (src_buffer && distmap, NULL);
+  extent = *gegl_buffer_get_extent (src_buffer);
 
-      smart_line_art     = TRUE;
-      antialias          = FALSE;
-      threshold          = 0.0;
-      select_transparent = FALSE;
-      select_criterion   = GIMP_SELECT_CRITERION_COMPOSITE;
-      diagonal_neighbors = FALSE;
+  mask_buffer = gegl_buffer_new (&extent, babl_format ("Y float"));
 
-      format = choose_format (src_buffer, select_criterion,
-                              &n_components, &has_alpha);
-      gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format,
-                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
-    }
-  else
+  if (x >= extent.x && x < (extent.x + extent.width) &&
+      y >= extent.y && y < (extent.y + extent.height))
     {
-      gimp_pickable_flush (pickable);
-      src_buffer = gimp_pickable_get_buffer (pickable);
+      GIMP_TIMER_START();
 
-      format = choose_format (src_buffer, select_criterion,
-                              &n_components, &has_alpha);
-      gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format,
-                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+      find_contiguous_region (src_buffer, mask_buffer,
+                              format, n_components, has_alpha,
+                              select_transparent, select_criterion,
+                              antialias, threshold, diagonal_neighbors,
+                              x, y, start_col);
 
-      if (has_alpha)
+      GIMP_TIMER_END("foo");
+    }
+
+  return mask_buffer;
+}
+
+GeglBuffer *
+gimp_pickable_contiguous_region_by_color (GimpPickable        *pickable,
+                                          gboolean             antialias,
+                                          gfloat               threshold,
+                                          gboolean             select_transparent,
+                                          GimpSelectCriterion  select_criterion,
+                                          const GimpRGB       *color)
+{
+  /*  Scan over the pickable's active layer, finding pixels within the
+   *  specified threshold from the given R, G, & B values.  If
+   *  antialiasing is on, use the same antialiasing scheme as in
+   *  fuzzy_select.  Modify the pickable's mask to reflect the
+   *  additional selection
+   */
+  GeglBufferIterator *iter;
+  GeglBuffer         *src_buffer;
+  GeglBuffer         *mask_buffer;
+  const Babl         *format;
+  gint                n_components;
+  gboolean            has_alpha;
+  gfloat              start_col[MAX_CHANNELS];
+
+  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
+  g_return_val_if_fail (color != NULL, NULL);
+
+  gimp_pickable_flush (pickable);
+
+  src_buffer = gimp_pickable_get_buffer (pickable);
+
+  format = choose_format (src_buffer, select_criterion,
+                          &n_components, &has_alpha);
+
+  gimp_rgba_get_pixel (color, format, start_col);
+
+  if (has_alpha)
+    {
+      if (select_transparent)
         {
-          if (select_transparent)
-            {
-              /*  don't select transparent regions if the start pixel isn't
-               *  fully transparent
-               */
-              if (start_col[n_components - 1] > 0)
-                select_transparent = FALSE;
-            }
+          /*  don't select transparency if "color" isn't fully transparent
+           */
+          if (start_col[n_components - 1] > 0.0)
+            select_transparent = FALSE;
         }
-      else
+    }
+  else
+    {
+      select_transparent = FALSE;
+    }
+
+  mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer),
+                                 babl_format ("Y float"));
+
+  iter = gegl_buffer_iterator_new (src_buffer,
+                                   NULL, 0, format,
+                                   GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+  gegl_buffer_iterator_add (iter, mask_buffer,
+                            NULL, 0, babl_format ("Y float"),
+                            GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+  while (gegl_buffer_iterator_next (iter))
+    {
+      const gfloat *src   = iter->items[0].data;
+      gfloat       *dest  = iter->items[1].data;
+      gint          count = iter->length;
+
+      while (count--)
         {
-          select_transparent = FALSE;
+          /*  Find how closely the colors match  */
+          *dest = pixel_difference (start_col, src,
+                                    antialias,
+                                    threshold,
+                                    n_components,
+                                    has_alpha,
+                                    select_transparent,
+                                    select_criterion);
+
+          src  += n_components;
+          dest += 1;
         }
     }
 
+  return mask_buffer;
+}
+
+GeglBuffer *
+gimp_pickable_contiguous_region_by_line_art (GimpPickable *pickable,
+                                             GimpLineArt  *line_art,
+                                             gint          x,
+                                             gint          y)
+{
+  GeglBuffer    *src_buffer;
+  GeglBuffer    *mask_buffer;
+  const Babl    *format  = babl_format ("Y float");
+  gfloat        *distmap = NULL;
+  GeglRectangle  extent;
+  gfloat         start_col;
+  gboolean       free_line_art  = FALSE;
+  gint           line_art_max_grow;
+
+  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable) || GIMP_IS_LINE_ART (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_line_art_new ();
+      gimp_line_art_set_input (line_art, pickable);
+      free_line_art = TRUE;
+    }
+
+  src_buffer = gimp_line_art_get (line_art, &distmap);
+  g_return_val_if_fail (src_buffer && distmap, NULL);
+
+  gegl_buffer_sample (src_buffer, x, y, NULL, &start_col, format,
+                      GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
   extent = *gegl_buffer_get_extent (src_buffer);
 
   mask_buffer = gegl_buffer_new (&extent, babl_format ("Y float"));
 
-  if (smart_line_art && start_col[0])
+  if (start_col)
     {
       /* As a special exception, if you fill over a line art pixel, only
        * fill the pixel and exit
        */
-      start_col[0] = 1.0;
+      start_col = 1.0;
       gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (x, y, 1, 1),
-                       0, babl_format ("Y float"), start_col,
+                       0, babl_format ("Y float"), &start_col,
                        GEGL_AUTO_ROWSTRIDE);
-      smart_line_art = FALSE;
     }
   else if (x >= extent.x && x < (extent.x + extent.width) &&
-      y >= extent.y && y < (extent.y + extent.height))
+           y >= extent.y && y < (extent.y + extent.height))
     {
+      gfloat *mask;
+      GQueue *queue  = g_queue_new ();
+      gint    width  = gegl_buffer_get_width (src_buffer);
+      gint    height = gegl_buffer_get_height (src_buffer);
+      gint    nx, ny;
+
       GIMP_TIMER_START();
 
       find_contiguous_region (src_buffer, mask_buffer,
-                              format, n_components, has_alpha,
-                              select_transparent, select_criterion,
-                              antialias, threshold, diagonal_neighbors,
-                              x, y, start_col);
+                              format, 1, FALSE,
+                              FALSE, GIMP_SELECT_CRITERION_COMPOSITE,
+                              FALSE, 0.0, FALSE,
+                              x, y, &start_col);
 
-      GIMP_TIMER_END("foo");
-    }
-
-  if (smart_line_art)
-    {
       /* The last step of the line art algorithm is to make sure that
        * selections does not leave "holes" between its borders and the
        * line arts, while not stepping over as well.
@@ -231,14 +335,6 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
        * the stroke pixels, but for such simple need, this simple code
        * is so much faster while producing better results.
        */
-      gfloat *mask;
-      GQueue *queue  = g_queue_new ();
-      gint    width  = gegl_buffer_get_width (src_buffer);
-      gint    height = gegl_buffer_get_height (src_buffer);
-      gint    nx, ny;
-
-      GIMP_TIMER_START();
-
       mask = g_new (gfloat, width * height);
       gegl_buffer_get (mask_buffer, NULL, 1.0, NULL,
                        mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
@@ -407,100 +503,13 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable        *pickable,
       g_free (mask);
 
       GIMP_TIMER_END("watershed line art");
-
-      if (free_line_art)
-        g_clear_object (&line_art);
-    }
-
-  return mask_buffer;
-}
-
-GeglBuffer *
-gimp_pickable_contiguous_region_by_color (GimpPickable        *pickable,
-                                          gboolean             antialias,
-                                          gfloat               threshold,
-                                          gboolean             select_transparent,
-                                          GimpSelectCriterion  select_criterion,
-                                          const GimpRGB       *color)
-{
-  /*  Scan over the pickable's active layer, finding pixels within the
-   *  specified threshold from the given R, G, & B values.  If
-   *  antialiasing is on, use the same antialiasing scheme as in
-   *  fuzzy_select.  Modify the pickable's mask to reflect the
-   *  additional selection
-   */
-  GeglBufferIterator *iter;
-  GeglBuffer         *src_buffer;
-  GeglBuffer         *mask_buffer;
-  const Babl         *format;
-  gint                n_components;
-  gboolean            has_alpha;
-  gfloat              start_col[MAX_CHANNELS];
-
-  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
-  g_return_val_if_fail (color != NULL, NULL);
-
-  gimp_pickable_flush (pickable);
-
-  src_buffer = gimp_pickable_get_buffer (pickable);
-
-  format = choose_format (src_buffer, select_criterion,
-                          &n_components, &has_alpha);
-
-  gimp_rgba_get_pixel (color, format, start_col);
-
-  if (has_alpha)
-    {
-      if (select_transparent)
-        {
-          /*  don't select transparency if "color" isn't fully transparent
-           */
-          if (start_col[n_components - 1] > 0.0)
-            select_transparent = FALSE;
-        }
-    }
-  else
-    {
-      select_transparent = FALSE;
-    }
-
-  mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer),
-                                 babl_format ("Y float"));
-
-  iter = gegl_buffer_iterator_new (src_buffer,
-                                   NULL, 0, format,
-                                   GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
-
-  gegl_buffer_iterator_add (iter, mask_buffer,
-                            NULL, 0, babl_format ("Y float"),
-                            GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
-  while (gegl_buffer_iterator_next (iter))
-    {
-      const gfloat *src   = iter->items[0].data;
-      gfloat       *dest  = iter->items[1].data;
-      gint          count = iter->length;
-
-      while (count--)
-        {
-          /*  Find how closely the colors match  */
-          *dest = pixel_difference (start_col, src,
-                                    antialias,
-                                    threshold,
-                                    n_components,
-                                    has_alpha,
-                                    select_transparent,
-                                    select_criterion);
-
-          src  += n_components;
-          dest += 1;
-        }
     }
+  if (free_line_art)
+    g_clear_object (&line_art);
 
   return mask_buffer;
 }
 
-
 /*  private functions  */
 
 static const Babl *
@@ -547,10 +556,6 @@ choose_format (GeglBuffer          *buffer,
       format = babl_format ("CIE LCH(ab) alpha float");
       break;
 
-    case GIMP_SELECT_CRITERION_LINE_ART:
-      format = babl_format ("Y'A float");
-      break;
-
     default:
       g_return_val_if_reached (NULL);
       break;
@@ -641,10 +646,6 @@ pixel_difference (const gfloat        *col1,
           max = fabs (col1[2] - col2[2]) / 360.0;
           max = MIN (max, 1.0 - max);
           break;
-
-        case GIMP_SELECT_CRITERION_LINE_ART:
-          /* Smart selection is handled before. */
-          g_return_val_if_reached (0.0);
         }
     }
 
diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h
index 9b4ffa544f..26cbe7476a 100644
--- a/app/core/gimppickable-contiguous-region.h
+++ b/app/core/gimppickable-contiguous-region.h
@@ -20,7 +20,6 @@
 
 
 GeglBuffer * gimp_pickable_contiguous_region_by_seed                (GimpPickable        *pickable,
-                                                                     GimpLineArt         *line_art,
                                                                      gboolean             antialias,
                                                                      gfloat               threshold,
                                                                      gboolean             select_transparent,
@@ -36,5 +35,9 @@ GeglBuffer * gimp_pickable_contiguous_region_by_color               (GimpPickabl
                                                                      GimpSelectCriterion  select_criterion,
                                                                      const GimpRGB       *color);
 
+GeglBuffer * gimp_pickable_contiguous_region_by_line_art            (GimpPickable        *pickable,
+                                                                     GimpLineArt         *line_art,
+                                                                     gint                 x,
+                                                                     gint                 y);
 
 #endif  /*  __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ */
diff --git a/app/pdb/drawable-edit-cmds.c b/app/pdb/drawable-edit-cmds.c
index a5212bb1cd..43ac00a7e6 100644
--- a/app/pdb/drawable-edit-cmds.c
+++ b/app/pdb/drawable-edit-cmds.c
@@ -165,7 +165,7 @@ 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, options,
+              gimp_drawable_bucket_fill (drawable, options,
                                          GIMP_PDB_CONTEXT (context)->sample_transparent,
                                          GIMP_PDB_CONTEXT (context)->sample_criterion,
                                          GIMP_PDB_CONTEXT (context)->sample_threshold,
diff --git a/app/tools/gimpbucketfilloptions.c b/app/tools/gimpbucketfilloptions.c
index d30b7e9b3d..b58e88a169 100644
--- a/app/tools/gimpbucketfilloptions.c
+++ b/app/tools/gimpbucketfilloptions.c
@@ -47,7 +47,7 @@ enum
 {
   PROP_0,
   PROP_FILL_MODE,
-  PROP_FILL_SELECTION,
+  PROP_FILL_AREA,
   PROP_FILL_TRANSPARENT,
   PROP_SAMPLE_MERGED,
   PROP_DIAGONAL_NEIGHBORS,
@@ -65,10 +65,8 @@ struct _GimpBucketFillOptionsPrivate
   GtkWidget *diagonal_neighbors_checkbox;
   GtkWidget *threshold_scale;
 
-  GtkWidget *line_art_threshold_scale;
-  GtkWidget *line_art_grow_scale;
-  GtkWidget *line_art_segment_max_len_scale;
-  GtkWidget *line_art_spline_max_len_scale;
+  GtkWidget *similar_color_frame;
+  GtkWidget *line_art_frame;
 };
 
 static void   gimp_bucket_fill_options_config_iface_init (GimpConfigInterface *config_iface);
@@ -83,7 +81,7 @@ static void   gimp_bucket_fill_options_get_property      (GObject
                                                           GParamSpec            *pspec);
 
 static void   gimp_bucket_fill_options_reset             (GimpConfig            *config);
-static void   gimp_bucket_fill_options_update_criterion  (GimpBucketFillOptions *options);
+static void   gimp_bucket_fill_options_update_area       (GimpBucketFillOptions *options);
 
 
 G_DEFINE_TYPE_WITH_CODE (GimpBucketFillOptions, gimp_bucket_fill_options,
@@ -113,12 +111,13 @@ gimp_bucket_fill_options_class_init (GimpBucketFillOptionsClass *klass)
                          GIMP_BUCKET_FILL_FG,
                          GIMP_PARAM_STATIC_STRINGS);
 
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FILL_SELECTION,
-                            "fill-selection",
-                            _("Fill selection"),
-                            _("Which area will be filled"),
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
+  GIMP_CONFIG_PROP_ENUM (object_class, PROP_FILL_AREA,
+                         "fill-area",
+                         _("Fill selection"),
+                         _("Which area will be filled"),
+                         GIMP_TYPE_BUCKET_FILL_AREA,
+                         GIMP_BUCKET_FILL_SIMILAR_COLORS,
+                         GIMP_PARAM_STATIC_STRINGS);
 
   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FILL_TRANSPARENT,
                             "fill-transparent",
@@ -224,8 +223,9 @@ gimp_bucket_fill_options_set_property (GObject      *object,
     case PROP_FILL_MODE:
       options->fill_mode = g_value_get_enum (value);
       break;
-    case PROP_FILL_SELECTION:
-      options->fill_selection = g_value_get_boolean (value);
+    case PROP_FILL_AREA:
+      options->fill_area = g_value_get_enum (value);
+      gimp_bucket_fill_options_update_area (options);
       break;
     case PROP_FILL_TRANSPARENT:
       options->fill_transparent = g_value_get_boolean (value);
@@ -256,7 +256,6 @@ gimp_bucket_fill_options_set_property (GObject      *object,
       break;
     case PROP_FILL_CRITERION:
       options->fill_criterion = g_value_get_enum (value);
-      gimp_bucket_fill_options_update_criterion (options);
       break;
 
     default:
@@ -278,8 +277,8 @@ gimp_bucket_fill_options_get_property (GObject    *object,
     case PROP_FILL_MODE:
       g_value_set_enum (value, options->fill_mode);
       break;
-    case PROP_FILL_SELECTION:
-      g_value_set_boolean (value, options->fill_selection);
+    case PROP_FILL_AREA:
+      g_value_set_enum (value, options->fill_area);
       break;
     case PROP_FILL_TRANSPARENT:
       g_value_set_boolean (value, options->fill_transparent);
@@ -335,31 +334,25 @@ gimp_bucket_fill_options_reset (GimpConfig *config)
 }
 
 static void
-gimp_bucket_fill_options_update_criterion (GimpBucketFillOptions *options)
+gimp_bucket_fill_options_update_area (GimpBucketFillOptions *options)
 {
   /* GUI not created yet. */
   if (! options->priv->threshold_scale)
     return;
 
-  switch (options->fill_criterion)
+  switch (options->fill_area)
     {
-    case GIMP_SELECT_CRITERION_LINE_ART:
-      gtk_widget_hide (options->priv->diagonal_neighbors_checkbox);
-      gtk_widget_hide (options->priv->threshold_scale);
-
-      gtk_widget_show (options->priv->line_art_threshold_scale);
-      gtk_widget_show (options->priv->line_art_grow_scale);
-      gtk_widget_show (options->priv->line_art_segment_max_len_scale);
-      gtk_widget_show (options->priv->line_art_spline_max_len_scale);
+    case GIMP_BUCKET_FILL_LINE_ART:
+      gtk_widget_hide (options->priv->similar_color_frame);
+      gtk_widget_show (options->priv->line_art_frame);
+      break;
+    case GIMP_BUCKET_FILL_SIMILAR_COLORS:
+      gtk_widget_show (options->priv->similar_color_frame);
+      gtk_widget_hide (options->priv->line_art_frame);
       break;
     default:
-      gtk_widget_hide (options->priv->line_art_threshold_scale);
-      gtk_widget_hide (options->priv->line_art_grow_scale);
-      gtk_widget_hide (options->priv->line_art_segment_max_len_scale);
-      gtk_widget_hide (options->priv->line_art_spline_max_len_scale);
-
-      gtk_widget_show (options->priv->diagonal_neighbors_checkbox);
-      gtk_widget_show (options->priv->threshold_scale);
+      gtk_widget_hide (options->priv->similar_color_frame);
+      gtk_widget_hide (options->priv->line_art_frame);
       break;
     }
 }
@@ -398,27 +391,18 @@ gimp_bucket_fill_options_gui (GimpToolOptions *tool_options)
   /*  fill selection  */
   str = g_strdup_printf (_("Affected Area  (%s)"),
                          gimp_get_mod_string (extend_mask));
-  frame = gimp_prop_boolean_radio_frame_new (config, "fill-selection",
-                                             str,
-                                             _("Fill whole selection"),
-                                             _("Fill similar colors"));
+  frame = gimp_prop_enum_radio_frame_new (config, "fill-area", str, 0, 0);
   g_free (str);
-  gtk_box_reorder_child (GTK_BOX (gtk_bin_get_child (GTK_BIN (frame))),
-                         g_object_get_data (G_OBJECT (frame), "radio-button"),
-                         1);
 
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
+  /* Similar color frame */
   frame = gimp_frame_new (_("Finding Similar Colors"));
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  options->priv->similar_color_frame = frame;
   gtk_widget_show (frame);
 
-  g_object_bind_property (config, "fill-selection",
-                          frame,  "sensitive",
-                          G_BINDING_SYNC_CREATE |
-                          G_BINDING_INVERT_BOOLEAN);
-
   vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
   gtk_container_add (GTK_CONTAINER (frame), vbox2);
   gtk_widget_show (vbox2);
@@ -451,41 +435,62 @@ gimp_bucket_fill_options_gui (GimpToolOptions *tool_options)
   options->priv->threshold_scale = scale;
   gtk_widget_show (scale);
 
+  /*  the fill criterion combo  */
+  combo = gimp_prop_enum_combo_box_new (config, "fill-criterion", 0, 0);
+  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Fill by"));
+  gtk_box_pack_start (GTK_BOX (vbox2), combo, FALSE, FALSE, 0);
+  gtk_widget_show (combo);
+
+  /* Line art frame */
+  frame = gimp_frame_new (_("Line art detection"));
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  options->priv->line_art_frame = frame;
+  gtk_widget_show (frame);
+
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
+
+  /*  the fill transparent areas toggle  */
+  button = gimp_prop_check_button_new (config, "fill-transparent", NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  /*  the sample merged toggle  */
+  button = gimp_prop_check_button_new (config, "sample-merged", NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  /*  the antialias toggle  */
+  button = gimp_prop_check_button_new (config, "antialias", NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
   /*  Line Art: max growing size */
   scale = gimp_prop_spin_scale_new (config, "line-art-max-grow", NULL,
                                     1, 5, 0);
   gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
-  options->priv->line_art_grow_scale = scale;
   gtk_widget_show (scale);
 
   /*  Line Art: stroke threshold */
   scale = gimp_prop_spin_scale_new (config, "line-art-threshold", NULL,
                                     0.05, 0.1, 2);
   gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
-  options->priv->line_art_threshold_scale = scale;
   gtk_widget_show (scale);
 
   /*  Line Art: segment max len */
   scale = gimp_prop_spin_scale_new (config, "line-art-segment-max-len", NULL,
                                     1, 5, 0);
   gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
-  options->priv->line_art_segment_max_len_scale = scale;
   gtk_widget_show (scale);
 
   /*  Line Art: spline max len */
   scale = gimp_prop_spin_scale_new (config, "line-art-spline-max-len", NULL,
                                     1, 5, 0);
   gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
-  options->priv->line_art_spline_max_len_scale = scale;
   gtk_widget_show (scale);
 
-  /*  the fill criterion combo  */
-  combo = gimp_prop_enum_combo_box_new (config, "fill-criterion", 0, 0);
-  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Fill by"));
-  gtk_box_pack_start (GTK_BOX (vbox2), combo, FALSE, FALSE, 0);
-  gtk_widget_show (combo);
-
-  gimp_bucket_fill_options_update_criterion (options);
+  gimp_bucket_fill_options_update_area (options);
 
   return vbox;
 }
diff --git a/app/tools/gimpbucketfilloptions.h b/app/tools/gimpbucketfilloptions.h
index 261fc6b9d2..f924a1923c 100644
--- a/app/tools/gimpbucketfilloptions.h
+++ b/app/tools/gimpbucketfilloptions.h
@@ -39,7 +39,7 @@ struct _GimpBucketFillOptions
   GimpPaintOptions              paint_options;
 
   GimpBucketFillMode            fill_mode;
-  gboolean                      fill_selection;
+  GimpBucketFillArea            fill_area;
   gboolean                      fill_transparent;
   gboolean                      sample_merged;
   gboolean                      diagonal_neighbors;
diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c
index e11c9077be..55213f8742 100644
--- a/app/tools/gimpbucketfilltool.c
+++ b/app/tools/gimpbucketfilltool.c
@@ -77,6 +77,7 @@ struct _GimpBucketFillToolPrivate
 
   /* Temp property save */
   GimpBucketFillMode  fill_mode;
+  GimpBucketFillArea  fill_area;
 };
 
 /*  local function prototypes  */
@@ -128,7 +129,6 @@ static void     gimp_bucket_fill_tool_cursor_update    (GimpTool              *t
                                                         GdkModifierType        state,
                                                         GimpDisplay           *display);
 
-static gboolean gimp_bucket_fill_tool_connect_handlers (gpointer               data);
 static void     gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
                                                         GParamSpec            *pspec,
                                                         GimpBucketFillTool    *tool);
@@ -207,6 +207,8 @@ 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;
+  GimpContext           *context = gimp_get_user_context (gimp);
+  GimpImage             *image   = gimp_context_get_image (context);
   GimpLineArt           *line_art;
 
   G_OBJECT_CLASS (parent_class)->constructed (object);
@@ -229,14 +231,20 @@ gimp_bucket_fill_tool_constructed (GObject *object)
                           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.
-   */
-  if (gimp_is_restored (gimp))
-    gimp_bucket_fill_tool_connect_handlers (tool);
-  else
-    g_idle_add (gimp_bucket_fill_tool_connect_handlers, tool);
+  g_signal_connect (options, "notify::fill-criterion",
+                    G_CALLBACK (gimp_bucket_fill_tool_options_notified),
+                    tool);
+  g_signal_connect (options, "notify::sample-merged",
+                    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);
+
+  g_signal_connect (context, "image-changed",
+                    G_CALLBACK (gimp_bucket_fill_tool_image_changed),
+                    tool);
+  gimp_bucket_fill_tool_image_changed (context, image, GIMP_BUCKET_FILL_TOOL (tool));
 
   GIMP_COLOR_TOOL (tool)->pick_target = (options->fill_mode == GIMP_BUCKET_FILL_BG) ?
                                            GIMP_COLOR_PICK_TARGET_BACKGROUND : 
GIMP_COLOR_PICK_TARGET_FOREGROUND;
@@ -371,16 +379,27 @@ gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
           y -= (gdouble) off_y;
         }
 
-      fill = gimp_drawable_get_bucket_fill_buffer (drawable,
-                                                   tool->priv->line_art,
-                                                   fill_options,
-                                                   options->fill_transparent,
-                                                   options->fill_criterion,
-                                                   options->threshold / 255.0,
-                                                   options->sample_merged,
-                                                   options->diagonal_neighbors,
-                                                   x, y, &tool->priv->fill_mask,
-                                                   &x, &y, NULL, NULL);
+      if (options->fill_area == GIMP_BUCKET_FILL_SIMILAR_COLORS)
+        {
+          fill = gimp_drawable_get_bucket_fill_buffer (drawable,
+                                                       fill_options,
+                                                       options->fill_transparent,
+                                                       options->fill_criterion,
+                                                       options->threshold / 255.0,
+                                                       options->sample_merged,
+                                                       options->diagonal_neighbors,
+                                                       x, y, &tool->priv->fill_mask,
+                                                       &x, &y, NULL, NULL);
+        }
+      else
+        {
+          fill = gimp_drawable_get_line_art_fill_buffer (drawable,
+                                                         tool->priv->line_art,
+                                                         fill_options,
+                                                         options->sample_merged,
+                                                         x, y, &tool->priv->fill_mask,
+                                                         &x, &y, NULL, NULL);
+        }
       if (fill)
         {
           gegl_node_set (tool->priv->fill_node,
@@ -507,12 +526,12 @@ gimp_bucket_fill_tool_button_press (GimpTool            *tool,
           gimp_context_set_paint_mode (GIMP_CONTEXT (fill_options),
                                        gimp_context_get_paint_mode (context));
 
-          if (options->fill_selection)
+          if (options->fill_area == GIMP_BUCKET_FILL_SELECTION)
             {
               gimp_drawable_edit_fill (drawable, fill_options, NULL);
               gimp_image_flush (image);
             }
-          else
+          else /* GIMP_BUCKET_FILL_SIMILAR_COLORS or GIMP_BUCKET_FILL_LINE_ART */
             {
               gimp_bucket_fill_tool_start (bucket_tool, coords, display);
               gimp_bucket_fill_tool_preview (bucket_tool, coords, display, fill_options);
@@ -551,7 +570,7 @@ gimp_bucket_fill_tool_motion (GimpTool         *tool,
   if (gimp_image_coords_in_active_pickable (image, coords,
                                             options->sample_merged, TRUE) &&
       /* Fill selection only needs to happen once. */
-      ! options->fill_selection)
+      options->fill_area != GIMP_BUCKET_FILL_SELECTION)
     {
       GimpContext     *context  = GIMP_CONTEXT (options);
       GimpFillOptions *fill_options;
@@ -591,8 +610,8 @@ gimp_bucket_fill_tool_button_release (GimpTool              *tool,
                                       GimpButtonReleaseType  release_type,
                                       GimpDisplay           *display)
 {
-  GimpBucketFillTool *bucket_tool = GIMP_BUCKET_FILL_TOOL (tool);
-  gboolean            commit;
+  GimpBucketFillTool    *bucket_tool = GIMP_BUCKET_FILL_TOOL (tool);
+  GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
 
   if (gimp_color_tool_is_enabled (GIMP_COLOR_TOOL (tool)))
     {
@@ -602,12 +621,11 @@ gimp_bucket_fill_tool_button_release (GimpTool              *tool,
       return;
     }
 
-  commit = (release_type != GIMP_BUTTON_RELEASE_CANCEL);
-
-  if (commit)
+  if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
     gimp_bucket_fill_tool_commit (bucket_tool);
 
-  gimp_bucket_fill_tool_halt (bucket_tool);
+  if (options->fill_area != GIMP_BUCKET_FILL_SELECTION)
+    gimp_bucket_fill_tool_halt (bucket_tool);
 
   GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
                                                   release_type, display);
@@ -681,7 +699,28 @@ gimp_bucket_fill_tool_modifier_key (GimpTool        *tool,
     }
   else if (key == gimp_get_extend_selection_mask ())
     {
-      g_object_set (options, "fill-selection", ! options->fill_selection, NULL);
+      if (press)
+        {
+          GIMP_BUCKET_FILL_TOOL (tool)->priv->fill_area = options->fill_area;
+          switch (options->fill_area)
+            {
+            case GIMP_BUCKET_FILL_SIMILAR_COLORS:
+              g_object_set (options, "fill-area", GIMP_BUCKET_FILL_SELECTION, NULL);
+              break;
+
+            default: /* GIMP_BUCKET_FILL_SELECTION && GIMP_BUCKET_FILL_LINE_ART */
+              g_object_set (options, "fill-area", GIMP_BUCKET_FILL_SIMILAR_COLORS, NULL);
+              break;
+
+              break;
+            }
+        }
+      else /* release */
+        {
+          g_object_set (options, "fill-area",
+                        GIMP_BUCKET_FILL_TOOL (tool)->priv->fill_area,
+                        NULL);
+        }
     }
 }
 
@@ -726,38 +765,6 @@ gimp_bucket_fill_tool_cursor_update (GimpTool         *tool,
   GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
 }
 
-static gboolean
-gimp_bucket_fill_tool_connect_handlers (gpointer data)
-{
-  GimpBucketFillTool    *tool    = GIMP_BUCKET_FILL_TOOL (data);
-  GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-  Gimp                  *gimp    = GIMP_CONTEXT (options)->gimp;
-
-  if (gimp_is_restored (gimp))
-    {
-      GimpContext *context = gimp_get_user_context (gimp);
-      GimpImage   *image   = gimp_context_get_image (context);
-
-      g_signal_connect (options, "notify::fill-criterion",
-                        G_CALLBACK (gimp_bucket_fill_tool_options_notified),
-                        tool);
-      g_signal_connect (options, "notify::sample-merged",
-                        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);
-
-      g_signal_connect (context, "image-changed",
-                        G_CALLBACK (gimp_bucket_fill_tool_image_changed),
-                        tool);
-      gimp_bucket_fill_tool_image_changed (context, image, GIMP_BUCKET_FILL_TOOL (tool));
-
-      return G_SOURCE_REMOVE;
-    }
-  return G_SOURCE_CONTINUE;
-}
-
 static void
 gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
                                         GParamSpec            *pspec,
@@ -811,7 +818,7 @@ gimp_bucket_fill_reset_line_art (GimpBucketFillTool    *tool,
   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)
+  if (image && options->fill_area == GIMP_BUCKET_FILL_LINE_ART)
     {
       GimpBucketFillOptions *options  = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
       GimpDrawable          *drawable = gimp_image_get_active_drawable (image);
diff --git a/app/tools/gimpfuzzyselecttool.c b/app/tools/gimpfuzzyselecttool.c
index b3eee7d196..785804a27b 100644
--- a/app/tools/gimpfuzzyselecttool.c
+++ b/app/tools/gimpfuzzyselecttool.c
@@ -122,7 +122,7 @@ gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select,
       pickable = GIMP_PICKABLE (image);
     }
 
-  return gimp_pickable_contiguous_region_by_seed (pickable, NULL,
+  return gimp_pickable_contiguous_region_by_seed (pickable,
                                                   sel_options->antialias,
                                                   options->threshold / 255.0,
                                                   options->select_transparent,
diff --git a/data/tool-presets/FX/Fill-Paper.gtp b/data/tool-presets/FX/Fill-Paper.gtp
index c7a151caf4..23d12d368f 100644
--- a/data/tool-presets/FX/Fill-Paper.gtp
+++ b/data/tool-presets/FX/Fill-Paper.gtp
@@ -10,7 +10,7 @@
     (pattern "Paper")
     (brush-size 80.000000)
     (fill-mode pattern)
-    (fill-selection yes)
+    (fill-area selection)
     (sample-merged yes))
 (use-fg-bg no)
 (use-brush no)
diff --git a/libgimp/gimpenums.c.tail b/libgimp/gimpenums.c.tail
index e33a9fdbef..aa86a56b61 100644
--- a/libgimp/gimpenums.c.tail
+++ b/libgimp/gimpenums.c.tail
@@ -8,6 +8,7 @@ static const GimpGetTypeFunc get_type_funcs[] =
   gimp_blend_mode_get_type,
   gimp_brush_application_mode_get_type,
   gimp_brush_generated_shape_get_type,
+  gimp_bucket_fill_area_get_type,
   gimp_bucket_fill_mode_get_type,
   gimp_cap_style_get_type,
   gimp_channel_ops_get_type,
@@ -75,6 +76,7 @@ static const gchar * const type_names[] =
   "GimpBlendMode",
   "GimpBrushApplicationMode",
   "GimpBrushGeneratedShape",
+  "GimpBucketFillArea",
   "GimpBucketFillMode",
   "GimpCapStyle",
   "GimpChannelOps",
diff --git a/libgimpbase/gimpbaseenums.c b/libgimpbase/gimpbaseenums.c
index 2fa63c14e5..47620a53eb 100644
--- a/libgimpbase/gimpbaseenums.c
+++ b/libgimpbase/gimpbaseenums.c
@@ -114,6 +114,38 @@ gimp_brush_generated_shape_get_type (void)
   return type;
 }
 
+GType
+gimp_bucket_fill_area_get_type (void)
+{
+  static const GEnumValue values[] =
+  {
+    { GIMP_BUCKET_FILL_SELECTION, "GIMP_BUCKET_FILL_SELECTION", "selection" },
+    { GIMP_BUCKET_FILL_SIMILAR_COLORS, "GIMP_BUCKET_FILL_SIMILAR_COLORS", "similar-colors" },
+    { GIMP_BUCKET_FILL_LINE_ART, "GIMP_BUCKET_FILL_LINE_ART", "line-art" },
+    { 0, NULL, NULL }
+  };
+
+  static const GimpEnumDesc descs[] =
+  {
+    { GIMP_BUCKET_FILL_SELECTION, NC_("bucket-fill-area", "Fill whole selection"), NULL },
+    { GIMP_BUCKET_FILL_SIMILAR_COLORS, NC_("bucket-fill-area", "Fill similar colors"), NULL },
+    { GIMP_BUCKET_FILL_LINE_ART, NC_("bucket-fill-area", "Fill by line art detection"), NULL },
+    { 0, NULL, NULL }
+  };
+
+  static GType type = 0;
+
+  if (G_UNLIKELY (! type))
+    {
+      type = g_enum_register_static ("GimpBucketFillArea", values);
+      gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+      gimp_type_set_translation_context (type, "bucket-fill-area");
+      gimp_enum_set_value_descriptions (type, descs);
+    }
+
+  return type;
+}
+
 GType
 gimp_bucket_fill_mode_get_type (void)
 {
@@ -1666,7 +1698,6 @@ gimp_select_criterion_get_type (void)
     { GIMP_SELECT_CRITERION_LCH_L, "GIMP_SELECT_CRITERION_LCH_L", "lch-l" },
     { GIMP_SELECT_CRITERION_LCH_C, "GIMP_SELECT_CRITERION_LCH_C", "lch-c" },
     { GIMP_SELECT_CRITERION_LCH_H, "GIMP_SELECT_CRITERION_LCH_H", "lch-h" },
-    { GIMP_SELECT_CRITERION_LINE_ART, "GIMP_SELECT_CRITERION_LINE_ART", "line-art" },
     { 0, NULL, NULL }
   };
 
@@ -1683,7 +1714,6 @@ gimp_select_criterion_get_type (void)
     { GIMP_SELECT_CRITERION_LCH_L, NC_("select-criterion", "LCh Lightness"), NULL },
     { GIMP_SELECT_CRITERION_LCH_C, NC_("select-criterion", "LCh Chroma"), NULL },
     { GIMP_SELECT_CRITERION_LCH_H, NC_("select-criterion", "LCh Hue"), NULL },
-    { GIMP_SELECT_CRITERION_LINE_ART, NC_("select-criterion", "Line Art"), NULL },
     { 0, NULL, NULL }
   };
 
diff --git a/libgimpbase/gimpbaseenums.h b/libgimpbase/gimpbaseenums.h
index a00b0bdeb5..274f746d3f 100644
--- a/libgimpbase/gimpbaseenums.h
+++ b/libgimpbase/gimpbaseenums.h
@@ -104,6 +104,26 @@ typedef enum
 } GimpBrushGeneratedShape;
 
 
+/**
+ * GimpBucketFillArea:
+ * @GIMP_BUCKET_FILL_SELECTION:      Fill whole selection
+ * @GIMP_BUCKET_FILL_SIMILAR_COLORS: Fill similar colors
+ * @GIMP_BUCKET_FILL_LINE_ART:       Fill by line art detection
+ *
+ * Bucket fill area.
+ */
+#define GIMP_TYPE_BUCKET_FILL_AREA (gimp_bucket_fill_area_get_type ())
+
+GType gimp_bucket_fill_area_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+  GIMP_BUCKET_FILL_SELECTION,       /*< desc="Fill whole selection" >*/
+  GIMP_BUCKET_FILL_SIMILAR_COLORS,  /*< desc="Fill similar colors" >*/
+  GIMP_BUCKET_FILL_LINE_ART         /*< desc="Fill by line art detection" >*/
+} GimpBucketFillArea;
+
+
 /**
  * GimpBucketFillMode:
  * @GIMP_BUCKET_FILL_FG:      FG color fill
@@ -1144,7 +1164,6 @@ typedef enum
  * @GIMP_SELECT_CRITERION_LCH_L:     LCh Lightness
  * @GIMP_SELECT_CRITERION_LCH_C:     LCh Chroma
  * @GIMP_SELECT_CRITERION_LCH_H:     LCh Hue
- * @GIMP_SELECT_CRITERION_LINE_ART:  Line Art
  *
  * Criterions for color similarity.
  **/
@@ -1165,7 +1184,6 @@ typedef enum
   GIMP_SELECT_CRITERION_LCH_L,      /*< desc="LCh Lightness"  >*/
   GIMP_SELECT_CRITERION_LCH_C,      /*< desc="LCh Chroma"     >*/
   GIMP_SELECT_CRITERION_LCH_H,      /*< desc="LCh Hue"        >*/
-  GIMP_SELECT_CRITERION_LINE_ART,   /*< desc="Line Art"       >*/
 } GimpSelectCriterion;
 
 
diff --git a/libgimpwidgets/gimppropwidgets.c b/libgimpwidgets/gimppropwidgets.c
index 1c739abd87..30b3677bdb 100644
--- a/libgimpwidgets/gimppropwidgets.c
+++ b/libgimpwidgets/gimppropwidgets.c
@@ -546,8 +546,7 @@ gimp_prop_enum_combo_box_new (GObject     *config,
                                                GIMP_SELECT_CRITERION_V,
                                                GIMP_SELECT_CRITERION_LCH_L,
                                                GIMP_SELECT_CRITERION_LCH_C,
-                                               GIMP_SELECT_CRITERION_LCH_H,
-                                               GIMP_SELECT_CRITERION_LINE_ART);
+                                               GIMP_SELECT_CRITERION_LCH_H);
     }
 
   if (store)
diff --git a/pdb/enums.pl b/pdb/enums.pl
index 808f28d475..105a16c7f0 100644
--- a/pdb/enums.pl
+++ b/pdb/enums.pl
@@ -64,6 +64,16 @@ package Gimp::CodeGen::enums;
                       GIMP_BRUSH_GENERATED_SQUARE => '1',
                       GIMP_BRUSH_GENERATED_DIAMOND => '2' }
        },
+    GimpBucketFillArea =>
+       { contig => 1,
+         header => 'libgimpbase/gimpbaseenums.h',
+         symbols => [ qw(GIMP_BUCKET_FILL_SELECTION
+                         GIMP_BUCKET_FILL_SIMILAR_COLORS
+                         GIMP_BUCKET_FILL_LINE_ART) ],
+         mapping => { GIMP_BUCKET_FILL_SELECTION => '0',
+                      GIMP_BUCKET_FILL_SIMILAR_COLORS => '1',
+                      GIMP_BUCKET_FILL_LINE_ART => '2' }
+       },
     GimpBucketFillMode =>
        { contig => 1,
          header => 'libgimpbase/gimpbaseenums.h',
@@ -568,8 +578,7 @@ package Gimp::CodeGen::enums;
                          GIMP_SELECT_CRITERION_A
                          GIMP_SELECT_CRITERION_LCH_L
                          GIMP_SELECT_CRITERION_LCH_C
-                         GIMP_SELECT_CRITERION_LCH_H
-                         GIMP_SELECT_CRITERION_LINE_ART) ],
+                         GIMP_SELECT_CRITERION_LCH_H) ],
          mapping => { GIMP_SELECT_CRITERION_COMPOSITE => '0',
                       GIMP_SELECT_CRITERION_R => '1',
                       GIMP_SELECT_CRITERION_G => '2',
@@ -580,8 +589,7 @@ package Gimp::CodeGen::enums;
                       GIMP_SELECT_CRITERION_A => '7',
                       GIMP_SELECT_CRITERION_LCH_L => '8',
                       GIMP_SELECT_CRITERION_LCH_C => '9',
-                      GIMP_SELECT_CRITERION_LCH_H => '10',
-                      GIMP_SELECT_CRITERION_LINE_ART => '11' }
+                      GIMP_SELECT_CRITERION_LCH_H => '10' }
        },
     GimpSizeType =>
        { contig => 1,
diff --git a/pdb/groups/drawable_edit.pdb b/pdb/groups/drawable_edit.pdb
index 79b8c395c5..52ab64b6e5 100644
--- a/pdb/groups/drawable_edit.pdb
+++ b/pdb/groups/drawable_edit.pdb
@@ -169,7 +169,7 @@ HELP
       if (gimp_fill_options_set_by_fill_type (options, context,
                                               fill_type, error))
         {
-          gimp_drawable_bucket_fill (drawable, NULL, options,
+          gimp_drawable_bucket_fill (drawable, options,
                                      GIMP_PDB_CONTEXT (context)->sample_transparent,
                                      GIMP_PDB_CONTEXT (context)->sample_criterion,
                                      GIMP_PDB_CONTEXT (context)->sample_threshold,


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