2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1997-11-25 06:05:25 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1997-11-25 06:05:25 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1997-11-25 06:05:25 +08:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2000-04-28 01:27:28 +08:00
|
|
|
#include "config.h"
|
2000-01-14 20:41:00 +08:00
|
|
|
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <gegl.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
#include <gtk/gtk.h>
|
2000-01-14 20:41:00 +08:00
|
|
|
|
2001-01-25 06:36:18 +08:00
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
|
2002-05-03 20:45:22 +08:00
|
|
|
#include "tools-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2007-12-07 03:07:11 +08:00
|
|
|
#include "core/gimp.h"
|
2018-11-04 21:29:16 +08:00
|
|
|
#include "core/gimpasync.h"
|
|
|
|
#include "core/gimpcancelable.h"
|
2001-11-09 03:14:51 +08:00
|
|
|
#include "core/gimpdrawable-bucket-fill.h"
|
2018-04-19 05:44:34 +08:00
|
|
|
#include "core/gimpdrawable-edit.h"
|
2018-11-04 23:22:56 +08:00
|
|
|
#include "core/gimpdrawablefilter.h"
|
2009-08-20 23:05:23 +08:00
|
|
|
#include "core/gimperror.h"
|
2016-03-12 02:52:36 +08:00
|
|
|
#include "core/gimpfilloptions.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpimage.h"
|
2007-03-08 23:32:58 +08:00
|
|
|
#include "core/gimpitem.h"
|
2018-11-04 01:40:50 +08:00
|
|
|
#include "core/gimplineart.h"
|
|
|
|
#include "core/gimppickable.h"
|
|
|
|
#include "core/gimppickable-contiguous-region.h"
|
2018-11-04 23:22:56 +08:00
|
|
|
#include "core/gimpprogress.h"
|
|
|
|
#include "core/gimpprojection.h"
|
2018-11-04 21:29:16 +08:00
|
|
|
#include "core/gimpwaitable.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
#include "gegl/gimp-gegl-nodes.h"
|
|
|
|
|
|
|
|
#include "operations/layer-modes/gimp-layer-modes.h"
|
|
|
|
|
2003-08-22 09:42:57 +08:00
|
|
|
#include "widgets/gimphelp-ids.h"
|
2011-10-07 03:59:07 +08:00
|
|
|
#include "widgets/gimpwidgets-utils.h"
|
2003-08-22 09:42:57 +08:00
|
|
|
|
2001-09-26 07:23:09 +08:00
|
|
|
#include "display/gimpdisplay.h"
|
|
|
|
|
2003-02-05 22:39:40 +08:00
|
|
|
#include "gimpbucketfilloptions.h"
|
2001-03-08 09:07:03 +08:00
|
|
|
#include "gimpbucketfilltool.h"
|
2003-04-16 00:05:52 +08:00
|
|
|
#include "gimptoolcontrol.h"
|
2001-01-25 06:36:18 +08:00
|
|
|
|
2003-03-26 00:38:19 +08:00
|
|
|
#include "gimp-intl.h"
|
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
struct _GimpBucketFillToolPrivate
|
|
|
|
{
|
2018-11-04 23:22:56 +08:00
|
|
|
GimpAsync *async;
|
|
|
|
GeglBuffer *line_art;
|
2018-11-21 22:52:51 +08:00
|
|
|
gfloat *distmap;
|
2018-11-04 23:22:56 +08:00
|
|
|
GWeakRef cached_image;
|
|
|
|
GWeakRef cached_drawable;
|
|
|
|
|
|
|
|
gboolean fill_in_progress;
|
|
|
|
gboolean compute_line_art_after_fill;
|
|
|
|
GeglBuffer *fill_buffer;
|
|
|
|
GeglBuffer *fill_mask;
|
|
|
|
|
|
|
|
/* For preview */
|
|
|
|
GeglNode *graph;
|
|
|
|
GeglNode *fill_node;
|
|
|
|
GeglNode *offset_node;
|
|
|
|
|
|
|
|
GimpDrawableFilter *filter;
|
2018-11-04 01:40:50 +08:00
|
|
|
};
|
|
|
|
|
2005-12-13 17:13:50 +08:00
|
|
|
/* local function prototypes */
|
2001-02-28 09:05:22 +08:00
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
static void gimp_bucket_fill_tool_constructed (GObject *object);
|
|
|
|
static void gimp_bucket_fill_tool_finalize (GObject *object);
|
|
|
|
|
|
|
|
static gboolean gimp_bucket_fill_tool_initialize (GimpTool *tool,
|
|
|
|
GimpDisplay *display,
|
|
|
|
GError **error);
|
|
|
|
|
|
|
|
static void gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GimpDisplay *display,
|
|
|
|
GimpFillOptions *fill_options);
|
|
|
|
static void gimp_bucket_fill_tool_commit (GimpBucketFillTool *tool);
|
|
|
|
static void gimp_bucket_fill_tool_halt (GimpBucketFillTool *tool);
|
|
|
|
static void gimp_bucket_fill_tool_filter_flush (GimpDrawableFilter *filter,
|
|
|
|
GimpTool *tool);
|
|
|
|
static void gimp_bucket_fill_tool_create_graph (GimpBucketFillTool *tool);
|
|
|
|
|
|
|
|
static void gimp_bucket_fill_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_bucket_fill_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_bucket_fill_tool_button_release (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_bucket_fill_tool_modifier_key (GimpTool *tool,
|
|
|
|
GdkModifierType key,
|
|
|
|
gboolean press,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_bucket_fill_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
1999-04-09 06:25:54 +08:00
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
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_tool_image_changed (GimpContext *context,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpBucketFillTool *tool);
|
|
|
|
static void gimp_bucket_fill_tool_drawable_changed (GimpImage *image,
|
|
|
|
GimpBucketFillTool *tool);
|
2018-11-19 21:13:03 +08:00
|
|
|
static void gimp_bucket_fill_tool_projection_rendered (GimpProjection *proj,
|
|
|
|
GimpBucketFillTool *tool);
|
|
|
|
static void gimp_bucket_fill_tool_drawable_painted (GimpDrawable *drawable,
|
2018-11-04 01:40:50 +08:00
|
|
|
GimpBucketFillTool *tool);
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GimpBucketFillTool, gimp_bucket_fill_tool, GIMP_TYPE_TOOL)
|
2005-12-13 17:13:50 +08:00
|
|
|
|
|
|
|
#define parent_class gimp_bucket_fill_tool_parent_class
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-02-28 09:05:22 +08:00
|
|
|
void
|
2002-03-29 11:50:29 +08:00
|
|
|
gimp_bucket_fill_tool_register (GimpToolRegisterCallback callback,
|
2003-08-22 09:42:57 +08:00
|
|
|
gpointer data)
|
2001-02-28 09:05:22 +08:00
|
|
|
{
|
2002-03-29 11:50:29 +08:00
|
|
|
(* callback) (GIMP_TYPE_BUCKET_FILL_TOOL,
|
2003-02-05 22:39:40 +08:00
|
|
|
GIMP_TYPE_BUCKET_FILL_OPTIONS,
|
|
|
|
gimp_bucket_fill_options_gui,
|
2015-09-09 03:18:49 +08:00
|
|
|
GIMP_CONTEXT_PROP_MASK_FOREGROUND |
|
|
|
|
GIMP_CONTEXT_PROP_MASK_BACKGROUND |
|
|
|
|
GIMP_CONTEXT_PROP_MASK_OPACITY |
|
|
|
|
GIMP_CONTEXT_PROP_MASK_PAINT_MODE |
|
|
|
|
GIMP_CONTEXT_PROP_MASK_PATTERN,
|
2002-03-21 20:17:17 +08:00
|
|
|
"gimp-bucket-fill-tool",
|
2001-11-21 07:00:47 +08:00
|
|
|
_("Bucket Fill"),
|
2006-09-19 02:00:22 +08:00
|
|
|
_("Bucket Fill Tool: Fill selected area with a color or pattern"),
|
2004-04-29 21:19:28 +08:00
|
|
|
N_("_Bucket Fill"), "<shift>B",
|
2003-08-22 09:42:57 +08:00
|
|
|
NULL, GIMP_HELP_TOOL_BUCKET_FILL,
|
2017-03-05 23:01:59 +08:00
|
|
|
GIMP_ICON_TOOL_BUCKET_FILL,
|
2002-05-03 19:31:08 +08:00
|
|
|
data);
|
2001-02-28 09:05:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_class_init (GimpBucketFillToolClass *klass)
|
|
|
|
{
|
2018-11-04 01:40:50 +08:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
|
|
|
|
|
|
object_class->constructed = gimp_bucket_fill_tool_constructed;
|
|
|
|
object_class->finalize = gimp_bucket_fill_tool_finalize;
|
2001-02-28 09:05:22 +08:00
|
|
|
|
2009-08-20 23:05:23 +08:00
|
|
|
tool_class->initialize = gimp_bucket_fill_tool_initialize;
|
2018-11-04 23:22:56 +08:00
|
|
|
tool_class->button_press = gimp_bucket_fill_tool_button_press;
|
|
|
|
tool_class->motion = gimp_bucket_fill_tool_motion;
|
2001-02-28 09:05:22 +08:00
|
|
|
tool_class->button_release = gimp_bucket_fill_tool_button_release;
|
|
|
|
tool_class->modifier_key = gimp_bucket_fill_tool_modifier_key;
|
|
|
|
tool_class->cursor_update = gimp_bucket_fill_tool_cursor_update;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_init (GimpBucketFillTool *bucket_fill_tool)
|
|
|
|
{
|
2005-03-04 19:42:46 +08:00
|
|
|
GimpTool *tool = GIMP_TOOL (bucket_fill_tool);
|
2001-02-28 09:05:22 +08:00
|
|
|
|
2005-03-05 03:05:40 +08:00
|
|
|
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
|
2007-03-08 23:32:58 +08:00
|
|
|
gimp_tool_control_set_wants_click (tool->control, TRUE);
|
2005-03-05 03:05:40 +08:00
|
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
|
|
|
GIMP_TOOL_CURSOR_BUCKET_FILL);
|
2014-04-20 02:09:39 +08:00
|
|
|
gimp_tool_control_set_action_opacity (tool->control,
|
2005-03-05 03:05:40 +08:00
|
|
|
"context/context-opacity-set");
|
|
|
|
gimp_tool_control_set_action_object_1 (tool->control,
|
|
|
|
"context/context-pattern-select-set");
|
2018-11-04 01:40:50 +08:00
|
|
|
|
|
|
|
bucket_fill_tool->priv = gimp_bucket_fill_tool_get_instance_private (bucket_fill_tool);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
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;
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
if (gimp_is_restored (gimp))
|
|
|
|
gimp_bucket_fill_tool_connect_handlers (tool);
|
|
|
|
else
|
|
|
|
g_idle_add (gimp_bucket_fill_tool_connect_handlers, tool);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
GimpBucketFillTool *tool = GIMP_BUCKET_FILL_TOOL (object);
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
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);
|
|
|
|
|
2018-11-20 03:25:51 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
g_clear_object (&tool->priv->line_art);
|
2018-11-21 22:52:51 +08:00
|
|
|
g_clear_pointer (&tool->priv->distmap, g_free);
|
2018-11-04 01:40:50 +08:00
|
|
|
|
|
|
|
if (image)
|
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_data (image, tool);
|
2018-11-19 21:13:03 +08:00
|
|
|
g_signal_handlers_disconnect_by_data (gimp_image_get_projection (image),
|
|
|
|
tool);
|
2018-11-04 01:40:50 +08:00
|
|
|
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 (context, tool);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
2001-02-28 09:05:22 +08:00
|
|
|
}
|
|
|
|
|
2009-08-20 23:05:23 +08:00
|
|
|
static gboolean
|
|
|
|
gimp_bucket_fill_tool_initialize (GimpTool *tool,
|
|
|
|
GimpDisplay *display,
|
|
|
|
GError **error)
|
|
|
|
{
|
2018-11-04 23:22:56 +08:00
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
2009-08-20 23:05:23 +08:00
|
|
|
|
|
|
|
if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:07:14 +08:00
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
|
|
|
|
{
|
|
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
2016-12-21 11:05:32 +08:00
|
|
|
_("Cannot modify the pixels of layer groups."));
|
2009-08-29 02:07:14 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
if (! gimp_item_is_visible (GIMP_ITEM (drawable)))
|
2009-08-20 23:05:23 +08:00
|
|
|
{
|
|
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
2018-11-04 01:40:50 +08:00
|
|
|
_("The active layer is not visible."));
|
2009-08-20 23:05:23 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
|
2013-04-24 22:27:12 +08:00
|
|
|
{
|
|
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
2018-11-04 01:40:50 +08:00
|
|
|
_("The active layer's pixels are locked."));
|
2013-04-24 22:27:12 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-08-20 23:05:23 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
1998-03-19 06:35:31 +08:00
|
|
|
static void
|
2018-11-04 23:22:56 +08:00
|
|
|
gimp_bucket_fill_tool_start (GimpBucketFillTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpContext *context = GIMP_CONTEXT (options);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
|
|
|
|
|
|
g_return_if_fail (! tool->priv->filter);
|
|
|
|
|
|
|
|
tool->priv->fill_in_progress = TRUE;
|
|
|
|
|
|
|
|
GIMP_TOOL (tool)->display = display;
|
|
|
|
GIMP_TOOL (tool)->drawable = drawable;
|
|
|
|
|
|
|
|
gimp_bucket_fill_tool_create_graph (tool);
|
|
|
|
|
|
|
|
tool->priv->filter = gimp_drawable_filter_new (drawable, _("Bucket fill"),
|
|
|
|
tool->priv->graph,
|
|
|
|
GIMP_ICON_TOOL_BUCKET_FILL);
|
|
|
|
|
|
|
|
gimp_drawable_filter_set_region (tool->priv->filter, GIMP_FILTER_REGION_DRAWABLE);
|
|
|
|
|
|
|
|
/* We only set these here, and don't need to update it since we assume
|
|
|
|
* the settings can't change while the fill started.
|
|
|
|
*/
|
|
|
|
gimp_drawable_filter_set_mode (tool->priv->filter,
|
|
|
|
gimp_context_get_paint_mode (context),
|
|
|
|
GIMP_LAYER_COLOR_SPACE_AUTO,
|
|
|
|
GIMP_LAYER_COLOR_SPACE_AUTO,
|
|
|
|
gimp_layer_mode_get_paint_composite_mode (gimp_context_get_paint_mode (context)));
|
|
|
|
gimp_drawable_filter_set_opacity (tool->priv->filter,
|
|
|
|
gimp_context_get_opacity (context));
|
|
|
|
|
|
|
|
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
|
|
|
|
gimp_bucket_fill_tool_preview (GimpBucketFillTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GimpDisplay *display,
|
|
|
|
GimpFillOptions *fill_options)
|
|
|
|
{
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
|
|
|
|
|
|
if (tool->priv->filter)
|
|
|
|
{
|
|
|
|
GeglBuffer *fill = NULL;
|
|
|
|
GeglBuffer *line_art = NULL;
|
2018-11-21 22:52:51 +08:00
|
|
|
gfloat *distmap = NULL;
|
2018-11-04 23:22:56 +08:00
|
|
|
gdouble x = coords->x;
|
|
|
|
gdouble y = coords->y;
|
|
|
|
|
|
|
|
if (! options->sample_merged)
|
|
|
|
{
|
|
|
|
gint off_x, off_y;
|
|
|
|
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
|
|
|
|
|
|
|
|
x -= (gdouble) off_x;
|
|
|
|
y -= (gdouble) off_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
|
2018-11-21 22:52:51 +08:00
|
|
|
{
|
|
|
|
line_art = g_object_ref (tool->priv->line_art);
|
|
|
|
distmap = tool->priv->distmap;
|
|
|
|
}
|
2018-11-04 23:22:56 +08:00
|
|
|
|
|
|
|
fill = gimp_drawable_get_bucket_fill_buffer (drawable,
|
2018-11-27 21:59:35 +08:00
|
|
|
line_art, distmap,
|
2018-11-04 23:22:56 +08:00
|
|
|
fill_options,
|
|
|
|
options->fill_transparent,
|
|
|
|
options->fill_criterion,
|
|
|
|
options->threshold / 255.0,
|
|
|
|
options->sample_merged,
|
|
|
|
options->diagonal_neighbors,
|
2018-11-07 21:22:10 +08:00
|
|
|
options->line_art_threshold,
|
2018-11-23 01:13:58 +08:00
|
|
|
options->line_art_max_grow,
|
2018-11-04 23:22:56 +08:00
|
|
|
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,
|
|
|
|
"buffer", fill,
|
|
|
|
NULL);
|
|
|
|
gegl_node_set (tool->priv->offset_node,
|
|
|
|
"x", x,
|
|
|
|
"y", y,
|
|
|
|
NULL);
|
|
|
|
gimp_drawable_filter_apply (tool->priv->filter, NULL);
|
|
|
|
g_object_unref (fill);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_commit (GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
if (tool->priv->filter)
|
|
|
|
{
|
2018-11-19 21:13:03 +08:00
|
|
|
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;
|
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
gimp_drawable_filter_commit (tool->priv->filter,
|
|
|
|
GIMP_PROGRESS (tool), FALSE);
|
|
|
|
gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tool)->display));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_halt (GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
if (tool->priv->graph)
|
|
|
|
{
|
|
|
|
g_clear_object (&tool->priv->graph);
|
|
|
|
tool->priv->fill_node = NULL;
|
|
|
|
tool->priv->offset_node = NULL;
|
|
|
|
}
|
|
|
|
if (tool->priv->filter)
|
|
|
|
{
|
|
|
|
gimp_drawable_filter_abort (tool->priv->filter);
|
|
|
|
g_clear_object (&tool->priv->filter);
|
|
|
|
}
|
|
|
|
g_clear_object (&tool->priv->fill_mask);
|
|
|
|
|
|
|
|
tool->priv->fill_in_progress = FALSE;
|
|
|
|
tool->priv->compute_line_art_after_fill = FALSE;
|
|
|
|
|
|
|
|
GIMP_TOOL (tool)->display = NULL;
|
|
|
|
GIMP_TOOL (tool)->drawable = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_filter_flush (GimpDrawableFilter *filter,
|
|
|
|
GimpTool *tool)
|
|
|
|
{
|
|
|
|
GimpImage *image = gimp_display_get_image (tool->display);
|
|
|
|
|
|
|
|
gimp_projection_flush (gimp_image_get_projection (image));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_create_graph (GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
GeglNode *graph;
|
|
|
|
GeglNode *output;
|
|
|
|
GeglNode *fill_node;
|
|
|
|
GeglNode *offset_node;
|
|
|
|
|
|
|
|
g_return_if_fail (! tool->priv->graph &&
|
|
|
|
! tool->priv->fill_node &&
|
|
|
|
! tool->priv->offset_node);
|
|
|
|
|
|
|
|
graph = gegl_node_new ();
|
|
|
|
|
|
|
|
fill_node = gegl_node_new_child (graph,
|
|
|
|
"operation", "gegl:buffer-source",
|
|
|
|
NULL);
|
|
|
|
offset_node = gegl_node_new_child (graph,
|
|
|
|
"operation", "gegl:translate",
|
|
|
|
NULL);
|
|
|
|
output = gegl_node_get_output_proxy (graph, "output");
|
|
|
|
gegl_node_link_many (fill_node, offset_node, output, NULL);
|
|
|
|
|
|
|
|
tool->priv->graph = graph;
|
|
|
|
tool->priv->fill_node = fill_node;
|
|
|
|
tool->priv->offset_node = offset_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2018-11-04 21:29:16 +08:00
|
|
|
GimpBucketFillTool *bucket_tool = GIMP_BUCKET_FILL_TOOL (tool);
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
2001-11-20 21:53:21 +08:00
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
if (press_type == GIMP_BUTTON_PRESS_NORMAL &&
|
2009-10-08 01:00:42 +08:00
|
|
|
gimp_image_coords_in_active_pickable (image, coords,
|
2007-03-08 23:32:58 +08:00
|
|
|
options->sample_merged, TRUE))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2016-03-12 05:03:32 +08:00
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
|
|
GimpContext *context = GIMP_CONTEXT (options);
|
|
|
|
GimpFillOptions *fill_options;
|
|
|
|
GError *error = NULL;
|
2007-03-08 23:32:58 +08:00
|
|
|
|
2016-03-16 03:10:16 +08:00
|
|
|
fill_options = gimp_fill_options_new (image->gimp, NULL, FALSE);
|
2007-03-08 23:32:58 +08:00
|
|
|
|
2016-03-12 05:41:25 +08:00
|
|
|
if (gimp_fill_options_set_by_fill_mode (fill_options, context,
|
|
|
|
options->fill_mode,
|
|
|
|
&error))
|
2014-06-03 20:00:01 +08:00
|
|
|
{
|
2016-09-15 18:24:37 +08:00
|
|
|
gimp_fill_options_set_antialias (fill_options, options->antialias);
|
|
|
|
|
2016-03-12 05:03:32 +08:00
|
|
|
gimp_context_set_opacity (GIMP_CONTEXT (fill_options),
|
|
|
|
gimp_context_get_opacity (context));
|
|
|
|
gimp_context_set_paint_mode (GIMP_CONTEXT (fill_options),
|
|
|
|
gimp_context_get_paint_mode (context));
|
2016-03-12 02:52:36 +08:00
|
|
|
|
2016-03-12 05:03:32 +08:00
|
|
|
if (options->fill_selection)
|
2016-03-12 02:52:36 +08:00
|
|
|
{
|
2018-04-19 05:44:34 +08:00
|
|
|
gimp_drawable_edit_fill (drawable, fill_options, NULL);
|
2018-11-16 20:41:12 +08:00
|
|
|
gimp_image_flush (image);
|
2016-03-12 02:52:36 +08:00
|
|
|
}
|
2016-03-12 05:03:32 +08:00
|
|
|
else
|
2016-03-12 02:52:36 +08:00
|
|
|
{
|
2018-11-04 23:22:56 +08:00
|
|
|
gimp_bucket_fill_tool_start (bucket_tool, coords, display);
|
|
|
|
gimp_bucket_fill_tool_preview (bucket_tool, coords, display, fill_options);
|
2016-03-12 02:52:36 +08:00
|
|
|
}
|
2018-11-04 23:22:56 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_message_literal (display->gimp, G_OBJECT (display),
|
|
|
|
GIMP_MESSAGE_WARNING, error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
2016-03-12 05:03:32 +08:00
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
g_object_unref (fill_options);
|
|
|
|
}
|
|
|
|
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
|
|
|
|
press_type, display);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpBucketFillTool *bucket_tool = GIMP_BUCKET_FILL_TOOL (tool);
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state, display);
|
|
|
|
|
|
|
|
if (gimp_image_coords_in_active_pickable (image, coords,
|
|
|
|
options->sample_merged, TRUE) &&
|
|
|
|
/* Fill selection only needs to happen once. */
|
|
|
|
! options->fill_selection)
|
|
|
|
{
|
|
|
|
GimpContext *context = GIMP_CONTEXT (options);
|
|
|
|
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,
|
|
|
|
options->fill_mode,
|
|
|
|
&error))
|
|
|
|
{
|
|
|
|
gimp_fill_options_set_antialias (fill_options, options->antialias);
|
|
|
|
|
|
|
|
gimp_context_set_opacity (GIMP_CONTEXT (fill_options),
|
|
|
|
gimp_context_get_opacity (context));
|
|
|
|
gimp_context_set_paint_mode (GIMP_CONTEXT (fill_options),
|
|
|
|
gimp_context_get_paint_mode (context));
|
|
|
|
|
|
|
|
gimp_bucket_fill_tool_preview (bucket_tool, coords, display, fill_options);
|
2007-12-07 03:07:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-03 20:00:01 +08:00
|
|
|
gimp_message_literal (display->gimp, G_OBJECT (display),
|
|
|
|
GIMP_MESSAGE_WARNING, error->message);
|
|
|
|
g_clear_error (&error);
|
2007-12-07 03:07:11 +08:00
|
|
|
}
|
2016-03-12 05:41:25 +08:00
|
|
|
|
|
|
|
g_object_unref (fill_options);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2018-11-04 23:22:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_button_release (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpBucketFillTool *bucket_tool = GIMP_BUCKET_FILL_TOOL (tool);
|
|
|
|
gboolean commit;
|
|
|
|
|
|
|
|
commit = (release_type != GIMP_BUTTON_RELEASE_CANCEL);
|
|
|
|
|
|
|
|
if (commit)
|
|
|
|
gimp_bucket_fill_tool_commit (bucket_tool);
|
|
|
|
|
|
|
|
gimp_bucket_fill_tool_halt (bucket_tool);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2007-03-08 23:32:58 +08:00
|
|
|
GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
|
|
|
|
release_type, display);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-11-09 03:14:51 +08:00
|
|
|
gimp_bucket_fill_tool_modifier_key (GimpTool *tool,
|
|
|
|
GdkModifierType key,
|
|
|
|
gboolean press,
|
|
|
|
GdkModifierType state,
|
2006-03-29 01:55:52 +08:00
|
|
|
GimpDisplay *display)
|
2001-11-09 03:14:51 +08:00
|
|
|
{
|
2006-09-06 02:25:31 +08:00
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
2001-11-20 21:53:21 +08:00
|
|
|
|
2011-10-07 03:59:07 +08:00
|
|
|
if (key == gimp_get_toggle_behavior_mask ())
|
2001-11-09 03:14:51 +08:00
|
|
|
{
|
2001-11-20 21:53:21 +08:00
|
|
|
switch (options->fill_mode)
|
2001-11-09 03:14:51 +08:00
|
|
|
{
|
2014-04-30 02:55:08 +08:00
|
|
|
case GIMP_BUCKET_FILL_FG:
|
|
|
|
g_object_set (options, "fill-mode", GIMP_BUCKET_FILL_BG, NULL);
|
2001-11-09 03:14:51 +08:00
|
|
|
break;
|
2003-02-08 01:12:21 +08:00
|
|
|
|
2014-04-30 02:55:08 +08:00
|
|
|
case GIMP_BUCKET_FILL_BG:
|
|
|
|
g_object_set (options, "fill-mode", GIMP_BUCKET_FILL_FG, NULL);
|
2001-11-09 03:14:51 +08:00
|
|
|
break;
|
2003-02-08 01:12:21 +08:00
|
|
|
|
2001-11-09 03:14:51 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-10-17 21:31:08 +08:00
|
|
|
else if (key == gimp_get_extend_selection_mask ())
|
2004-01-27 23:26:11 +08:00
|
|
|
{
|
|
|
|
g_object_set (options, "fill-selection", ! options->fill_selection, NULL);
|
|
|
|
}
|
2001-11-09 03:14:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-01 23:17:36 +08:00
|
|
|
gimp_bucket_fill_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2006-09-06 02:25:31 +08:00
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
2006-06-03 23:41:40 +08:00
|
|
|
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_BAD;
|
2009-10-08 01:00:42 +08:00
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2009-10-08 01:00:42 +08:00
|
|
|
if (gimp_image_coords_in_active_pickable (image, coords,
|
2006-06-03 23:41:40 +08:00
|
|
|
options->sample_merged, TRUE))
|
1998-01-22 15:02:57 +08:00
|
|
|
{
|
2009-10-08 01:00:42 +08:00
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
2006-06-03 23:41:40 +08:00
|
|
|
|
2009-08-29 02:07:14 +08:00
|
|
|
if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
|
2013-04-24 22:27:12 +08:00
|
|
|
! gimp_item_is_content_locked (GIMP_ITEM (drawable)) &&
|
|
|
|
gimp_item_is_visible (GIMP_ITEM (drawable)))
|
2009-08-22 01:34:59 +08:00
|
|
|
{
|
|
|
|
switch (options->fill_mode)
|
|
|
|
{
|
2014-04-30 02:55:08 +08:00
|
|
|
case GIMP_BUCKET_FILL_FG:
|
2009-08-22 01:34:59 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_FOREGROUND;
|
|
|
|
break;
|
|
|
|
|
2014-04-30 02:55:08 +08:00
|
|
|
case GIMP_BUCKET_FILL_BG:
|
2009-08-22 01:34:59 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_BACKGROUND;
|
|
|
|
break;
|
|
|
|
|
2014-04-30 02:55:08 +08:00
|
|
|
case GIMP_BUCKET_FILL_PATTERN:
|
2009-08-22 01:34:59 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_PATTERN;
|
|
|
|
break;
|
|
|
|
}
|
2003-05-08 22:06:03 +08:00
|
|
|
}
|
1998-01-22 15:02:57 +08:00
|
|
|
}
|
2000-07-30 00:12:40 +08:00
|
|
|
|
2006-06-03 23:41:40 +08:00
|
|
|
gimp_tool_control_set_cursor_modifier (tool->control, modifier);
|
2002-02-05 01:43:01 +08:00
|
|
|
|
2006-03-29 01:55:52 +08:00
|
|
|
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2018-11-04 01:40:50 +08:00
|
|
|
|
2018-11-04 21:29:16 +08:00
|
|
|
static void
|
2018-11-04 23:22:56 +08:00
|
|
|
gimp_bucket_fill_compute_line_art_cb (GimpAsync *async,
|
2018-11-04 21:29:16 +08:00
|
|
|
GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
if (gimp_async_is_canceled (async))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (gimp_async_is_finished (async))
|
2018-11-21 22:52:51 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2018-11-19 23:54:05 +08:00
|
|
|
|
|
|
|
g_clear_object (&tool->priv->async);
|
2018-11-04 21:29:16 +08:00
|
|
|
}
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
static void
|
|
|
|
gimp_bucket_fill_compute_line_art (GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
|
2018-11-04 23:22:56 +08:00
|
|
|
if (tool->priv->fill_in_progress)
|
|
|
|
{
|
|
|
|
tool->priv->compute_line_art_after_fill = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-20 03:25:51 +08:00
|
|
|
if (tool->priv->async)
|
|
|
|
{
|
|
|
|
gimp_cancelable_cancel (GIMP_CANCELABLE (tool->priv->async));
|
|
|
|
g_clear_object (&tool->priv->async);
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
g_clear_object (&tool->priv->line_art);
|
2018-11-21 22:52:51 +08:00
|
|
|
g_clear_pointer (&tool->priv->distmap, g_free);
|
|
|
|
|
2018-11-04 01:40:50 +08:00
|
|
|
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)
|
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.
2018-11-20 03:48:26 +08:00
|
|
|
pickable = GIMP_PICKABLE (image);
|
2018-11-04 01:40:50 +08:00
|
|
|
else if (drawable && ! options->sample_merged)
|
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.
2018-11-20 03:48:26 +08:00
|
|
|
pickable = GIMP_PICKABLE (drawable);
|
2018-11-04 01:40:50 +08:00
|
|
|
|
|
|
|
if (pickable)
|
2018-11-04 21:29:16 +08:00
|
|
|
{
|
2018-11-21 00:49:12 +08:00
|
|
|
/* 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);
|
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.
2018-11-20 03:48:26 +08:00
|
|
|
tool->priv->async =
|
|
|
|
gimp_pickable_contiguous_region_prepare_line_art_async (
|
|
|
|
pickable,
|
|
|
|
options->fill_transparent,
|
|
|
|
options->line_art_threshold,
|
|
|
|
+1);
|
2018-11-21 00:49:12 +08:00
|
|
|
g_signal_handlers_unblock_by_func (gimp_image_get_projection (GIMP_IMAGE (image)),
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
|
|
|
|
tool);
|
2018-11-04 21:29:16 +08:00
|
|
|
|
|
|
|
gimp_async_add_callback (tool->priv->async,
|
|
|
|
(GimpAsyncCallback) gimp_bucket_fill_compute_line_art_cb,
|
|
|
|
tool);
|
|
|
|
}
|
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.
2018-11-20 03:48:26 +08:00
|
|
|
|
|
|
|
g_clear_object (&image);
|
|
|
|
g_clear_object (&drawable);
|
2018-11-04 01:40:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_bucket_fill_tool_connect_handlers (gpointer data)
|
|
|
|
{
|
|
|
|
GimpBucketFillTool *tool = GIMP_BUCKET_FILL_TOOL (data);
|
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
Gimp *gimp = GIMP_CONTEXT (options)->gimp;
|
|
|
|
|
|
|
|
if (gimp_is_restored (gimp))
|
|
|
|
{
|
|
|
|
GimpContext *context = gimp_get_user_context (gimp);
|
|
|
|
GimpImage *image = gimp_context_get_image (context);
|
|
|
|
|
|
|
|
g_signal_connect (options, "notify::fill-criterion",
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_options_notified),
|
|
|
|
tool);
|
|
|
|
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);
|
2018-11-07 21:22:10 +08:00
|
|
|
g_signal_connect (options, "notify::line-art-threshold",
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_options_notified),
|
|
|
|
tool);
|
2018-11-04 01:40:50 +08:00
|
|
|
|
|
|
|
g_signal_connect (context, "image-changed",
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_image_changed),
|
|
|
|
tool);
|
|
|
|
gimp_bucket_fill_tool_image_changed (context, image, GIMP_BUCKET_FILL_TOOL (tool));
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_options_notified (GimpBucketFillOptions *options,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
GimpBucketFillTool *tool)
|
|
|
|
{
|
2018-11-07 21:22:10 +08:00
|
|
|
if ((! strcmp (pspec->name, "fill-criterion") ||
|
|
|
|
! strcmp (pspec->name, "fill-transparent") ||
|
|
|
|
! strcmp (pspec->name, "line-art-threshold") ||
|
2018-11-04 01:40:50 +08:00
|
|
|
! strcmp (pspec->name, "sample-merged")) &&
|
|
|
|
options->fill_criterion == GIMP_SELECT_CRITERION_LINE_ART)
|
|
|
|
{
|
|
|
|
gimp_bucket_fill_compute_line_art (tool);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_image_changed (GimpContext *context,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
GimpImage *prev_image = g_weak_ref_get (&tool->priv->cached_image);
|
|
|
|
|
|
|
|
if (image != prev_image)
|
|
|
|
{
|
|
|
|
GimpImage *prev_drawable = g_weak_ref_get (&tool->priv->cached_drawable);
|
|
|
|
|
|
|
|
g_clear_object (&tool->priv->line_art);
|
2018-11-21 22:52:51 +08:00
|
|
|
g_clear_pointer (&tool->priv->distmap, g_free);
|
2018-11-04 01:40:50 +08:00
|
|
|
|
|
|
|
if (prev_image)
|
2018-11-19 21:13:03 +08:00
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_data (prev_image, tool);
|
|
|
|
g_signal_handlers_disconnect_by_data (gimp_image_get_projection (prev_image),
|
|
|
|
tool);
|
|
|
|
}
|
2018-11-04 01:40:50 +08:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
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);
|
2018-11-19 21:13:03 +08:00
|
|
|
g_signal_connect (gimp_image_get_projection (image), "rendered",
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_projection_rendered),
|
|
|
|
tool);
|
2018-11-04 01:40:50 +08:00
|
|
|
gimp_bucket_fill_tool_drawable_changed (image, tool);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (prev_image)
|
|
|
|
g_object_unref (prev_image);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_bucket_fill_tool_drawable_changed (GimpImage *image,
|
|
|
|
GimpBucketFillTool *tool)
|
|
|
|
{
|
|
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
|
|
GimpDrawable *prev_drawable = g_weak_ref_get (&tool->priv->cached_drawable);
|
|
|
|
|
|
|
|
if (drawable != prev_drawable)
|
|
|
|
{
|
|
|
|
if (prev_drawable)
|
|
|
|
g_signal_handlers_disconnect_by_data (prev_drawable, tool);
|
|
|
|
|
|
|
|
g_weak_ref_set (&tool->priv->cached_drawable, drawable ? drawable : NULL);
|
|
|
|
if (drawable)
|
2018-11-19 21:13:03 +08:00
|
|
|
g_signal_connect (drawable, "painted",
|
|
|
|
G_CALLBACK (gimp_bucket_fill_tool_drawable_painted),
|
2018-11-04 01:40:50 +08:00
|
|
|
tool);
|
|
|
|
|
|
|
|
gimp_bucket_fill_compute_line_art (tool);
|
|
|
|
}
|
|
|
|
if (prev_drawable)
|
|
|
|
g_object_unref (prev_drawable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-11-19 21:13:03 +08:00
|
|
|
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)
|
2018-11-04 01:40:50 +08:00
|
|
|
{
|
2018-11-19 21:13:03 +08:00
|
|
|
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
|
|
|
|
|
|
|
|
if (! options->sample_merged)
|
|
|
|
gimp_bucket_fill_compute_line_art (tool);
|
2018-11-04 01:40:50 +08:00
|
|
|
}
|