From b4e12fbbbba8d607abdbe353e424c8386d2a3f11 Mon Sep 17 00:00:00 2001 From: Ell Date: Mon, 19 Nov 2018 14:48:26 -0500 Subject: [PATCH] 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. --- app/core/gimppickable-contiguous-region.c | 140 ++++++++++++++++++---- app/core/gimppickable-contiguous-region.h | 43 ++++--- app/tools/gimpbucketfilltool.c | 60 ++-------- 3 files changed, 155 insertions(+), 88 deletions(-) diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.c index 38a0f2ed0a..0187734616 100644 --- a/app/core/gimppickable-contiguous-region.c +++ b/app/core/gimppickable-contiguous-region.c @@ -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); +} diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h index b73f9aa1c4..95543a59e5 100644 --- a/app/core/gimppickable-contiguous-region.h +++ b/app/core/gimppickable-contiguous-region.h @@ -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__ */ diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c index 9ce209c714..829cf87412 100644 --- a/app/tools/gimpbucketfilltool.c +++ b/app/tools/gimpbucketfilltool.c @@ -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); } }