[gimp/gimp-2-10] app: reorganize the line art code inside a GimpLineArt object.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] app: reorganize the line art code inside a GimpLineArt object.
- Date: Wed, 19 Dec 2018 15:45:40 +0000 (UTC)
commit a68524d43ba97ec8ba9a468c483cdf65ca67acdb
Author: Jehan <jehan girinstud io>
Date: Sat Dec 1 18:33:51 2018 +0100
app: reorganize the line art code inside a GimpLineArt object.
The code was too much spread out, in core and tool code, and also it was
made too specific to fill. I'll want to reuse this code at least in the
fuzzy select tool. This will avoid code duplication, and also make this
new process more self-contained and simpler to review later (the
algorithm also has a lot of settings and it is much cleaner to have them
as properties rather than passing these as parameters through many
functions).
The refactoring may not be finished; that's at least a first step.
(cherry picked from commit db18c679f382f5e0647e8204760f95659ea96126)
app/core/core-types.h | 1 +
app/core/gimpchannel-select.c | 3 +-
app/core/gimpdrawable-bucket-fill.c | 27 +-
app/core/gimpdrawable-bucket-fill.h | 14 +-
app/core/gimplineart.c | 750 +++++++++++++++++++++++++++---
app/core/gimplineart.h | 52 ++-
app/core/gimppickable-contiguous-region.c | 272 +----------
app/core/gimppickable-contiguous-region.h | 25 +-
app/pdb/drawable-edit-cmds.c | 3 +-
app/tools/gimpbucketfilltool.c | 295 ++++--------
app/tools/gimpfuzzyselecttool.c | 3 +-
pdb/groups/drawable_edit.pdb | 3 +-
12 files changed, 826 insertions(+), 622 deletions(-)
---
diff --git a/app/core/core-types.h b/app/core/core-types.h
index aedd5e37f8..050ca8181e 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -187,6 +187,7 @@ typedef struct _GimpHistogram GimpHistogram;
typedef struct _GimpIdTable GimpIdTable;
typedef struct _GimpImagefile GimpImagefile;
typedef struct _GimpInterpreterDB GimpInterpreterDB;
+typedef struct _GimpLineArt GimpLineArt;
typedef struct _GimpObjectQueue GimpObjectQueue;
typedef struct _GimpParasiteList GimpParasiteList;
typedef struct _GimpPdbProgress GimpPdbProgress;
diff --git a/app/core/gimpchannel-select.c b/app/core/gimpchannel-select.c
index d23cadcb05..dd6c433aee 100644
--- a/app/core/gimpchannel-select.c
+++ b/app/core/gimpchannel-select.c
@@ -516,13 +516,12 @@ gimp_channel_select_fuzzy (GimpChannel *channel,
pickable = GIMP_PICKABLE (drawable);
add_on = gimp_pickable_contiguous_region_by_seed (pickable,
- NULL, NULL,
+ NULL,
antialias,
threshold,
select_transparent,
select_criterion,
diagonal_neighbors,
- 0.92, 3, 20, 60, /* TODO */
x, y);
if (! sample_merged)
diff --git a/app/core/gimpdrawable-bucket-fill.c b/app/core/gimpdrawable-bucket-fill.c
index a455b9606a..3e1ada2d60 100644
--- a/app/core/gimpdrawable-bucket-fill.c
+++ b/app/core/gimpdrawable-bucket-fill.c
@@ -50,18 +50,13 @@
void
gimp_drawable_bucket_fill (GimpDrawable *drawable,
- GeglBuffer *line_art,
- gfloat *distmap,
+ GimpLineArt *line_art,
GimpFillOptions *options,
gboolean fill_transparent,
GimpSelectCriterion fill_criterion,
gdouble threshold,
gboolean sample_merged,
gboolean diagonal_neighbors,
- gfloat line_art_stroke_threshold,
- gint line_art_max_grow,
- gint line_art_segment_max_length,
- gint line_art_spline_max_length,
gdouble seed_x,
gdouble seed_y)
{
@@ -77,15 +72,10 @@ 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,
- distmap, options,
+ buffer = gimp_drawable_get_bucket_fill_buffer (drawable, line_art, options,
fill_transparent, fill_criterion,
threshold, sample_merged,
diagonal_neighbors,
- line_art_stroke_threshold,
- line_art_max_grow,
- line_art_segment_max_length,
- line_art_spline_max_length,
seed_x, seed_y, NULL,
&mask_x, &mask_y, &width, &height);
@@ -142,18 +132,13 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable,
*/
GeglBuffer *
gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable,
- GeglBuffer *line_art,
- gfloat *distmap,
+ GimpLineArt *line_art,
GimpFillOptions *options,
gboolean fill_transparent,
GimpSelectCriterion fill_criterion,
gdouble threshold,
gboolean sample_merged,
gboolean diagonal_neighbors,
- gfloat stroke_threshold,
- gint max_grow,
- gint segment_max_length,
- gint spline_max_length,
gdouble seed_x,
gdouble seed_y,
GeglBuffer **mask_buffer,
@@ -209,16 +194,12 @@ gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable,
* contiguous region.
*/
new_mask = gimp_pickable_contiguous_region_by_seed (pickable,
- line_art, distmap,
+ line_art,
antialias,
threshold,
fill_transparent,
fill_criterion,
diagonal_neighbors,
- stroke_threshold,
- max_grow,
- segment_max_length,
- spline_max_length,
(gint) seed_x,
(gint) seed_y);
if (mask_buffer && *mask_buffer)
diff --git a/app/core/gimpdrawable-bucket-fill.h b/app/core/gimpdrawable-bucket-fill.h
index 4019d82780..e2b2525d45 100644
--- a/app/core/gimpdrawable-bucket-fill.h
+++ b/app/core/gimpdrawable-bucket-fill.h
@@ -20,33 +20,23 @@
void gimp_drawable_bucket_fill (GimpDrawable *drawable,
- GeglBuffer *line_art,
- gfloat *distmap,
+ GimpLineArt *line_art,
GimpFillOptions *options,
gboolean fill_transparent,
GimpSelectCriterion fill_criterion,
gdouble threshold,
gboolean sample_merged,
gboolean diagonal_neighbors,
- gfloat line_art_stroke_threshold,
- gint line_art_max_grow,
- gint line_art_segment_max_length,
- gint line_art_spline_max_length,
gdouble x,
gdouble y);
GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable,
- GeglBuffer *line_art,
- gfloat *distmap,
+ GimpLineArt *line_art,
GimpFillOptions *options,
gboolean fill_transparent,
GimpSelectCriterion fill_criterion,
gdouble threshold,
gboolean sample_merged,
gboolean diagonal_neighbors,
- gfloat line_art_stroke_threshold,
- gint line_art_max_grow,
- gint line_art_segment_max_length,
- gint line_art_spline_max_length,
gdouble seed_x,
gdouble seed_y,
GeglBuffer **mask_buffer,
diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c
index 5fa9d8267f..ae0027ee6e 100644
--- a/app/core/gimplineart.c
+++ b/app/core/gimplineart.c
@@ -20,14 +20,74 @@
#include "config.h"
-#define GEGL_ITERATOR2_API
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libgimpmath/gimpmath.h"
#include "core-types.h"
+#include "gimp-parallel.h"
+#include "gimp-utils.h" /* GIMP_TIMER */
+#include "gimpasync.h"
+#include "gimpcancelable.h"
+#include "gimpdrawable.h"
+#include "gimpimage.h"
#include "gimplineart.h"
+#include "gimppickable.h"
+#include "gimpprojection.h"
+#include "gimpwaitable.h"
+
+#include "gimp-intl.h"
+
+enum
+{
+ PROP_0,
+ PROP_SELECT_TRANSPARENT,
+ PROP_MAX_GROW,
+ PROP_THRESHOLD,
+ PROP_SPLINE_MAX_LEN,
+ PROP_SEGMENT_MAX_LEN,
+};
+
+typedef struct _GimpLineArtPrivate GimpLineArtPrivate;
+
+struct _GimpLineArtPrivate
+{
+ gboolean frozen;
+ gboolean compute_after_thaw;
+
+ GimpAsync *async;
+
+ GimpPickable *input;
+ GeglBuffer *closed;
+ gfloat *distmap;
+
+ /* Used in the closing step. */
+ gboolean select_transparent;
+ gdouble threshold;
+ gint spline_max_len;
+ gint segment_max_len;
+
+ /* Used in the grow step. */
+ gint max_grow;
+};
+
+typedef struct
+{
+ GeglBuffer *buffer;
+
+ gboolean select_transparent;
+ gdouble threshold;
+ gint spline_max_len;
+ gint segment_max_len;
+} LineArtData;
+
+typedef struct
+{
+ GeglBuffer *closed;
+ gfloat *distmap;
+} LineArtResult;
static int DeltaX[4] = {+1, -1, 0, 0};
static int DeltaY[4] = {0, 0, +1, -1};
@@ -68,56 +128,109 @@ typedef struct _Edgel
guint next, previous;
} Edgel;
-static void gimp_lineart_denoise (GeglBuffer *buffer,
- int size);
-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);
-static GArray * gimp_lineart_curvature_extremums (gfloat *curvatures,
- gfloat *smoothed_curvatures,
- gint curvatures_width,
- gint curvatures_height);
-static gint gimp_spline_candidate_cmp (const SplineCandidate *a,
- const SplineCandidate *b,
- gpointer user_data);
-static GList * gimp_lineart_find_spline_candidates (GArray *max_positions,
- gfloat *normals,
- gint width,
- gint distance_threshold,
- gfloat max_angle_deg);
-
-static GArray * gimp_lineart_discrete_spline (Pixel p0,
- GimpVector2 n0,
- Pixel p1,
- GimpVector2 n1);
-
-static gint gimp_number_of_transitions (GArray *pixels,
- GeglBuffer *buffer);
-static gboolean gimp_lineart_curve_creates_region (GeglBuffer *mask,
- GArray *pixels,
- int lower_size_limit,
- int upper_size_limit);
-static GArray * gimp_lineart_line_segment_until_hit (const GeglBuffer *buffer,
- Pixel start,
- GimpVector2 direction,
- int size);
-static gfloat * gimp_lineart_estimate_strokes_radii (GeglBuffer *mask);
+
+static void gimp_line_art_finalize (GObject *object);
+static void gimp_line_art_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_line_art_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* Functions for asynchronous computation. */
+
+static void gimp_line_art_compute (GimpLineArt *line_art);
+static void gimp_line_art_compute_cb (GimpAsync *async,
+ GimpLineArt *line_art);
+
+static GimpAsync * gimp_line_art_prepare_async (GimpLineArt *line_art,
+ gint priority);
+static void gimp_line_art_prepare_async_func (GimpAsync *async,
+ LineArtData *data);
+static LineArtData * line_art_data_new (GeglBuffer *buffer,
+ GimpLineArt *line_art);
+static void line_art_data_free (LineArtData *data);
+static LineArtResult * line_art_result_new (GeglBuffer *line_art,
+ gfloat *distmap);
+static void line_art_result_free (LineArtResult *result);
+
+static void gimp_line_art_projection_rendered (GimpProjection *proj,
+ GimpLineArt *line_art);
+static void gimp_line_art_drawable_painted (GimpDrawable *drawable,
+ GimpLineArt *line_art);
+
+
+/* All actual computation functions. */
+
+static GeglBuffer * gimp_line_art_close (GeglBuffer *buffer,
+ gboolean select_transparent,
+ gdouble stroke_threshold,
+ gint spline_max_length,
+ gint segment_max_length,
+ gint minimal_lineart_area,
+ gint
normal_estimate_mask_size,
+ gfloat end_point_rate,
+ gfloat spline_max_angle,
+ gint
end_point_connectivity,
+ gfloat spline_roundness,
+ gboolean
allow_self_intersections,
+ gint
created_regions_significant_area,
+ gint
created_regions_minimum_area,
+ gboolean
small_segments_from_spline_sources,
+ gfloat **lineart_distmap);
+
+static void gimp_lineart_denoise (GeglBuffer *buffer,
+ int size);
+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);
+static GArray * gimp_lineart_curvature_extremums (gfloat *curvatures,
+ gfloat *smoothed_curvatures,
+ gint curvatures_width,
+ gint curvatures_height);
+static gint gimp_spline_candidate_cmp (const SplineCandidate *a,
+ const SplineCandidate *b,
+ gpointer user_data);
+static GList * gimp_lineart_find_spline_candidates (GArray *max_positions,
+ gfloat *normals,
+ gint width,
+ gint distance_threshold,
+ gfloat max_angle_deg);
+
+static GArray * gimp_lineart_discrete_spline (Pixel p0,
+ GimpVector2 n0,
+ Pixel p1,
+ GimpVector2 n1);
+
+static gint gimp_number_of_transitions (GArray *pixels,
+ GeglBuffer *buffer);
+static gboolean gimp_lineart_curve_creates_region (GeglBuffer *mask,
+ GArray *pixels,
+ int lower_size_limit,
+ int upper_size_limit);
+static GArray * gimp_lineart_line_segment_until_hit (const GeglBuffer *buffer,
+ Pixel start,
+ GimpVector2 direction,
+ int size);
+static gfloat * gimp_lineart_estimate_strokes_radii (GeglBuffer *mask);
/* Some callback-type functions. */
-static guint visited_hash_fun (Pixel *key);
-static gboolean visited_equal_fun (Pixel *e1,
- Pixel *e2);
+static guint visited_hash_fun (Pixel *key);
+static gboolean visited_equal_fun (Pixel *e1,
+ Pixel *e2);
-static inline gboolean border_in_direction (GeglBuffer *mask,
- Pixel p,
- int direction);
-static inline GimpVector2 pair2normal (Pixel p,
- gfloat *normals,
- gint width);
+static inline gboolean border_in_direction (GeglBuffer *mask,
+ Pixel p,
+ int direction);
+static inline GimpVector2 pair2normal (Pixel p,
+ gfloat *normals,
+ gint width);
/* Edgel */
@@ -158,22 +271,518 @@ static void gimp_edgelset_next8 (const GeglBuffer *buffer,
Edgel *it,
Edgel *n);
+G_DEFINE_TYPE_WITH_CODE (GimpLineArt, gimp_line_art, GIMP_TYPE_OBJECT,
+ G_ADD_PRIVATE (GimpLineArt))
+
+static void
+gimp_line_art_class_init (GimpLineArtClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_line_art_finalize;
+ object_class->set_property = gimp_line_art_set_property;
+ object_class->get_property = gimp_line_art_get_property;
+
+ g_object_class_install_property (object_class, PROP_SELECT_TRANSPARENT,
+ g_param_spec_boolean ("select-transparent",
+ _("Select transparent pixels instead of gray ones"),
+ _("Select transparent pixels instead of gray ones"),
+ TRUE,
+ G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_THRESHOLD,
+ g_param_spec_double ("threshold",
+ _("Line art detection threshold"),
+ _("Threshold to detect contour (higher values will
include more pixels)"),
+ 0.0, 1.0, 0.92,
+ G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_MAX_GROW,
+ g_param_spec_int ("max-grow",
+ _("Maximum growing size"),
+ _("Maximum number of pixels grown under the line art"),
+ 1, 100, 3,
+ G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SPLINE_MAX_LEN,
+ g_param_spec_int ("spline-max-length",
+ _("Maximum curved closing length"),
+ _("Maximum curved length (in pixels) to close the line
art"),
+ 1, 1000, 60,
+ G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SEGMENT_MAX_LEN,
+ g_param_spec_int ("segment-max-length",
+ _("Maximum straight closing length"),
+ _("Maximum straight length (in pixels) to close the
line art"),
+ 1, 1000, 20,
+ G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_line_art_init (GimpLineArt *line_art)
+{
+ line_art->priv = gimp_line_art_get_instance_private (line_art);
+}
+
+static void
+gimp_line_art_finalize (GObject *object)
+{
+ GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+ if (line_art->priv->input)
+ {
+ if (GIMP_IS_IMAGE (line_art->priv->input))
+ g_signal_handlers_disconnect_by_data (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+ line_art);
+ else
+ g_signal_handlers_disconnect_by_data (line_art->priv->input,
+ 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_bucket_fill_compute_line_art_cb() bails if the async has
+ * been canceled, to avoid accessing the dead tool.
+ */
+ gimp_cancelable_cancel (GIMP_CANCELABLE (line_art->priv->async));
+ g_clear_object (&line_art->priv->async);
+ }
+
+ g_clear_object (&line_art->priv->closed);
+ g_clear_pointer (&line_art->priv->distmap, g_free);
+}
+
+static void
+gimp_line_art_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+ switch (property_id)
+ {
+ case PROP_SELECT_TRANSPARENT:
+ line_art->priv->select_transparent = g_value_get_boolean (value);
+ gimp_line_art_compute (line_art);
+ break;
+ case PROP_MAX_GROW:
+ line_art->priv->max_grow = g_value_get_int (value);
+ break;
+ case PROP_THRESHOLD:
+ line_art->priv->threshold = g_value_get_double (value);
+ gimp_line_art_compute (line_art);
+ break;
+ case PROP_SPLINE_MAX_LEN:
+ line_art->priv->spline_max_len = g_value_get_int (value);
+ gimp_line_art_compute (line_art);
+ break;
+ case PROP_SEGMENT_MAX_LEN:
+ line_art->priv->segment_max_len = g_value_get_int (value);
+ gimp_line_art_compute (line_art);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_line_art_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLineArt *line_art = GIMP_LINE_ART (object);
+
+ switch (property_id)
+ {
+ case PROP_SELECT_TRANSPARENT:
+ g_value_set_boolean (value, line_art->priv->select_transparent);
+ break;
+ case PROP_MAX_GROW:
+ g_value_set_int (value, line_art->priv->max_grow);
+ break;
+ case PROP_THRESHOLD:
+ g_value_set_double (value, line_art->priv->threshold);
+ break;
+ case PROP_SPLINE_MAX_LEN:
+ g_value_set_int (value, line_art->priv->spline_max_len);
+ break;
+ case PROP_SEGMENT_MAX_LEN:
+ g_value_set_int (value, line_art->priv->segment_max_len);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
/* Public functions */
+GimpLineArt *
+gimp_line_art_new (void)
+{
+ return g_object_new (GIMP_TYPE_LINE_ART,
+ NULL);
+}
+
+void
+gimp_line_art_set_input (GimpLineArt *line_art,
+ GimpPickable *pickable)
+{
+ if (line_art->priv->input)
+ {
+ if (GIMP_IS_IMAGE (line_art->priv->input))
+ g_signal_handlers_disconnect_by_data (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+ line_art);
+ else
+ g_signal_handlers_disconnect_by_data (line_art->priv->input,
+ line_art);
+ }
+
+ line_art->priv->input = pickable;
+
+ gimp_line_art_compute (line_art);
+ if (pickable)
+ {
+ if (GIMP_IS_IMAGE (pickable))
+ g_signal_connect (gimp_image_get_projection (GIMP_IMAGE (pickable)), "rendered",
+ G_CALLBACK (gimp_line_art_projection_rendered),
+ line_art);
+ else if (GIMP_IS_DRAWABLE (pickable))
+ g_signal_connect (pickable, "painted",
+ G_CALLBACK (gimp_line_art_drawable_painted),
+ line_art);
+ else
+ g_return_if_reached ();
+ }
+}
+
+void
+gimp_line_art_freeze (GimpLineArt *line_art)
+{
+ g_return_if_fail (! line_art->priv->frozen);
+
+ line_art->priv->frozen = TRUE;
+ line_art->priv->compute_after_thaw = FALSE;
+}
+
+void
+gimp_line_art_thaw (GimpLineArt *line_art)
+{
+ g_return_if_fail (line_art->priv->frozen);
+
+ line_art->priv->frozen = FALSE;
+ if (line_art->priv->compute_after_thaw)
+ {
+ gimp_line_art_compute (line_art);
+ line_art->priv->compute_after_thaw = FALSE;
+ }
+}
+
+GeglBuffer *
+gimp_line_art_get (GimpLineArt *line_art,
+ gfloat **distmap)
+{
+ g_return_val_if_fail (line_art->priv->input, NULL);
+
+ if (line_art->priv->async)
+ {
+ gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async));
+ }
+ else if (! line_art->priv->closed)
+ {
+ gimp_line_art_compute (line_art);
+ if (line_art->priv->async)
+ gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async));
+ }
+
+ g_return_val_if_fail (line_art->priv->closed, NULL);
+
+ if (distmap)
+ *distmap = line_art->priv->distmap;
+
+ return line_art->priv->closed;
+}
+
+/* Functions for asynchronous computation. */
+
+static void
+gimp_line_art_compute (GimpLineArt *line_art)
+{
+ if (line_art->priv->frozen)
+ {
+ line_art->priv->compute_after_thaw = TRUE;
+ return;
+ }
+
+ if (line_art->priv->async)
+ {
+ gimp_cancelable_cancel (GIMP_CANCELABLE (line_art->priv->async));
+ g_clear_object (&line_art->priv->async);
+ }
+
+ g_clear_object (&line_art->priv->closed);
+ g_clear_pointer (&line_art->priv->distmap, g_free);
+
+ if (line_art->priv->input)
+ {
+ /* gimp_line_art_prepare_async() will flush the pickable, which
+ * may trigger this signal handler, and will leak a line art (as
+ * line_art->priv->async has not been set yet).
+ */
+ if (GIMP_IS_IMAGE (line_art->priv->input))
+ g_signal_handlers_block_by_func (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+ G_CALLBACK (gimp_line_art_projection_rendered),
+ line_art);
+ else
+ g_signal_handlers_block_by_func (line_art->priv->input,
+ G_CALLBACK (gimp_line_art_drawable_painted),
+ line_art);
+ line_art->priv->async = gimp_line_art_prepare_async (line_art, +1);
+ if (GIMP_IS_IMAGE (line_art->priv->input))
+ g_signal_handlers_unblock_by_func (gimp_image_get_projection (GIMP_IMAGE (line_art->priv->input)),
+ G_CALLBACK (gimp_line_art_projection_rendered),
+ line_art);
+ else
+ g_signal_handlers_unblock_by_func (line_art->priv->input,
+ G_CALLBACK (gimp_line_art_drawable_painted),
+ line_art);
+
+ gimp_async_add_callback_for_object (line_art->priv->async,
+ (GimpAsyncCallback) gimp_line_art_compute_cb,
+ line_art, line_art);
+ }
+}
+
+static void
+gimp_line_art_compute_cb (GimpAsync *async,
+ GimpLineArt *line_art)
+{
+ if (gimp_async_is_canceled (async))
+ return;
+
+ if (gimp_async_is_finished (async))
+ {
+ LineArtResult *result;
+
+ result = gimp_async_get_result (async);
+
+ line_art->priv->closed = g_object_ref (result->closed);
+ line_art->priv->distmap = result->distmap;
+ result->distmap = NULL;
+ }
+
+ g_clear_object (&line_art->priv->async);
+}
+
+static GimpAsync *
+gimp_line_art_prepare_async (GimpLineArt *line_art,
+ gint priority)
+{
+ GeglBuffer *buffer;
+ GimpAsync *async;
+ LineArtData *data;
+
+ g_return_val_if_fail (GIMP_IS_PICKABLE (line_art->priv->input), NULL);
+
+ gimp_pickable_flush (line_art->priv->input);
+
+ buffer = gegl_buffer_dup (gimp_pickable_get_buffer (line_art->priv->input));
+
+ data = line_art_data_new (buffer, line_art);
+
+ g_object_unref (buffer);
+
+ async = gimp_parallel_run_async_full (
+ priority,
+ (GimpParallelRunAsyncFunc) gimp_line_art_prepare_async_func,
+ data, (GDestroyNotify) line_art_data_free);
+
+ return async;
+}
+
+static void
+gimp_line_art_prepare_async_func (GimpAsync *async,
+ LineArtData *data)
+{
+ GeglBuffer *closed;
+ gfloat *distmap;
+ gboolean has_alpha;
+ gboolean select_transparent = FALSE;
+
+ has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
+
+ if (has_alpha)
+ {
+ if (data->select_transparent)
+ {
+ /* don't select transparent regions if there are no fully
+ * transparent pixels.
+ */
+ GeglBufferIterator *gi;
+
+ 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)
+ {
+ select_transparent = TRUE;
+ break;
+ }
+ p++;
+ }
+ if (select_transparent)
+ break;
+ }
+ if (select_transparent)
+ gegl_buffer_iterator_stop (gi);
+ }
+ }
+
+ /* For smart selection, we generate a binarized image with close
+ * regions, then run a composite selection with no threshold on
+ * this intermediate buffer.
+ */
+ GIMP_TIMER_START();
+
+ closed = gimp_line_art_close (data->buffer,
+ select_transparent,
+ data->threshold,
+ data->spline_max_len,
+ data->segment_max_len,
+ /*minimal_lineart_area,*/
+ 5,
+ /*normal_estimate_mask_size,*/
+ 5,
+ /*end_point_rate,*/
+ 0.85,
+ /*spline_max_angle,*/
+ 90.0,
+ /*end_point_connectivity,*/
+ 2,
+ /*spline_roundness,*/
+ 1.0,
+ /*allow_self_intersections,*/
+ TRUE,
+ /*created_regions_significant_area,*/
+ 4,
+ /*created_regions_minimum_area,*/
+ 100,
+ /*small_segments_from_spline_sources,*/
+ TRUE,
+ &distmap);
+
+ GIMP_TIMER_END("close line-art");
+
+ gimp_async_finish_full (async,
+ line_art_result_new (closed, distmap),
+ (GDestroyNotify) line_art_result_free);
+
+ line_art_data_free (data);
+}
+
+static LineArtData *
+line_art_data_new (GeglBuffer *buffer,
+ GimpLineArt *line_art)
+{
+ LineArtData *data = g_slice_new (LineArtData);
+
+ data->buffer = g_object_ref (buffer);
+ data->select_transparent = line_art->priv->select_transparent;
+ data->threshold = line_art->priv->threshold;
+ data->spline_max_len = line_art->priv->spline_max_len;
+ data->segment_max_len = line_art->priv->segment_max_len;
+
+ return data;
+}
+
+static void
+line_art_data_free (LineArtData *data)
+{
+ g_object_unref (data->buffer);
+
+ g_slice_free (LineArtData, data);
+}
+
+static LineArtResult *
+line_art_result_new (GeglBuffer *closed,
+ gfloat *distmap)
+{
+ LineArtResult *data;
+
+ data = g_slice_new (LineArtResult);
+ data->closed = closed;
+ data->distmap = distmap;
+
+ return data;
+}
+
+static void
+line_art_result_free (LineArtResult *data)
+{
+ g_object_unref (data->closed);
+ g_clear_pointer (&data->distmap, g_free);
+
+ g_slice_free (LineArtResult, data);
+}
+
+static void
+gimp_line_art_projection_rendered (GimpProjection *proj,
+ GimpLineArt *line_art)
+{
+ gimp_line_art_compute (line_art);
+}
+
+static void
+gimp_line_art_drawable_painted (GimpDrawable *drawable,
+ GimpLineArt *line_art)
+{
+ gimp_line_art_compute (line_art);
+}
+
+/* All actual computation functions. */
+
/**
- * gimp_lineart_close:
+ * gimp_line_art_close:
* @buffer: the input #GeglBuffer.
* @select_transparent: whether we binarize the alpha channel or the
* luminosity.
* @stroke_threshold: [0-1] threshold value for detecting stroke pixels
* (higher values will detect more stroke pixels).
+ * @spline_max_length: the maximum length for creating splines between
+ * end points.
+ * @segment_max_length: the maximum length for creating segments
+ * between end points. Unlike splines, segments
+ * are straight lines.
* @minimal_lineart_area: the minimum size in number pixels for area to
* be considered as line art.
* @normal_estimate_mask_size:
* @end_point_rate: threshold to estimate if a curvature is an end-point
* in [0-1] range value.
- * @spline_max_length: the maximum length for creating splines between
- * end points.
* @spline_max_angle: the maximum angle between end point normals for
* creating splines between them.
* @end_point_connectivity:
@@ -183,9 +792,6 @@ static void gimp_edgelset_next8 (const GeglBuffer *buffer,
* @created_regions_significant_area:
* @created_regions_minimum_area:
* @small_segments_from_spline_sources:
- * @segments_max_length: the maximum length for creating segments
- * between end points. Unlike splines, segments
- * are straight lines.
* @closed_distmap: a distance map of the closed line art pixels.
*
* Creates a binarized version of the strokes of @buffer, detected either
@@ -205,23 +811,23 @@ static void gimp_edgelset_next8 (const GeglBuffer *buffer,
* newly allocated float buffer is returned, which can be used
* for overflowing created masks later.
*/
-GeglBuffer *
-gimp_lineart_close (GeglBuffer *buffer,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint minimal_lineart_area,
- gint normal_estimate_mask_size,
- gfloat end_point_rate,
- gint spline_max_length,
- gfloat spline_max_angle,
- gint end_point_connectivity,
- gfloat spline_roundness,
- gboolean allow_self_intersections,
- gint created_regions_significant_area,
- gint created_regions_minimum_area,
- gboolean small_segments_from_spline_sources,
- gint segments_max_length,
- gfloat **closed_distmap)
+static GeglBuffer *
+gimp_line_art_close (GeglBuffer *buffer,
+ gboolean select_transparent,
+ gdouble stroke_threshold,
+ gint spline_max_length,
+ gint segment_max_length,
+ gint minimal_lineart_area,
+ gint normal_estimate_mask_size,
+ gfloat end_point_rate,
+ gfloat spline_max_angle,
+ gint end_point_connectivity,
+ gfloat spline_roundness,
+ gboolean allow_self_intersections,
+ gint created_regions_significant_area,
+ gint created_regions_minimum_area,
+ gboolean small_segments_from_spline_sources,
+ gfloat **closed_distmap)
{
const Babl *gray_format;
gfloat *normals;
@@ -422,7 +1028,7 @@ gimp_lineart_close (GeglBuffer *buffer,
{
GArray *segment = gimp_lineart_line_segment_until_hit (closed, *point,
pair2normal (*point, normals, width),
- segments_max_length);
+ segment_max_length);
if (segment->len &&
! gimp_lineart_curve_creates_region (closed, segment,
@@ -488,8 +1094,6 @@ gimp_lineart_close (GeglBuffer *buffer,
return closed;
}
-/* Private functions */
-
static void
gimp_lineart_denoise (GeglBuffer *buffer,
int minimum_area)
diff --git a/app/core/gimplineart.h b/app/core/gimplineart.h
index 5ab579800b..1a867b598a 100644
--- a/app/core/gimplineart.h
+++ b/app/core/gimplineart.h
@@ -22,22 +22,42 @@
#define __GIMP_LINEART__
-GeglBuffer * gimp_lineart_close (GeglBuffer *buffer,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint minimal_lineart_area,
- gint normal_estimate_mask_size,
- gfloat end_point_rate,
- gint spline_max_length,
- gfloat spline_max_angle,
- gint end_point_connectivity,
- gfloat spline_roundness,
- gboolean allow_self_intersections,
- gint created_regions_significant_area,
- gint created_regions_minimum_area,
- gboolean small_segments_from_spline_sources,
- gint segments_max_length,
- gfloat **lineart_distmap);
+#include "gimpobject.h"
+#define GIMP_TYPE_LINE_ART (gimp_line_art_get_type ())
+#define GIMP_LINE_ART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINE_ART, GimpLineArt))
+#define GIMP_LINE_ART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINE_ART,
GimpLineArtClass))
+#define GIMP_IS_LINE_ART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINE_ART))
+#define GIMP_IS_LINE_ART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINE_ART))
+#define GIMP_LINE_ART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINE_ART,
GimpLineArtClass))
+
+
+typedef struct _GimpLineArtClass GimpLineArtClass;
+typedef struct _GimpLineArtPrivate GimpLineArtPrivate;
+
+struct _GimpLineArt
+{
+ GimpObject parent_instance;
+
+ GimpLineArtPrivate *priv;
+};
+
+struct _GimpLineArtClass
+{
+ GimpObjectClass parent_class;
+};
+
+
+GType gimp_line_art_get_type (void) G_GNUC_CONST;
+
+GimpLineArt * gimp_line_art_new (void);
+
+void gimp_line_art_set_input (GimpLineArt *line_art,
+ GimpPickable *pickable);
+void gimp_line_art_freeze (GimpLineArt *line_art);
+void gimp_line_art_thaw (GimpLineArt *line_art);
+
+GeglBuffer * gimp_line_art_get (GimpLineArt *line_art,
+ gfloat **distmap);
#endif /* __GIMP_LINEART__ */
diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.c
index 472e5e32f3..06b4095646 100644
--- a/app/core/gimppickable-contiguous-region.c
+++ b/app/core/gimppickable-contiguous-region.c
@@ -38,15 +38,6 @@
#include "gimppickable-contiguous-region.h"
-typedef struct
-{
- GeglBuffer *buffer;
- gboolean select_transparent;
- gfloat stroke_threshold;
- gint segment_max_length;
- gint spline_max_length;
-} LineArtData;
-
typedef struct
{
gint x;
@@ -114,17 +105,6 @@ 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,
- gint segment_max_length,
- gint spline_max_length);
-static void line_art_data_free (LineArtData *data);
-static GimpPickableLineArtAsyncResult *
- line_art_result_new (GeglBuffer *line_art,
- gfloat *distmap);
-static void line_art_result_free (GimpPickableLineArtAsyncResult
- *data);
static void line_art_queue_pixel (GQueue *queue,
gint x,
gint y,
@@ -133,220 +113,46 @@ static void line_art_queue_pixel (GQueue *queue,
/* public functions */
-static void
-gimp_pickable_contiguous_region_prepare_line_art_async_func (GimpAsync *async,
- LineArtData *data)
-{
- GeglBuffer *lineart;
- gfloat *distmap;
- gboolean has_alpha;
- gboolean select_transparent = FALSE;
-
- has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer));
-
- if (has_alpha)
- {
- if (data->select_transparent)
- {
- /* don't select transparent regions if there are no fully
- * transparent pixels.
- */
- GeglBufferIterator *gi;
-
- 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)
- {
- select_transparent = TRUE;
- break;
- }
- p++;
- }
- if (select_transparent)
- break;
- }
- if (select_transparent)
- gegl_buffer_iterator_stop (gi);
- }
- }
-
- /* For smart selection, we generate a binarized image with close
- * regions, then run a composite selection with no threshold on
- * this intermediate buffer.
- */
- GIMP_TIMER_START();
-
- lineart = gimp_lineart_close (data->buffer,
- select_transparent,
- data->stroke_threshold,
- /*minimal_lineart_area,*/
- 5,
- /*normal_estimate_mask_size,*/
- 5,
- /*end_point_rate,*/
- 0.85,
- data->spline_max_length,
- /*spline_max_angle,*/
- 90.0,
- /*end_point_connectivity,*/
- 2,
- /*spline_roundness,*/
- 1.0,
- /*allow_self_intersections,*/
- TRUE,
- /*created_regions_significant_area,*/
- 4,
- /*created_regions_minimum_area,*/
- 100,
- /*small_segments_from_spline_sources,*/
- TRUE,
- data->segment_max_length,
- &distmap);
-
- GIMP_TIMER_END("close line-art");
-
- gimp_async_finish_full (async,
- line_art_result_new (lineart, distmap),
- (GDestroyNotify) line_art_result_free);
-
- line_art_data_free (data);
-}
-
-GeglBuffer *
-gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint segment_max_length,
- gint spline_max_length,
- gfloat **distmap)
-{
- GimpAsync *async;
- LineArtData *data;
- GimpPickableLineArtAsyncResult *result;
- 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,
- segment_max_length, spline_max_length);
-
- gimp_pickable_contiguous_region_prepare_line_art_async_func (async, data);
-
- result = gimp_async_get_result (async);
-
- lineart = g_object_ref (result->line_art);
- *distmap = result->distmap;
- result->distmap = NULL;
-
- g_object_unref (async);
-
- return lineart;
-}
-
-GimpAsync *
-gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint segment_max_length,
- gint spline_max_length,
- 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,
- segment_max_length, spline_max_length);
-
- 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,
- gfloat *distmap,
+ GimpLineArt *line_art,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
gboolean diagonal_neighbors,
- gfloat stroke_threshold,
- gint flooding_max,
- gint segment_max_length,
- gint spline_max_length,
gint x,
gint y)
{
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)
{
- g_return_val_if_fail ((line_art && distmap) ||
- (! line_art && ! distmap),
- NULL);
- if (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_pickable_contiguous_region_prepare_line_art (pickable, select_transparent,
- stroke_threshold,
- segment_max_length,
- spline_max_length,
- &distmap);
+ line_art = gimp_line_art_new ();
+ gimp_line_art_set_input (line_art, pickable);
free_line_art = TRUE;
}
- src_buffer = line_art;
+ src_buffer = gimp_line_art_get (line_art, &distmap);
+ g_return_val_if_fail (src_buffer && distmap, NULL);
smart_line_art = TRUE;
antialias = FALSE;
@@ -427,8 +233,8 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
*/
gfloat *mask;
GQueue *queue = g_queue_new ();
- gint width = gegl_buffer_get_width (line_art);
- gint height = gegl_buffer_get_height (line_art);
+ gint width = gegl_buffer_get_width (src_buffer);
+ gint height = gegl_buffer_get_height (src_buffer);
gint nx, ny;
GIMP_TIMER_START();
@@ -520,6 +326,9 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
}
}
+ g_object_get (line_art,
+ "max-grow", &line_art_max_grow,
+ NULL);
while (! g_queue_is_empty (queue))
{
BorderPixel *c = g_queue_pop_head (queue);
@@ -527,7 +336,7 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
if (mask[c->x + c->y * width] != 1.0)
{
mask[c->x + c->y * width] = 1.0;
- if (c->level >= flooding_max)
+ if (c->level >= line_art_max_grow)
/* Do not overflood under line arts. */
continue;
if (c->x > 0)
@@ -600,10 +409,7 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
GIMP_TIMER_END("watershed line art");
if (free_line_art)
- {
- g_object_unref (src_buffer);
- g_free (distmap);
- }
+ g_clear_object (&line_art);
}
return mask_buffer;
@@ -1149,54 +955,6 @@ find_contiguous_region (GeglBuffer *src_buffer,
#endif
}
-static LineArtData *
-line_art_data_new (GeglBuffer *buffer,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint segment_max_length,
- gint spline_max_length)
-{
- LineArtData *data = g_slice_new (LineArtData);
-
- data->buffer = g_object_ref (buffer);
- data->select_transparent = select_transparent;
- data->stroke_threshold = stroke_threshold;
- data->segment_max_length = segment_max_length;
- data->spline_max_length = spline_max_length;
-
- return data;
-}
-
-static void
-line_art_data_free (LineArtData *data)
-{
- g_object_unref (data->buffer);
-
- g_slice_free (LineArtData, data);
-}
-
-static GimpPickableLineArtAsyncResult *
-line_art_result_new (GeglBuffer *line_art,
- gfloat *distmap)
-{
- GimpPickableLineArtAsyncResult *data;
-
- data = g_slice_new (GimpPickableLineArtAsyncResult);
- data->line_art = line_art;
- data->distmap = distmap;
-
- return data;
-}
-
-static void
-line_art_result_free (GimpPickableLineArtAsyncResult *data)
-{
- g_object_unref (data->line_art);
- g_clear_pointer (&data->distmap, g_free);
-
- g_slice_free (GimpPickableLineArtAsyncResult, data);
-}
-
static void
line_art_queue_pixel (GQueue *queue,
gint x,
diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h
index 772f9d3cdc..9b4ffa544f 100644
--- a/app/core/gimppickable-contiguous-region.h
+++ b/app/core/gimppickable-contiguous-region.h
@@ -18,37 +18,14 @@
#ifndef __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
#define __GIMP_PICKABLE_CONTIGUOUS_REGION_H__
-typedef struct
-{
- GeglBuffer *line_art;
- gfloat *distmap;
-} GimpPickableLineArtAsyncResult;
-
-GeglBuffer * gimp_pickable_contiguous_region_prepare_line_art (GimpPickable *pickable,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint segment_max_length,
- gint spline_max_length,
- gfloat **distmap);
-GimpAsync * gimp_pickable_contiguous_region_prepare_line_art_async (GimpPickable *pickable,
- gboolean select_transparent,
- gfloat stroke_threshold,
- gint segment_max_length,
- gint spline_max_length,
- gint priority);
GeglBuffer * gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
- GeglBuffer *line_art,
- gfloat *line_art_distmap,
+ GimpLineArt *line_art,
gboolean antialias,
gfloat threshold,
gboolean select_transparent,
GimpSelectCriterion select_criterion,
gboolean diagonal_neighbors,
- gfloat
line_art_stroke_threshold,
- gint line_art_max_grow,
- gint
line_art_segment_max_length,
- gint
line_art_spline_max_length,
gint x,
gint y);
diff --git a/app/pdb/drawable-edit-cmds.c b/app/pdb/drawable-edit-cmds.c
index 4d7c8a9cfc..a5212bb1cd 100644
--- a/app/pdb/drawable-edit-cmds.c
+++ b/app/pdb/drawable-edit-cmds.c
@@ -165,13 +165,12 @@ drawable_edit_bucket_fill_invoker (GimpProcedure *procedure,
if (gimp_fill_options_set_by_fill_type (options, context,
fill_type, error))
{
- gimp_drawable_bucket_fill (drawable, NULL, NULL, options,
+ gimp_drawable_bucket_fill (drawable, NULL, options,
GIMP_PDB_CONTEXT (context)->sample_transparent,
GIMP_PDB_CONTEXT (context)->sample_criterion,
GIMP_PDB_CONTEXT (context)->sample_threshold,
GIMP_PDB_CONTEXT (context)->sample_merged,
GIMP_PDB_CONTEXT (context)->diagonal_neighbors,
- 0.92, 3, 20, 60, /* TODO */
x, y);
}
else
diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c
index 3b2f1968c0..bf0f0f243f 100644
--- a/app/tools/gimpbucketfilltool.c
+++ b/app/tools/gimpbucketfilltool.c
@@ -62,15 +62,10 @@
struct _GimpBucketFillToolPrivate
{
- GimpAsync *async;
- GeglBuffer *line_art;
- gfloat *distmap;
+ GimpLineArt *line_art;
GWeakRef cached_image;
GWeakRef cached_drawable;
- gboolean fill_in_progress;
- gboolean compute_line_art_after_fill;
- GeglBuffer *fill_buffer;
GeglBuffer *fill_mask;
/* For preview */
@@ -130,20 +125,17 @@ static void gimp_bucket_fill_tool_cursor_update (GimpTool *t
GdkModifierType state,
GimpDisplay *display);
-static void gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool);
static gboolean gimp_bucket_fill_tool_connect_handlers (gpointer data);
static void gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
GParamSpec *pspec,
GimpBucketFillTool *tool);
+static void gimp_bucket_fill_reset_line_art (GimpBucketFillTool *tool,
+ GimpBucketFillOptions *options);
static void gimp_bucket_fill_tool_image_changed (GimpContext *context,
GimpImage *image,
GimpBucketFillTool *tool);
static void gimp_bucket_fill_tool_drawable_changed (GimpImage *image,
GimpBucketFillTool *tool);
-static void gimp_bucket_fill_tool_projection_rendered (GimpProjection *proj,
- GimpBucketFillTool *tool);
-static void gimp_bucket_fill_tool_drawable_painted (GimpDrawable *drawable,
- GimpBucketFillTool *tool);
G_DEFINE_TYPE_WITH_PRIVATE (GimpBucketFillTool, gimp_bucket_fill_tool, GIMP_TYPE_COLOR_TOOL)
@@ -212,9 +204,28 @@ gimp_bucket_fill_tool_constructed (GObject *object)
GimpTool *tool = GIMP_TOOL (object);
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (object);
Gimp *gimp = GIMP_CONTEXT (options)->gimp;
+ GimpLineArt *line_art;
G_OBJECT_CLASS (parent_class)->constructed (object);
+ line_art = gimp_line_art_new ();
+ g_object_bind_property (options, "fill-transparent",
+ line_art, "select-transparent",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (options, "line-art-threshold",
+ line_art, "threshold",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (options, "line-art-max-grow",
+ line_art, "max-grow",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (options, "line-art-spline-max-len",
+ line_art, "spline-max-length",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (options, "line-art-segment-max-len",
+ line_art, "segment-max-length",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ GIMP_BUCKET_FILL_TOOL (tool)->priv->line_art = line_art;
+
/* Avoid computing initial line art several times (for every option
* property as it gets deserialized) if tool is selected when starting
* GIMP with an image.
@@ -236,38 +247,16 @@ gimp_bucket_fill_tool_finalize (GObject *object)
Gimp *gimp = GIMP_CONTEXT (options)->gimp;
GimpContext *context = gimp_get_user_context (gimp);
GimpImage *image = g_weak_ref_get (&tool->priv->cached_image);
- GimpDrawable *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
- if (tool->priv->async)
- {
- /* we cancel the async, but don't wait for it to finish, since
- * it can't actually be interrupted. instead
- * gimp_bucket_fill_compute_line_art_cb() bails if the async has
- * been canceled, to avoid accessing the dead tool.
- */
- gimp_cancelable_cancel (GIMP_CANCELABLE (tool->priv->async));
- g_clear_object (&tool->priv->async);
- }
g_clear_object (&tool->priv->line_art);
- g_clear_pointer (&tool->priv->distmap, g_free);
if (image)
{
g_signal_handlers_disconnect_by_data (image, tool);
- g_signal_handlers_disconnect_by_data (gimp_image_get_projection (image),
- tool);
g_object_unref (image);
}
- if (drawable)
- {
- g_signal_handlers_disconnect_by_data (drawable, tool);
- g_object_unref (drawable);
- }
- g_signal_handlers_disconnect_by_func (options,
- G_CALLBACK (gimp_bucket_fill_tool_options_notified),
- tool);
+ g_signal_handlers_disconnect_by_data (options, tool);
g_signal_handlers_disconnect_by_data (context, tool);
G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -324,7 +313,7 @@ gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
g_return_if_fail (! tool->priv->filter);
- tool->priv->fill_in_progress = TRUE;
+ gimp_line_art_freeze (tool->priv->line_art);
GIMP_TOOL (tool)->display = display;
GIMP_TOOL (tool)->drawable = drawable;
@@ -351,12 +340,6 @@ gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
g_signal_connect (tool->priv->filter, "flush",
G_CALLBACK (gimp_bucket_fill_tool_filter_flush),
tool);
-
- if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART &&
- tool->priv->async)
- {
- gimp_waitable_wait (GIMP_WAITABLE (tool->priv->async));
- }
}
static void
@@ -372,8 +355,6 @@ gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
if (tool->priv->filter)
{
GeglBuffer *fill = NULL;
- GeglBuffer *line_art = NULL;
- gfloat *distmap = NULL;
gdouble x = coords->x;
gdouble y = coords->y;
@@ -387,29 +368,16 @@ gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
y -= (gdouble) off_y;
}
- if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
- {
- line_art = g_object_ref (tool->priv->line_art);
- distmap = tool->priv->distmap;
- }
-
fill = gimp_drawable_get_bucket_fill_buffer (drawable,
- line_art, distmap,
+ tool->priv->line_art,
fill_options,
options->fill_transparent,
options->fill_criterion,
options->threshold / 255.0,
options->sample_merged,
options->diagonal_neighbors,
- options->line_art_threshold,
- options->line_art_max_grow,
- options->line_art_segment_max_len,
- options->line_art_spline_max_len,
x, y, &tool->priv->fill_mask,
&x, &y, NULL, NULL);
- if (line_art)
- g_object_unref (line_art);
-
if (fill)
{
gegl_node_set (tool->priv->fill_node,
@@ -430,12 +398,6 @@ gimp_bucket_fill_tool_commit (GimpBucketFillTool *tool)
{
if (tool->priv->filter)
{
- GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
- /* Make sure the drawable will signal being painted. */
- if (! options->sample_merged)
- tool->priv->fill_in_progress = FALSE;
-
gimp_drawable_filter_commit (tool->priv->filter,
GIMP_PROGRESS (tool), FALSE);
gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tool)->display));
@@ -458,8 +420,7 @@ gimp_bucket_fill_tool_halt (GimpBucketFillTool *tool)
}
g_clear_object (&tool->priv->fill_mask);
- tool->priv->fill_in_progress = FALSE;
- tool->priv->compute_line_art_after_fill = FALSE;
+ gimp_line_art_thaw (tool->priv->line_art);
GIMP_TOOL (tool)->display = NULL;
GIMP_TOOL (tool)->drawable = NULL;
@@ -593,8 +554,6 @@ gimp_bucket_fill_tool_motion (GimpTool *tool,
GimpFillOptions *fill_options;
GError *error = NULL;
- g_return_if_fail (bucket_tool->priv->fill_in_progress);
-
fill_options = gimp_fill_options_new (image->gimp, NULL, FALSE);
if (gimp_fill_options_set_by_fill_mode (fill_options, context,
@@ -755,92 +714,6 @@ gimp_bucket_fill_tool_cursor_update (GimpTool *tool,
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
}
-static void
-gimp_bucket_fill_compute_line_art_cb (GimpAsync *async,
- GimpBucketFillTool *tool)
-{
- if (gimp_async_is_canceled (async))
- return;
-
- if (gimp_async_is_finished (async))
- {
- GimpPickableLineArtAsyncResult *result;
-
- result = gimp_async_get_result (async);
-
- tool->priv->line_art = g_object_ref (result->line_art);
- tool->priv->distmap = result->distmap;
- result->distmap = NULL;
- }
-
- g_clear_object (&tool->priv->async);
-}
-
-static void
-gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool)
-{
- GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
- if (tool->priv->fill_in_progress)
- {
- tool->priv->compute_line_art_after_fill = TRUE;
- return;
- }
-
- if (tool->priv->async)
- {
- gimp_cancelable_cancel (GIMP_CANCELABLE (tool->priv->async));
- g_clear_object (&tool->priv->async);
- }
-
- g_clear_object (&tool->priv->line_art);
- g_clear_pointer (&tool->priv->distmap, g_free);
-
- if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
- {
- GimpPickable *pickable = NULL;
- GimpDrawable *image = g_weak_ref_get (&tool->priv->cached_image);
- GimpDrawable *drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
- if (image && options->sample_merged)
- pickable = GIMP_PICKABLE (image);
- else if (drawable && ! options->sample_merged)
- pickable = GIMP_PICKABLE (drawable);
-
- if (pickable)
- {
- /* gimp_pickable_contiguous_region_prepare_line_art_async()
- * will flush the pickable, which may trigger this signal
- * handler, and will leak a line art (as tool->priv->async has
- * not been set yet.
- */
- g_signal_handlers_block_by_func (gimp_image_get_projection (GIMP_IMAGE (image)),
- G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
- tool);
- tool->priv->async =
- gimp_pickable_contiguous_region_prepare_line_art_async (
- pickable,
- options->fill_transparent,
- options->line_art_threshold,
- options->line_art_segment_max_len,
- options->line_art_spline_max_len,
- +1);
- g_signal_handlers_unblock_by_func (gimp_image_get_projection (GIMP_IMAGE (image)),
- G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
- tool);
-
- gimp_async_add_callback_for_object (
- tool->priv->async,
- (GimpAsyncCallback) gimp_bucket_fill_compute_line_art_cb,
- tool,
- tool);
- }
-
- g_clear_object (&image);
- g_clear_object (&drawable);
- }
-}
-
static gboolean
gimp_bucket_fill_tool_connect_handlers (gpointer data)
{
@@ -859,13 +732,6 @@ gimp_bucket_fill_tool_connect_handlers (gpointer data)
g_signal_connect (options, "notify::sample-merged",
G_CALLBACK (gimp_bucket_fill_tool_options_notified),
tool);
- g_signal_connect (options, "notify::fill-transparent",
- G_CALLBACK (gimp_bucket_fill_tool_options_notified),
- tool);
- g_signal_connect (options, "notify::line-art-threshold",
- G_CALLBACK (gimp_bucket_fill_tool_options_notified),
- tool);
-
g_signal_connect (options, "notify::fill-mode",
G_CALLBACK (gimp_bucket_fill_tool_options_notified),
tool);
@@ -885,13 +751,10 @@ gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
GParamSpec *pspec,
GimpBucketFillTool *tool)
{
- if ((! strcmp (pspec->name, "fill-criterion") ||
- ! strcmp (pspec->name, "fill-transparent") ||
- ! strcmp (pspec->name, "line-art-threshold") ||
- ! strcmp (pspec->name, "sample-merged")) &&
- options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
+ if (! strcmp (pspec->name, "fill-criterion") ||
+ ! strcmp (pspec->name, "sample-merged"))
{
- gimp_bucket_fill_compute_line_art (tool);
+ gimp_bucket_fill_reset_line_art (tool, options);
}
else if (! strcmp (pspec->name, "fill-mode"))
{
@@ -920,6 +783,49 @@ gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
}
}
+static void
+gimp_bucket_fill_reset_line_art (GimpBucketFillTool *tool,
+ GimpBucketFillOptions *options)
+{
+ GimpImage *prev_image = g_weak_ref_get (&tool->priv->cached_image);
+ GimpContext *context = gimp_get_user_context (GIMP_CONTEXT (options)->gimp);
+ GimpImage *image = gimp_context_get_image (context);
+
+ if (prev_image)
+ {
+ g_signal_handlers_disconnect_by_data (prev_image, tool);
+ g_object_unref (prev_image);
+ }
+ g_weak_ref_set (&tool->priv->cached_image, image ? image : NULL);
+ g_weak_ref_set (&tool->priv->cached_drawable, NULL);
+
+ if (image && options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
+ {
+ GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
+ GimpDrawable *drawable = gimp_image_get_active_drawable (image);
+
+ g_signal_connect (image, "active-layer-changed",
+ G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
+ tool);
+ g_signal_connect (image, "active-channel-changed",
+ G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
+ tool);
+
+ g_weak_ref_set (&tool->priv->cached_drawable, drawable ? drawable : NULL);
+
+ if (options->sample_merged)
+ gimp_line_art_set_input (tool->priv->line_art, GIMP_PICKABLE (image));
+ else if (drawable)
+ gimp_line_art_set_input (tool->priv->line_art, GIMP_PICKABLE (drawable));
+ else
+ gimp_line_art_set_input (tool->priv->line_art, NULL);
+ }
+ else
+ {
+ gimp_line_art_set_input (tool->priv->line_art, NULL);
+ }
+}
+
static void
gimp_bucket_fill_tool_image_changed (GimpContext *context,
GimpImage *image,
@@ -929,37 +835,32 @@ gimp_bucket_fill_tool_image_changed (GimpContext *context,
if (image != prev_image)
{
- GimpImage *prev_drawable = g_weak_ref_get (&tool->priv->cached_drawable);
-
- g_clear_object (&tool->priv->line_art);
- g_clear_pointer (&tool->priv->distmap, g_free);
-
if (prev_image)
{
g_signal_handlers_disconnect_by_data (prev_image, tool);
- g_signal_handlers_disconnect_by_data (gimp_image_get_projection (prev_image),
- tool);
- }
- if (prev_drawable)
- {
- g_signal_handlers_disconnect_by_data (prev_drawable, tool);
- g_object_unref (prev_drawable);
}
g_weak_ref_set (&tool->priv->cached_image, image ? image : NULL);
g_weak_ref_set (&tool->priv->cached_drawable, NULL);
if (image)
{
+ GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
+
g_signal_connect (image, "active-layer-changed",
G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
tool);
g_signal_connect (image, "active-channel-changed",
G_CALLBACK (gimp_bucket_fill_tool_drawable_changed),
tool);
- g_signal_connect (gimp_image_get_projection (image), "rendered",
- G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
- tool);
gimp_bucket_fill_tool_drawable_changed (image, tool);
+
+ if (options->sample_merged)
+ gimp_line_art_set_input (tool->priv->line_art,
+ GIMP_PICKABLE (image));
+ }
+ else
+ {
+ gimp_line_art_set_input (tool->priv->line_art, NULL);
}
}
if (prev_image)
@@ -975,37 +876,13 @@ gimp_bucket_fill_tool_drawable_changed (GimpImage *image,
if (drawable != prev_drawable)
{
- if (prev_drawable)
- g_signal_handlers_disconnect_by_data (prev_drawable, tool);
+ GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
g_weak_ref_set (&tool->priv->cached_drawable, drawable ? drawable : NULL);
- if (drawable)
- g_signal_connect (drawable, "painted",
- G_CALLBACK (gimp_bucket_fill_tool_drawable_painted),
- tool);
-
- gimp_bucket_fill_compute_line_art (tool);
+ if (! options->sample_merged)
+ gimp_line_art_set_input (tool->priv->line_art,
+ drawable ? GIMP_PICKABLE (drawable) : NULL);
}
if (prev_drawable)
g_object_unref (prev_drawable);
}
-
-static void
-gimp_bucket_fill_tool_projection_rendered (GimpProjection *proj,
- GimpBucketFillTool *tool)
-{
- GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
- if (options->sample_merged)
- gimp_bucket_fill_compute_line_art (tool);
-}
-
-static void
-gimp_bucket_fill_tool_drawable_painted (GimpDrawable *drawable,
- GimpBucketFillTool *tool)
-{
- GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
-
- if (! options->sample_merged)
- gimp_bucket_fill_compute_line_art (tool);
-}
diff --git a/app/tools/gimpfuzzyselecttool.c b/app/tools/gimpfuzzyselecttool.c
index de86b3c171..b3eee7d196 100644
--- a/app/tools/gimpfuzzyselecttool.c
+++ b/app/tools/gimpfuzzyselecttool.c
@@ -122,12 +122,11 @@ gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select,
pickable = GIMP_PICKABLE (image);
}
- return gimp_pickable_contiguous_region_by_seed (pickable, NULL, NULL,
+ return gimp_pickable_contiguous_region_by_seed (pickable, NULL,
sel_options->antialias,
options->threshold / 255.0,
options->select_transparent,
options->select_criterion,
options->diagonal_neighbors,
- 0.92, 3, 20, 60, /* TODO */
x, y);
}
diff --git a/pdb/groups/drawable_edit.pdb b/pdb/groups/drawable_edit.pdb
index 2be98f4456..79b8c395c5 100644
--- a/pdb/groups/drawable_edit.pdb
+++ b/pdb/groups/drawable_edit.pdb
@@ -169,13 +169,12 @@ HELP
if (gimp_fill_options_set_by_fill_type (options, context,
fill_type, error))
{
- gimp_drawable_bucket_fill (drawable, NULL, NULL, options,
+ gimp_drawable_bucket_fill (drawable, NULL, options,
GIMP_PDB_CONTEXT (context)->sample_transparent,
GIMP_PDB_CONTEXT (context)->sample_criterion,
GIMP_PDB_CONTEXT (context)->sample_threshold,
GIMP_PDB_CONTEXT (context)->sample_merged,
GIMP_PDB_CONTEXT (context)->diagonal_neighbors,
- 0.92, 3, 20, 60, /* TODO */
x, y);
}
else
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]