[gimp] app: allow canceling line-art computation
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: allow canceling line-art computation
- Date: Wed, 6 Mar 2019 20:25:52 +0000 (UTC)
commit d8e69d66bc91d3def9efe58e9c4d9ab5c52df2a5
Author: Ell <ell_se yahoo com>
Date: Wed Mar 6 15:04:39 2019 -0500
app: allow canceling line-art computation
Line-art computation can take a long time, and it's therefore
desirable for it to be interruptable. While we do cancel the line-
art async when its result is no longer needed, most parts of the
computation don't respond to the cancelation request, leaving the
async operation running in the background, blocking subsequent
async operations.
Implement cancelation support of line-art computation, by passing
down the async object to the various functions, and periodically
checking for its cancelation at various points. When the async is
canceled, we quickly abort the operation.
Even though cancelation now happens relatively quickly, some parts
of the computation are still uninterruptable and may incur some
latency, so we avoid waiting for the async opration to be aborted
after cancelation, as we did before.
app/core/gimplineart.c | 710 +++++++++++++++++++++++++++++++++----------------
1 file changed, 480 insertions(+), 230 deletions(-)
---
diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c
index 0c2b8df066..23174a1609 100644
--- a/app/core/gimplineart.c
+++ b/app/core/gimplineart.c
@@ -193,20 +193,25 @@ static GeglBuffer * gimp_line_art_close (GeglBuffer
gint
created_regions_significant_area,
gint
created_regions_minimum_area,
gboolean
small_segments_from_spline_sources,
- gfloat **lineart_distmap);
+ gfloat **lineart_distmap,
+ GimpAsync *async);
static void gimp_lineart_denoise (GeglBuffer *buffer,
- int size);
+ int size,
+ GimpAsync *async);
static void gimp_lineart_compute_normals_curvatures (GeglBuffer *mask,
gfloat *normals,
gfloat *curvatures,
gfloat *smoothed_curvatures,
- int
normal_estimate_mask_size);
-static gfloat * gimp_lineart_get_smooth_curvatures (GArray *edgelset);
+ int
normal_estimate_mask_size,
+ GimpAsync *async);
+static gfloat * gimp_lineart_get_smooth_curvatures (GArray *edgelset,
+ GimpAsync *async);
static GArray * gimp_lineart_curvature_extremums (gfloat *curvatures,
gfloat *smoothed_curvatures,
gint curvatures_width,
- gint curvatures_height);
+ gint curvatures_height,
+ GimpAsync *async);
static gint gimp_spline_candidate_cmp (const SplineCandidate *a,
const SplineCandidate *b,
gpointer user_data);
@@ -214,7 +219,8 @@ static GList * gimp_lineart_find_spline_candidates (GArray
gfloat *normals,
gint width,
gint distance_threshold,
- gfloat max_angle_deg);
+ gfloat max_angle_deg,
+ GimpAsync *async);
static GArray * gimp_lineart_discrete_spline (Pixel p0,
GimpVector2 n0,
@@ -232,7 +238,8 @@ static GArray * gimp_lineart_line_segment_until_hit (const GeglBuffe
Pixel start,
GimpVector2 direction,
int size);
-static gfloat * gimp_lineart_estimate_strokes_radii (GeglBuffer *mask);
+static gfloat * gimp_lineart_estimate_strokes_radii (GeglBuffer *mask,
+ GimpAsync *async);
static void gimp_line_art_simple_fill (GeglBuffer *buffer,
gint x,
gint y,
@@ -272,7 +279,8 @@ static glong gimp_edgel_region_area (const GeglBuffer *mask,
/* Edgel set */
-static GArray * gimp_edgelset_new (GeglBuffer *buffer);
+static GArray * gimp_edgelset_new (GeglBuffer *buffer,
+ GimpAsync *async);
static void gimp_edgelset_add (GArray *set,
int x,
int y,
@@ -280,12 +288,15 @@ static void gimp_edgelset_add (GArray *set,
GHashTable *edgel2index);
static void gimp_edgelset_init_normals (GArray *set);
static void gimp_edgelset_smooth_normals (GArray *set,
- int mask_size);
-static void gimp_edgelset_compute_curvature (GArray *set);
+ int mask_size,
+ GimpAsync *async);
+static void gimp_edgelset_compute_curvature (GArray *set,
+ GimpAsync *async);
static void gimp_edgelset_build_graph (GArray *set,
GeglBuffer *buffer,
- GHashTable *edgel2index);
+ GHashTable *edgel2index,
+ GimpAsync *async);
static void gimp_edgelset_next8 (const GeglBuffer *buffer,
Edgel *it,
Edgel *n);
@@ -564,7 +575,7 @@ gimp_line_art_compute (GimpLineArt *line_art)
if (line_art->priv->async)
{
/* we cancel the async, but don't wait for it to finish, since
- * it can't actually be interrupted. instead gimp_line_art_compute_cb()
+ * it might take a while to respond. instead gimp_line_art_compute_cb()
* bails if the async has been canceled, to avoid accessing the line art.
*/
g_signal_emit (line_art, gimp_line_art_signals[COMPUTING_END], 0);
@@ -656,8 +667,8 @@ static void
gimp_line_art_prepare_async_func (GimpAsync *async,
LineArtData *data)
{
- GeglBuffer *closed;
- gfloat *distmap;
+ GeglBuffer *closed = NULL;
+ gfloat *distmap = NULL;
gboolean has_alpha;
gboolean select_transparent = FALSE;
@@ -739,13 +750,17 @@ gimp_line_art_prepare_async_func (GimpAsync *async,
100,
/*small_segments_from_spline_sources,*/
TRUE,
- &distmap);
+ &distmap,
+ async);
GIMP_TIMER_END("close line-art");
- gimp_async_finish_full (async,
- line_art_result_new (closed, distmap),
- (GDestroyNotify) line_art_result_free);
+ if (! gimp_async_is_stopped (async))
+ {
+ gimp_async_finish_full (async,
+ line_art_result_new (closed, distmap),
+ (GDestroyNotify) line_art_result_free);
+ }
line_art_data_free (data);
}
@@ -847,6 +862,7 @@ gimp_line_art_input_invalidate_preview (GimpViewable *viewable,
* @created_regions_minimum_area:
* @small_segments_from_spline_sources:
* @closed_distmap: a distance map of the closed line art pixels.
+ * @async: the #GimpAsync associated with the computation
*
* Creates a binarized version of the strokes of @buffer, detected either
* with luminosity (light means background) or alpha values depending on
@@ -882,12 +898,13 @@ gimp_line_art_close (GeglBuffer *buffer,
gint created_regions_significant_area,
gint created_regions_minimum_area,
gboolean small_segments_from_spline_sources,
- gfloat **closed_distmap)
+ gfloat **closed_distmap,
+ GimpAsync *async)
{
const Babl *gray_format;
GeglBufferIterator *gi;
- GeglBuffer *closed;
- GeglBuffer *strokes;
+ GeglBuffer *closed = NULL;
+ GeglBuffer *strokes = NULL;
guchar max_value = 0;
gint width = gegl_buffer_get_width (buffer);
gint height = gegl_buffer_get_height (buffer);
@@ -916,6 +933,15 @@ gimp_line_art_close (GeglBuffer *buffer,
guchar *data = (guchar*) gi->items[0].data;
gint k;
+ if (gimp_async_is_canceled (async))
+ {
+ gegl_buffer_iterator_stop (gi);
+
+ gimp_async_abort (async);
+
+ goto end1;
+ }
+
for (k = 0; k < gi->length; k++)
{
if (*data > max_value)
@@ -933,6 +959,15 @@ gimp_line_art_close (GeglBuffer *buffer,
guchar *data = (guchar*) gi->items[0].data;
gint k;
+ if (gimp_async_is_canceled (async))
+ {
+ gegl_buffer_iterator_stop (gi);
+
+ gimp_async_abort (async);
+
+ goto end1;
+ }
+
for (k = 0; k < gi->length; k++)
{
if (! select_transparent)
@@ -948,21 +983,23 @@ gimp_line_art_close (GeglBuffer *buffer,
}
/* Denoise (remove small connected components) */
- gimp_lineart_denoise (strokes, minimal_lineart_area);
+ gimp_lineart_denoise (strokes, minimal_lineart_area, async);
+ if (gimp_async_is_stopped (async))
+ goto end1;
- closed = strokes;
+ closed = g_object_ref (strokes);
if (spline_max_length > 0 || segment_max_length > 0)
{
- GArray *keypoints;
- GHashTable *visited;
- gfloat *radii;
- gfloat *normals;
- gfloat *curvatures;
- gfloat *smoothed_curvatures;
+ GArray *keypoints = NULL;
+ GHashTable *visited = NULL;
+ gfloat *radii = NULL;
+ gfloat *normals = NULL;
+ gfloat *curvatures = NULL;
+ gfloat *smoothed_curvatures = NULL;
gfloat threshold;
gfloat clamped_threshold;
- GList *fill_pixels = NULL;
+ GList *fill_pixels = NULL;
GList *iter;
normals = g_new0 (gfloat, width * height * 2);
@@ -972,14 +1009,27 @@ gimp_line_art_close (GeglBuffer *buffer,
/* Estimate normals & curvature */
gimp_lineart_compute_normals_curvatures (strokes, normals, curvatures,
smoothed_curvatures,
- normal_estimate_mask_size);
-
- radii = gimp_lineart_estimate_strokes_radii (strokes);
+ normal_estimate_mask_size,
+ async);
+ if (gimp_async_is_stopped (async))
+ goto end2;
+
+ radii = gimp_lineart_estimate_strokes_radii (strokes, async);
+ if (gimp_async_is_stopped (async))
+ goto end2;
threshold = 1.0f - end_point_rate;
clamped_threshold = MAX (0.25f, threshold);
for (i = 0; i < width; i++)
{
gint j;
+
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end2;
+ }
+
for (j = 0; j < height; j++)
{
if (smoothed_curvatures[i + j * width] >= (threshold / MAX (1.0f, radii[i + j * width])) ||
@@ -989,10 +1039,13 @@ gimp_line_art_close (GeglBuffer *buffer,
curvatures[i + j * width] = 0.0;
}
}
- g_free (radii);
+ g_clear_pointer (&radii, g_free);
keypoints = gimp_lineart_curvature_extremums (curvatures, smoothed_curvatures,
- width, height);
+ width, height, async);
+ if (gimp_async_is_stopped (async))
+ goto end2;
+
visited = g_hash_table_new_full ((GHashFunc) visited_hash_fun,
(GEqualFunc) visited_equal_fun,
(GDestroyNotify) g_free, NULL);
@@ -1004,16 +1057,31 @@ gimp_line_art_close (GeglBuffer *buffer,
candidates = gimp_lineart_find_spline_candidates (keypoints, normals, width,
spline_max_length,
- spline_max_angle);
+ spline_max_angle,
+ async);
+ if (gimp_async_is_stopped (async))
+ goto end3;
+
+ g_object_unref (closed);
closed = gegl_buffer_dup (strokes);
/* Draw splines */
while (candidates)
{
- Pixel *p1 = g_new (Pixel, 1);
- Pixel *p2 = g_new (Pixel, 1);
+ Pixel *p1;
+ Pixel *p2;
gboolean inserted = FALSE;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end3;
+ }
+
+ p1 = g_new (Pixel, 1);
+ p2 = g_new (Pixel, 1);
+
candidate = (SplineCandidate *) candidates->data;
p1->x = candidate->p1.x;
p1->y = candidate->p1.y;
@@ -1079,8 +1147,12 @@ gimp_line_art_close (GeglBuffer *buffer,
}
}
+ end3:
g_list_free_full (candidates, g_free);
- g_object_unref (strokes);
+ g_clear_object (&strokes);
+
+ if (gimp_async_is_stopped (async))
+ goto end2;
}
/* Draw straight line segments */
@@ -1091,9 +1163,17 @@ gimp_line_art_close (GeglBuffer *buffer,
point = (Pixel *) keypoints->data;
for (i = 0; i < keypoints->len; i++)
{
- Pixel *p = g_new (Pixel, 1);
+ Pixel *p;
gboolean inserted = FALSE;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end2;
+ }
+
+ p = g_new (Pixel, 1);
*p = *point;
if (! g_hash_table_contains (visited, p) ||
@@ -1136,6 +1216,13 @@ gimp_line_art_close (GeglBuffer *buffer,
Pixel *p = iter->data;
gint fill_max = created_regions_significant_area - 1;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end2;
+ }
+
/* XXX A best approach would be to generalize
* gimp_drawable_bucket_fill() to work on any buffer (the code
* is already mostly there) rather than reimplementing a naive
@@ -1146,12 +1233,16 @@ gimp_line_art_close (GeglBuffer *buffer,
gimp_line_art_simple_fill (closed, (gint) p->x, (gint) p->y, &fill_max);
}
+ end2:
g_list_free_full (fill_pixels, g_free);
g_free (normals);
g_free (curvatures);
g_free (smoothed_curvatures);
g_array_free (keypoints, TRUE);
g_hash_table_destroy (visited);
+
+ if (gimp_async_is_stopped (async))
+ goto end1;
}
if (closed_distmap)
@@ -1181,12 +1272,19 @@ gimp_line_art_close (GeglBuffer *buffer,
g_object_unref (graph);
}
+ end1:
+ g_clear_object (&strokes);
+
+ if (gimp_async_is_stopped (async))
+ g_clear_object (&closed);
+
return closed;
}
static void
gimp_lineart_denoise (GeglBuffer *buffer,
- int minimum_area)
+ int minimum_area,
+ GimpAsync *async)
{
/* Keep connected regions with significant area. */
GArray *region;
@@ -1203,6 +1301,13 @@ gimp_lineart_denoise (GeglBuffer *buffer,
{
guchar has_stroke;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
gegl_buffer_sample (buffer, x, y, NULL, &has_stroke, NULL,
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
if (has_stroke && ! visited[x + y * width])
@@ -1222,6 +1327,13 @@ gimp_lineart_denoise (GeglBuffer *buffer,
gint p2x;
gint p2y;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
p2x = p->x + 1;
p2y = p->y;
if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height)
@@ -1372,6 +1484,8 @@ gimp_lineart_denoise (GeglBuffer *buffer,
g_array_remove_range (region, 0, region->len);
}
}
+
+ end:
g_array_free (region, TRUE);
g_queue_free_full (q, g_free);
g_free (visited);
@@ -1382,22 +1496,41 @@ gimp_lineart_compute_normals_curvatures (GeglBuffer *mask,
gfloat *normals,
gfloat *curvatures,
gfloat *smoothed_curvatures,
- int normal_estimate_mask_size)
+ int normal_estimate_mask_size,
+ GimpAsync *async)
{
- gfloat *edgels_curvatures;
+ gfloat *edgels_curvatures = NULL;
gfloat *smoothed_curvature;
- GArray *es = gimp_edgelset_new (mask);
- Edgel **e = (Edgel **) es->data;
- gint width = gegl_buffer_get_width (mask);
+ GArray *es = NULL;
+ Edgel **e;
+ gint width = gegl_buffer_get_width (mask);
- gimp_edgelset_smooth_normals (es, normal_estimate_mask_size);
- gimp_edgelset_compute_curvature (es);
+ es = gimp_edgelset_new (mask, async);
+ if (gimp_async_is_stopped (async))
+ goto end;
+
+ e = (Edgel **) es->data;
+
+ gimp_edgelset_smooth_normals (es, normal_estimate_mask_size, async);
+ if (gimp_async_is_stopped (async))
+ goto end;
+
+ gimp_edgelset_compute_curvature (es, async);
+ if (gimp_async_is_stopped (async))
+ goto end;
while (*e)
{
const float curvature = ((*e)->curvature > 0.0f) ? (*e)->curvature : 0.0f;
const float w = MAX (1e-8f, curvature * curvature);
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
normals[((*e)->x + (*e)->y * width) * 2] += w * (*e)->x_normal;
normals[((*e)->x + (*e)->y * width) * 2 + 1] += w * (*e)->y_normal;
curvatures[(*e)->x + (*e)->y * width] = MAX (curvature,
@@ -1405,16 +1538,28 @@ gimp_lineart_compute_normals_curvatures (GeglBuffer *mask,
e++;
}
for (int y = 0; y < gegl_buffer_get_height (mask); ++y)
- for (int x = 0; x < gegl_buffer_get_width (mask); ++x)
- {
- const float _angle = atan2f (normals[(x + y * width) * 2 + 1],
- normals[(x + y * width) * 2]);
- normals[(x + y * width) * 2] = cosf (_angle);
- normals[(x + y * width) * 2 + 1] = sinf (_angle);
- }
+ {
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
+ for (int x = 0; x < gegl_buffer_get_width (mask); ++x)
+ {
+ const float _angle = atan2f (normals[(x + y * width) * 2 + 1],
+ normals[(x + y * width) * 2]);
+ normals[(x + y * width) * 2] = cosf (_angle);
+ normals[(x + y * width) * 2 + 1] = sinf (_angle);
+ }
+ }
/* Smooth curvatures on edgels, then take maximum on each pixel. */
- edgels_curvatures = gimp_lineart_get_smooth_curvatures (es);
+ edgels_curvatures = gimp_lineart_get_smooth_curvatures (es, async);
+ if (gimp_async_is_stopped (async))
+ goto end;
+
smoothed_curvature = edgels_curvatures;
e = (Edgel **) es->data;
@@ -1428,13 +1573,17 @@ gimp_lineart_compute_normals_curvatures (GeglBuffer *mask,
++smoothed_curvature;
e++;
}
+
+ end:
g_free (edgels_curvatures);
- g_array_free (es, TRUE);
+ if (es)
+ g_array_free (es, TRUE);
}
static gfloat *
-gimp_lineart_get_smooth_curvatures (GArray *edgelset)
+gimp_lineart_get_smooth_curvatures (GArray *edgelset,
+ GimpAsync *async)
{
Edgel **e;
gfloat *smoothed_curvatures = g_new0 (gfloat, edgelset->len);
@@ -1455,6 +1604,15 @@ gimp_lineart_get_smooth_curvatures (GArray *edgelset)
int n = 5;
int i = 1;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ g_free (smoothed_curvatures);
+
+ return NULL;
+ }
+
smoothed_curvature = (*e)->curvature;
weights_sum = weights[0];
while (n-- && (edgel_after != edgel_before))
@@ -1479,10 +1637,11 @@ gimp_lineart_get_smooth_curvatures (GArray *edgelset)
* Keep one pixel per connected component of curvature extremums.
*/
static GArray *
-gimp_lineart_curvature_extremums (gfloat *curvatures,
- gfloat *smoothed_curvatures,
- gint width,
- gint height)
+gimp_lineart_curvature_extremums (gfloat *curvatures,
+ gfloat *smoothed_curvatures,
+ gint width,
+ gint height,
+ GimpAsync *async)
{
gboolean *visited = g_new0 (gboolean, width * height);
GQueue *q = g_queue_new ();
@@ -1491,187 +1650,211 @@ gimp_lineart_curvature_extremums (gfloat *curvatures,
max_positions = g_array_new (FALSE, TRUE, sizeof (Pixel));
for (int y = 0; y < height; ++y)
- for (int x = 0; x < width; ++x)
- {
- if ((curvatures[x + y * width] > 0.0) && ! visited[x + y * width])
- {
- Pixel *p = g_new (Pixel, 1);
- Pixel max_smoothed_curvature_pixel;
- Pixel max_raw_curvature_pixel;
- gfloat max_smoothed_curvature;
- gfloat max_raw_curvature;
+ {
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
- max_smoothed_curvature_pixel = gimp_vector2_new (-1.0, -1.0);
- max_smoothed_curvature = 0.0f;
+ goto end;
+ }
- max_raw_curvature_pixel = gimp_vector2_new (x, y);
- max_raw_curvature = curvatures[x + y * width];
+ for (int x = 0; x < width; ++x)
+ {
+ if ((curvatures[x + y * width] > 0.0) && ! visited[x + y * width])
+ {
+ Pixel *p = g_new (Pixel, 1);
+ Pixel max_smoothed_curvature_pixel;
+ Pixel max_raw_curvature_pixel;
+ gfloat max_smoothed_curvature;
+ gfloat max_raw_curvature;
- p->x = x;
- p->y = y;
- g_queue_push_tail (q, p);
- visited[x + y * width] = TRUE;
+ max_smoothed_curvature_pixel = gimp_vector2_new (-1.0, -1.0);
+ max_smoothed_curvature = 0.0f;
- while (! g_queue_is_empty (q))
- {
- gfloat sc;
- gfloat c;
- gint p2x;
- gint p2y;
+ max_raw_curvature_pixel = gimp_vector2_new (x, y);
+ max_raw_curvature = curvatures[x + y * width];
- p = (Pixel *) g_queue_pop_head (q);
- sc = smoothed_curvatures[(gint) p->x + (gint) p->y * width];
- c = curvatures[(gint) p->x + (gint) p->y * width];
+ p->x = x;
+ p->y = y;
+ g_queue_push_tail (q, p);
+ visited[x + y * width] = TRUE;
- curvatures[(gint) p->x + (gint) p->y * width] = 0.0f;
+ while (! g_queue_is_empty (q))
+ {
+ gfloat sc;
+ gfloat c;
+ gint p2x;
+ gint p2y;
- p2x = (gint) p->x + 1;
- p2y = (gint) p->y;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ goto end;
+ }
- p2x = p->x - 1;
- p2y = p->y;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p = (Pixel *) g_queue_pop_head (q);
+ sc = smoothed_curvatures[(gint) p->x + (gint) p->y * width];
+ c = curvatures[(gint) p->x + (gint) p->y * width];
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ curvatures[(gint) p->x + (gint) p->y * width] = 0.0f;
- p2x = p->x;
- p2y = p->y - 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = (gint) p->x + 1;
+ p2y = (gint) p->y;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- p2x = p->x;
- p2y = p->y + 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = p->x - 1;
+ p2y = p->y;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- p2x = p->x + 1;
- p2y = p->y + 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = p->x;
+ p2y = p->y - 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- p2x = p->x - 1;
- p2y = p->y - 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = p->x;
+ p2y = p->y + 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- p2x = p->x - 1;
- p2y = p->y + 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = p->x + 1;
+ p2y = p->y + 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- p2x = p->x + 1;
- p2y = p->y - 1;
- if (p2x >= 0 && p2x < width &&
- p2y >= 0 && p2y < height &&
- curvatures[p2x + p2y * width] > 0.0 &&
- ! visited[p2x + p2y * width])
- {
- Pixel *p2 = g_new (Pixel, 1);
+ p2x = p->x - 1;
+ p2y = p->y - 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
- p2->x = p2x;
- p2->y = p2y;
- g_queue_push_tail (q, p2);
- visited[p2x + p2y * width] = TRUE;
- }
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
- if (sc > max_smoothed_curvature)
- {
- max_smoothed_curvature_pixel = *p;
- max_smoothed_curvature = sc;
- }
- if (c > max_raw_curvature)
- {
- max_raw_curvature_pixel = *p;
- max_raw_curvature = c;
- }
- g_free (p);
- }
- if (max_smoothed_curvature > 0.0f)
- {
- curvatures[(gint) max_smoothed_curvature_pixel.x + (gint) max_smoothed_curvature_pixel.y *
width] = max_smoothed_curvature;
- g_array_append_val (max_positions, max_smoothed_curvature_pixel);
- }
- else
- {
- curvatures[(gint) max_raw_curvature_pixel.x + (gint) max_raw_curvature_pixel.y * width] =
max_raw_curvature;
- g_array_append_val (max_positions, max_raw_curvature_pixel);
- }
- }
- }
+ p2x = p->x - 1;
+ p2y = p->y + 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
+
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
+
+ p2x = p->x + 1;
+ p2y = p->y - 1;
+ if (p2x >= 0 && p2x < width &&
+ p2y >= 0 && p2y < height &&
+ curvatures[p2x + p2y * width] > 0.0 &&
+ ! visited[p2x + p2y * width])
+ {
+ Pixel *p2 = g_new (Pixel, 1);
+
+ p2->x = p2x;
+ p2->y = p2y;
+ g_queue_push_tail (q, p2);
+ visited[p2x + p2y * width] = TRUE;
+ }
+
+ if (sc > max_smoothed_curvature)
+ {
+ max_smoothed_curvature_pixel = *p;
+ max_smoothed_curvature = sc;
+ }
+ if (c > max_raw_curvature)
+ {
+ max_raw_curvature_pixel = *p;
+ max_raw_curvature = c;
+ }
+ g_free (p);
+ }
+ if (max_smoothed_curvature > 0.0f)
+ {
+ curvatures[(gint) max_smoothed_curvature_pixel.x + (gint) max_smoothed_curvature_pixel.y *
width] = max_smoothed_curvature;
+ g_array_append_val (max_positions, max_smoothed_curvature_pixel);
+ }
+ else
+ {
+ curvatures[(gint) max_raw_curvature_pixel.x + (gint) max_raw_curvature_pixel.y * width] =
max_raw_curvature;
+ g_array_append_val (max_positions, max_raw_curvature_pixel);
+ }
+ }
+ }
+ }
+
+ end:
g_queue_free_full (q, g_free);
g_free (visited);
+ if (gimp_async_is_stopped (async))
+ {
+ g_array_free (max_positions, TRUE);
+ max_positions = NULL;
+ }
+
return max_positions;
}
@@ -1693,11 +1876,12 @@ gimp_spline_candidate_cmp (const SplineCandidate *a,
}
static GList *
-gimp_lineart_find_spline_candidates (GArray *max_positions,
- gfloat *normals,
- gint width,
- gint distance_threshold,
- gfloat max_angle_deg)
+gimp_lineart_find_spline_candidates (GArray *max_positions,
+ gfloat *normals,
+ gint width,
+ gint distance_threshold,
+ gfloat max_angle_deg,
+ GimpAsync *async)
{
GList *candidates = NULL;
const float CosMin = cosf (M_PI * (max_angle_deg / 180.0));
@@ -1708,6 +1892,15 @@ gimp_lineart_find_spline_candidates (GArray *max_positions,
Pixel p1 = g_array_index (max_positions, Pixel, i);
gint j;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ g_list_free_full (candidates, g_free);
+
+ return NULL;
+ }
+
for (j = i + 1; j < max_positions->len; j++)
{
Pixel p2 = g_array_index (max_positions, Pixel, j);
@@ -2046,7 +2239,8 @@ gimp_lineart_line_segment_until_hit (const GeglBuffer *mask,
}
static gfloat *
-gimp_lineart_estimate_strokes_radii (GeglBuffer *mask)
+gimp_lineart_estimate_strokes_radii (GeglBuffer *mask,
+ GimpAsync *async)
{
GeglBufferIterator *gi;
gfloat *dist;
@@ -2088,6 +2282,15 @@ gimp_lineart_estimate_strokes_radii (GeglBuffer *mask)
gint x;
gint y;
+ if (gimp_async_is_canceled (async))
+ {
+ gegl_buffer_iterator_stop (gi);
+
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
for (y = starty; y < endy; y++)
for (x = startx; x < endx; x++)
{
@@ -2180,8 +2383,12 @@ gimp_lineart_estimate_strokes_radii (GeglBuffer *mask)
}
}
+ end:
g_free (dist);
+ if (gimp_async_is_stopped (async))
+ g_clear_pointer (&thickness, g_free);
+
return thickness;
}
@@ -2406,7 +2613,8 @@ gimp_edgel_region_area (const GeglBuffer *mask,
/* Edgel sets */
static GArray *
-gimp_edgelset_new (GeglBuffer *buffer)
+gimp_edgelset_new (GeglBuffer *buffer,
+ GimpAsync *async)
{
GeglBufferIterator *gi;
GArray *set;
@@ -2447,6 +2655,13 @@ gimp_edgelset_new (GeglBuffer *buffer)
gint x;
gint y;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ goto end;
+ }
+
for (y = starty; y < endy; y++)
for (x = startx; x < endx; x++)
{
@@ -2468,10 +2683,21 @@ gimp_edgelset_new (GeglBuffer *buffer)
}
}
- gimp_edgelset_build_graph (set, buffer, edgel2index);
- g_hash_table_destroy (edgel2index);
+ gimp_edgelset_build_graph (set, buffer, edgel2index, async);
+ if (gimp_async_is_stopped (async))
+ goto end;
+
gimp_edgelset_init_normals (set);
+ end:
+ g_hash_table_destroy (edgel2index);
+
+ if (gimp_async_is_stopped (async))
+ {
+ g_array_free (set, TRUE);
+ set = NULL;
+ }
+
return set;
}
@@ -2505,8 +2731,9 @@ gimp_edgelset_init_normals (GArray *set)
}
static void
-gimp_edgelset_smooth_normals (GArray *set,
- int mask_size)
+gimp_edgelset_smooth_normals (GArray *set,
+ int mask_size,
+ GimpAsync *async)
{
const gfloat sigma = mask_size * 0.775;
const gfloat den = 2 * sigma * sigma;
@@ -2528,6 +2755,13 @@ gimp_edgelset_smooth_normals (GArray *set,
int n = mask_size;
int i = 1;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ return;
+ }
+
smoothed_normal = Direction2Normal[it->direction];
while (n-- && (edgel_after != edgel_before))
{
@@ -2546,7 +2780,8 @@ gimp_edgelset_smooth_normals (GArray *set,
}
static void
-gimp_edgelset_compute_curvature (GArray *set)
+gimp_edgelset_compute_curvature (GArray *set,
+ GimpAsync *async)
{
gint i;
@@ -2563,13 +2798,21 @@ gimp_edgelset_compute_curvature (GArray *set)
const float crossp = n_prev.x * n_next.y - n_prev.y * n_next.x;
it->curvature = (crossp > 0.0f) ? c : -c;
+
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ return;
+ }
}
}
static void
gimp_edgelset_build_graph (GArray *set,
GeglBuffer *buffer,
- GHashTable *edgel2index)
+ GHashTable *edgel2index,
+ GimpAsync *async)
{
Edgel edgel;
gint i;
@@ -2580,6 +2823,13 @@ gimp_edgelset_build_graph (GArray *set,
Edgel *it = g_array_index (set, Edgel *, i);
guint neighbor_pos;
+ if (gimp_async_is_canceled (async))
+ {
+ gimp_async_abort (async);
+
+ return;
+ }
+
gimp_edgelset_next8 (buffer, it, &edgel);
gimp_assert (g_hash_table_contains (edgel2index, &edgel));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]