mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-10-06 05:22:40 +02:00
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.
This commit is contained in:
@@ -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);
|
||||
has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
|
||||
|
||||
gimp_pickable_flush (pickable);
|
||||
|
||||
lineart = gimp_pickable_get_buffer (pickable);
|
||||
has_alpha = babl_format_has_alpha (gegl_buffer_get_format (lineart));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user