Commit cd924f45 authored by Jehan's avatar Jehan

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.
parent 74a7a5d3
......@@ -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,
......
......@@ -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.
......
......@@ -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__ */
......@@ -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 *p