Commit b4e12fbb authored by Ell's avatar Ell

app: add gimp_pickable_contiguous_region_prepare_line_art_async() ...

... and use in bucket-fill tool

Add gimp_pickable_contiguous_region_prepare_line_art_async(), which
computes a line-art asynchronously, and use it in the bucket-fill
tool, instead of having the tool create the async op.

This allows the async to keep running even after the pickable dies,
since we only need the pickable's buffer, and not the pickable
itself.  Previously, we reffed the pickable for the duration of the
async, but we could still segfault when unreffing it, if the
pickable was a drawable, and its parent image had already died.

Furthermore, let the async work on a copy of the pickable's buffer,
rather than the pickable's buffer directly.  This avoids some race
conditions when the pickable is the image (i.e., when "sample
merged" is active), since then we're using image projection's
buffer, which is generally unsafe to use in different threads
concurrently.

Also, s/! has_alpha/has_alpha/ when looking for transparent pixels,
and quit early, at least during this stage, if the async in
canceled.
parent 663a6c70
......@@ -31,12 +31,22 @@
#include "gegl/gimp-babl.h"
#include "gimp-parallel.h"
#include "gimp-utils.h" /* GIMP_TIMER */
#include "gimpasync.h"
#include "gimplineart.h"
#include "gimppickable.h"
#include "gimppickable-contiguous-region.h"
typedef struct
{
GeglBuffer *buffer;
gboolean select_transparent;
gfloat stroke_threshold;
} LineArtData;
/* local function prototypes */
static const Babl * choose_format (GeglBuffer *buffer,
......@@ -96,41 +106,52 @@ static void find_contiguous_region (GeglBuffer *src_buffer,
gint y,
const gfloat *col);
static LineArtData * line_art_data_new (GeglBuffer *buffer,
gboolean select_transparent,
gfloat stroke_threshold);
static void line_art_data_free (LineArtData *data);
/* public functions */
GeglBuffer *
gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold)
static void
gimp_pickable_contiguous_region_prepare_line_art_async_func (GimpAsync *async,
LineArtData *data)
{
GeglBuffer *lineart;
gboolean has_alpha;
gboolean select_transparent = FALSE;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
gimp_pickable_flush (pickable);
lineart = gimp_pickable_get_buffer (pickable);
has_alpha = babl_format_has_alpha (gegl_buffer_get_format (lineart));
has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
if (! has_alpha)
if (has_alpha)
{
if (select_transparent)
if (data->select_transparent)
{
/* don't select transparent regions if there are no fully
* transparent pixels.
*/
GeglBufferIterator *gi;
select_transparent = FALSE;
gi = gegl_buffer_iterator_new (lineart, NULL, 0, babl_format ("A u8"),
gi = gegl_buffer_iterator_new (data->buffer, NULL, 0,
babl_format ("A u8"),
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3);
while (gegl_buffer_iterator_next (gi))
{
guint8 *p = (guint8*) gi->items[0].data;
gint k;
if (gimp_async_is_canceled (async))
{
gegl_buffer_iterator_stop (gi);
gimp_async_abort (async);
line_art_data_free (data);
return;
}
for (k = 0; k < gi->length; k++)
{
if (! *p)
......@@ -147,10 +168,6 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
gegl_buffer_iterator_stop (gi);
}
}
else
{
select_transparent = FALSE;
}
/* For smart selection, we generate a binarized image with close
* regions, then run a composite selection with no threshold on
......@@ -158,9 +175,9 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
*/
GIMP_TIMER_START();
lineart = gimp_lineart_close (lineart,
lineart = gimp_lineart_close (data->buffer,
select_transparent,
stroke_threshold,
data->stroke_threshold,
/*minimal_lineart_area,*/
5,
/*normal_estimate_mask_size,*/
......@@ -188,9 +205,70 @@ gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
GIMP_TIMER_END("close line-art");
gimp_async_finish_full (async, lineart, g_object_unref);
line_art_data_free (data);
}
GeglBuffer *
gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold)
{
GimpAsync *async;
LineArtData *data;
GeglBuffer *lineart;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
gimp_pickable_flush (pickable);
async = gimp_async_new ();
data = line_art_data_new (gimp_pickable_get_buffer (pickable),
select_transparent,
stroke_threshold);
gimp_pickable_contiguous_region_prepare_line_art_async_func (async, data);
lineart = g_object_ref (gimp_async_get_result (async));
g_object_unref (async);
return lineart;
}
GimpAsync *
gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold,
gint priority)
{
GeglBuffer *buffer;
GimpAsync *async;
LineArtData *data;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
gimp_pickable_flush (pickable);
buffer = gegl_buffer_dup (gimp_pickable_get_buffer (pickable));
data = line_art_data_new (buffer,
select_transparent,
stroke_threshold);
g_object_unref (buffer);
async = gimp_parallel_run_async_full (
priority,
(GimpParallelRunAsyncFunc)
gimp_pickable_contiguous_region_prepare_line_art_async_func,
data,
(GDestroyNotify) line_art_data_free);
return async;
}
GeglBuffer *
gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
GeglBuffer *line_art,
......@@ -926,3 +1004,25 @@ find_contiguous_region (GeglBuffer *src_buffer,
g_free (row);
#endif
}
static LineArtData *
line_art_data_new (GeglBuffer *buffer,
gboolean select_transparent,
gfloat stroke_threshold)
{
LineArtData *data = g_slice_new (LineArtData);
data->buffer = g_object_ref (buffer);
data->select_transparent = select_transparent;
data->stroke_threshold = stroke_threshold;
return data;
}
static void
line_art_data_free (LineArtData *data)
{
g_object_unref (data->buffer);
g_slice_free (LineArtData, data);
}
......@@ -19,26 +19,31 @@
#define __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold);
GeglBuffer * gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
GeglBuffer *line_art,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
gboolean diagonal_neighbors,
gfloat stroke_threshold,
gint x,
gint y);
GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold);
GimpAsync * gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
gboolean select_transparent,
gfloat stroke_threshold,
gint priority);
GeglBuffer * gimp_pickable_contiguous_region_by_color (GimpPickable *pickable,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
const GimpRGB *color);
GeglBuffer * gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
GeglBuffer *line_art,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
gboolean diagonal_neighbors,
gfloat stroke_threshold,
gint x,
gint y);
GeglBuffer * gimp_pickable_contiguous_region_by_color (GimpPickable *pickable,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
const GimpRGB *color);
#endif /* __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ */
......@@ -35,7 +35,6 @@
#include "core/gimpimage.h"
#include "core/gimpitem.h"
#include "core/gimplineart.h"
#include "core/gimp-parallel.h"
#include "core/gimppickable.h"
#include "core/gimppickable-contiguous-region.h"
#include "core/gimpprogress.h"
......@@ -689,33 +688,6 @@ gimp_bucket_fill_tool_cursor_update (GimpTool *tool,
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
}
typedef struct
{
GimpPickable *pickable;
gboolean fill_transparent;
gdouble line_art_threshold;
} PrecomputeData;
static void
precompute_data_free (PrecomputeData *data)
{
g_object_unref (data->pickable);
g_slice_free (PrecomputeData, data);
}
static void
gimp_bucket_fill_compute_line_art_async (GimpAsync *async,
PrecomputeData *data)
{
GeglBuffer *line_art;
line_art = gimp_pickable_contiguous_region_prepare_line_art (data->pickable,
data->fill_transparent,
data->line_art_threshold);
precompute_data_free (data);
gimp_async_finish_full (async, line_art, g_object_unref);
}
static void
gimp_bucket_fill_compute_line_art_cb (GimpAsync *async,
GimpBucketFillTool *tool)
......@@ -754,36 +726,26 @@ gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool)
GimpDrawable *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
if (image && options->sample_merged)
{
pickable = GIMP_PICKABLE (image);
g_clear_object (&drawable);
}
pickable = GIMP_PICKABLE (image);
else if (drawable && ! options->sample_merged)
{
pickable = GIMP_PICKABLE (drawable);
g_clear_object (&image);
}
else
{
g_clear_object (&image);
g_clear_object (&drawable);
}
pickable = GIMP_PICKABLE (drawable);
if (pickable)
{
PrecomputeData *data = g_slice_new (PrecomputeData);
tool->priv->async =
gimp_pickable_contiguous_region_prepare_line_art_async (
pickable,
options->fill_transparent,
options->line_art_threshold,
+1);
data->pickable = pickable;
data->fill_transparent = options->fill_transparent;
data->line_art_threshold = options->line_art_threshold;
tool->priv->async = gimp_parallel_run_async_full (1,
(GimpParallelRunAsyncFunc) gimp_bucket_fill_compute_line_art_async,
data, (GDestroyNotify) precompute_data_free);
gimp_async_add_callback (tool->priv->async,
(GimpAsyncCallback) gimp_bucket_fill_compute_line_art_cb,
tool);
}
g_clear_object (&image);
g_clear_object (&drawable);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment