2011-05-23 21:11:08 +02:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
*
|
|
|
|
* gimpwarptool.c
|
|
|
|
* Copyright (C) 2011 Michael Muré <batolettre@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (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-11 23:27:07 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2011-05-23 21:11:08 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <gegl.h>
|
2019-03-03 14:10:40 -05:00
|
|
|
#include <gegl-plugin.h>
|
2011-05-23 21:11:08 +02:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
#include "libgimpbase/gimpbase.h"
|
2017-05-17 10:52:49 -04:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
|
|
|
|
#include "tools-types.h"
|
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
#include "config/gimpdisplayconfig.h"
|
2019-02-06 15:22:27 -05:00
|
|
|
#include "config/gimpguiconfig.h"
|
|
|
|
|
2014-11-08 00:47:39 +01:00
|
|
|
#include "gegl/gimp-gegl-apply-operation.h"
|
|
|
|
|
2011-05-23 21:11:08 +02:00
|
|
|
#include "core/gimp.h"
|
|
|
|
#include "core/gimpchannel.h"
|
2024-02-09 17:48:53 +00:00
|
|
|
#include "core/gimpcontainer.h"
|
|
|
|
#include "core/gimpdrawable-filters.h"
|
2016-05-12 01:49:53 +02:00
|
|
|
#include "core/gimpdrawablefilter.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
#include "core/gimpimage.h"
|
2014-11-08 00:47:39 +01:00
|
|
|
#include "core/gimplayer.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
#include "core/gimpprogress.h"
|
|
|
|
#include "core/gimpprojection.h"
|
2014-11-08 00:47:39 +01:00
|
|
|
#include "core/gimpsubprogress.h"
|
2020-02-05 21:34:28 +02:00
|
|
|
#include "core/gimptoolinfo.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
|
|
|
|
#include "widgets/gimphelp-ids.h"
|
2014-11-08 00:47:39 +01:00
|
|
|
#include "widgets/gimpwidgets-utils.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
|
|
|
|
#include "display/gimpdisplay.h"
|
|
|
|
|
|
|
|
#include "gimpwarptool.h"
|
|
|
|
#include "gimpwarpoptions.h"
|
|
|
|
#include "gimptoolcontrol.h"
|
2018-12-10 08:22:50 -05:00
|
|
|
#include "gimptools-utils.h"
|
2011-05-23 21:11:08 +02:00
|
|
|
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
#define STROKE_TIMER_MAX_FPS 20
|
|
|
|
#define PREVIEW_SAMPLER GEGL_SAMPLER_NEAREST
|
2013-05-22 00:08:14 +02:00
|
|
|
|
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
static void gimp_warp_tool_constructed (GObject *object);
|
|
|
|
|
2019-03-04 08:25:39 -05:00
|
|
|
static void gimp_warp_tool_control (GimpTool *tool,
|
|
|
|
GimpToolAction action,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_button_release (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_warp_tool_key_press (GimpTool *tool,
|
|
|
|
GdkEventKey *kevent,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_oper_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
gboolean proximity,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
const gchar * gimp_warp_tool_can_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
const gchar * gimp_warp_tool_can_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_warp_tool_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_warp_tool_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_options_notify (GimpTool *tool,
|
|
|
|
GimpToolOptions *options,
|
|
|
|
const GParamSpec *pspec);
|
|
|
|
|
|
|
|
static void gimp_warp_tool_draw (GimpDrawTool *draw_tool);
|
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
static void gimp_warp_tool_cursor_notify (GimpDisplayConfig *config,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
GimpWarpTool *wt);
|
|
|
|
|
2019-03-04 08:25:39 -05:00
|
|
|
static gboolean gimp_warp_tool_can_stroke (GimpWarpTool *wt,
|
|
|
|
GimpDisplay *display,
|
|
|
|
gboolean show_message);
|
|
|
|
|
|
|
|
static gboolean gimp_warp_tool_start (GimpWarpTool *wt,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_warp_tool_halt (GimpWarpTool *wt);
|
|
|
|
static void gimp_warp_tool_commit (GimpWarpTool *wt);
|
|
|
|
|
|
|
|
static void gimp_warp_tool_start_stroke_timer (GimpWarpTool *wt);
|
|
|
|
static void gimp_warp_tool_stop_stroke_timer (GimpWarpTool *wt);
|
|
|
|
static gboolean gimp_warp_tool_stroke_timer (GimpWarpTool *wt);
|
|
|
|
|
|
|
|
static void gimp_warp_tool_create_graph (GimpWarpTool *wt);
|
|
|
|
static void gimp_warp_tool_create_filter (GimpWarpTool *wt,
|
|
|
|
GimpDrawable *drawable);
|
|
|
|
static void gimp_warp_tool_set_sampler (GimpWarpTool *wt,
|
|
|
|
gboolean commit);
|
2017-05-19 18:14:45 -04:00
|
|
|
static GeglRectangle
|
2019-03-04 08:25:39 -05:00
|
|
|
gimp_warp_tool_get_stroke_bounds (GeglNode *node);
|
|
|
|
static GeglRectangle gimp_warp_tool_get_node_bounds (GeglNode *node);
|
|
|
|
static void gimp_warp_tool_clear_node_bounds (GeglNode *node);
|
|
|
|
static GeglRectangle gimp_warp_tool_get_invalidated_by_change (GimpWarpTool *wt,
|
|
|
|
const GeglRectangle *area);
|
|
|
|
static void gimp_warp_tool_update_bounds (GimpWarpTool *wt);
|
|
|
|
static void gimp_warp_tool_update_area (GimpWarpTool *wt,
|
|
|
|
const GeglRectangle *area,
|
|
|
|
gboolean synchronous);
|
|
|
|
static void gimp_warp_tool_update_stroke (GimpWarpTool *wt,
|
|
|
|
GeglNode *node);
|
|
|
|
static void gimp_warp_tool_stroke_append (GimpWarpTool *wt,
|
|
|
|
gchar type,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y);
|
|
|
|
static void gimp_warp_tool_filter_flush (GimpDrawableFilter *filter,
|
|
|
|
GimpTool *tool);
|
|
|
|
static void gimp_warp_tool_add_op (GimpWarpTool *wt,
|
|
|
|
GeglNode *op);
|
|
|
|
static void gimp_warp_tool_remove_op (GimpWarpTool *wt,
|
|
|
|
GeglNode *op);
|
|
|
|
static void gimp_warp_tool_free_op (GeglNode *op);
|
|
|
|
|
|
|
|
static void gimp_warp_tool_animate (GimpWarpTool *wt);
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2013-05-22 00:08:14 +02:00
|
|
|
|
2011-05-23 21:11:08 +02:00
|
|
|
G_DEFINE_TYPE (GimpWarpTool, gimp_warp_tool, GIMP_TYPE_DRAW_TOOL)
|
|
|
|
|
|
|
|
#define parent_class gimp_warp_tool_parent_class
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_warp_tool_register (GimpToolRegisterCallback callback,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
(* callback) (GIMP_TYPE_WARP_TOOL,
|
|
|
|
GIMP_TYPE_WARP_OPTIONS,
|
|
|
|
gimp_warp_options_gui,
|
|
|
|
0,
|
|
|
|
"gimp-warp-tool",
|
|
|
|
_("Warp Transform"),
|
|
|
|
_("Warp Transform: Deform with different tools"),
|
|
|
|
N_("_Warp Transform"), "W",
|
|
|
|
NULL, GIMP_HELP_TOOL_WARP,
|
2017-02-28 19:31:27 +01:00
|
|
|
GIMP_ICON_TOOL_WARP,
|
2011-05-23 21:11:08 +02:00
|
|
|
data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_class_init (GimpWarpToolClass *klass)
|
|
|
|
{
|
2020-02-05 21:34:28 +02:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
2011-05-23 21:11:08 +02:00
|
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
|
|
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
|
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
object_class->constructed = gimp_warp_tool_constructed;
|
|
|
|
|
2013-05-22 00:33:50 +02:00
|
|
|
tool_class->control = gimp_warp_tool_control;
|
2011-05-23 21:11:08 +02:00
|
|
|
tool_class->button_press = gimp_warp_tool_button_press;
|
|
|
|
tool_class->button_release = gimp_warp_tool_button_release;
|
|
|
|
tool_class->motion = gimp_warp_tool_motion;
|
2013-05-22 00:47:43 +02:00
|
|
|
tool_class->key_press = gimp_warp_tool_key_press;
|
2011-05-23 21:11:08 +02:00
|
|
|
tool_class->oper_update = gimp_warp_tool_oper_update;
|
2013-05-22 00:47:43 +02:00
|
|
|
tool_class->cursor_update = gimp_warp_tool_cursor_update;
|
2017-07-04 18:48:03 +02:00
|
|
|
tool_class->can_undo = gimp_warp_tool_can_undo;
|
|
|
|
tool_class->can_redo = gimp_warp_tool_can_redo;
|
2013-05-22 23:01:51 +02:00
|
|
|
tool_class->undo = gimp_warp_tool_undo;
|
|
|
|
tool_class->redo = gimp_warp_tool_redo;
|
2014-06-20 01:14:53 +02:00
|
|
|
tool_class->options_notify = gimp_warp_tool_options_notify;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
|
|
|
draw_tool_class->draw = gimp_warp_tool_draw;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_init (GimpWarpTool *self)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (self);
|
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
|
2019-03-03 09:10:06 -05:00
|
|
|
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
|
2016-03-22 23:51:32 +01:00
|
|
|
gimp_tool_control_set_preserve (tool->control, FALSE);
|
|
|
|
gimp_tool_control_set_dirty_mask (tool->control,
|
|
|
|
GIMP_DIRTY_IMAGE |
|
|
|
|
GIMP_DIRTY_DRAWABLE |
|
|
|
|
GIMP_DIRTY_SELECTION |
|
|
|
|
GIMP_DIRTY_ACTIVE_DRAWABLE);
|
2018-09-29 12:25:51 -04:00
|
|
|
gimp_tool_control_set_dirty_action (tool->control,
|
|
|
|
GIMP_TOOL_ACTION_COMMIT);
|
2016-03-22 23:51:32 +01:00
|
|
|
gimp_tool_control_set_wants_click (tool->control, TRUE);
|
2019-01-31 04:37:56 -05:00
|
|
|
gimp_tool_control_set_precision (tool->control,
|
|
|
|
GIMP_CURSOR_PRECISION_SUBPIXEL);
|
2016-03-22 23:51:32 +01:00
|
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
2017-05-30 19:18:24 -04:00
|
|
|
GIMP_TOOL_CURSOR_WARP);
|
2022-04-02 23:57:08 +02:00
|
|
|
|
|
|
|
gimp_tool_control_set_action_pixel_size (tool->control,
|
2023-02-26 19:53:24 +01:00
|
|
|
"tools-warp-effect-pixel-size-set");
|
2022-04-02 23:57:08 +02:00
|
|
|
gimp_tool_control_set_action_size (tool->control,
|
2023-02-26 19:53:24 +01:00
|
|
|
"tools-warp-effect-size-set");
|
2022-04-02 23:57:08 +02:00
|
|
|
gimp_tool_control_set_action_hardness (tool->control,
|
2023-02-26 19:53:24 +01:00
|
|
|
"tools-warp-effect-hardness-set");
|
2020-02-05 21:34:28 +02:00
|
|
|
|
|
|
|
self->show_cursor = TRUE;
|
|
|
|
self->draw_brush = TRUE;
|
|
|
|
self->snap_brush = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_constructed (GObject *object)
|
|
|
|
{
|
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (object);
|
|
|
|
GimpTool *tool = GIMP_TOOL (object);
|
|
|
|
GimpDisplayConfig *display_config;
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
|
|
|
|
display_config = GIMP_DISPLAY_CONFIG (tool->tool_info->gimp->config);
|
|
|
|
|
|
|
|
wt->show_cursor = display_config->show_paint_tool_cursor;
|
|
|
|
wt->draw_brush = display_config->show_brush_outline;
|
|
|
|
wt->snap_brush = display_config->snap_brush_outline;
|
|
|
|
|
|
|
|
g_signal_connect_object (display_config, "notify::show-paint-tool-cursor",
|
|
|
|
G_CALLBACK (gimp_warp_tool_cursor_notify),
|
|
|
|
wt, 0);
|
|
|
|
g_signal_connect_object (display_config, "notify::show-brush-outline",
|
|
|
|
G_CALLBACK (gimp_warp_tool_cursor_notify),
|
|
|
|
wt, 0);
|
|
|
|
g_signal_connect_object (display_config, "notify::snap-brush-outline",
|
|
|
|
G_CALLBACK (gimp_warp_tool_cursor_notify),
|
|
|
|
wt, 0);
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_control (GimpTool *tool,
|
|
|
|
GimpToolAction action,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
|
|
|
|
switch (action)
|
|
|
|
{
|
|
|
|
case GIMP_TOOL_ACTION_PAUSE:
|
|
|
|
case GIMP_TOOL_ACTION_RESUME:
|
2011-05-25 01:54:38 +02:00
|
|
|
break;
|
|
|
|
|
2011-05-23 21:11:08 +02:00
|
|
|
case GIMP_TOOL_ACTION_HALT:
|
2013-05-22 00:33:50 +02:00
|
|
|
gimp_warp_tool_halt (wt);
|
2011-05-23 21:11:08 +02:00
|
|
|
break;
|
2014-04-04 22:34:26 +02:00
|
|
|
|
|
|
|
case GIMP_TOOL_ACTION_COMMIT:
|
|
|
|
gimp_warp_tool_commit (wt);
|
|
|
|
break;
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_warp_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display)
|
2011-05-23 21:11:08 +02:00
|
|
|
{
|
2013-05-22 23:26:07 +02:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GeglNode *new_op;
|
|
|
|
gint off_x, off_y;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
if (tool->display && display != tool->display)
|
2017-07-09 16:22:18 +02:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
if (! tool->display)
|
2015-12-09 20:30:32 +01:00
|
|
|
{
|
|
|
|
if (! gimp_warp_tool_start (wt, display))
|
|
|
|
return;
|
|
|
|
}
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
|
|
|
|
return;
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
g_return_if_fail (g_list_length (tool->drawables) == 1);
|
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
wt->current_stroke = gegl_path_new ();
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
wt->last_pos.x = coords->x;
|
|
|
|
wt->last_pos.y = coords->y;
|
|
|
|
|
|
|
|
wt->total_dist = 0.0;
|
|
|
|
|
2013-05-22 23:26:07 +02:00
|
|
|
new_op = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gegl:warp",
|
|
|
|
"behavior", options->behavior,
|
|
|
|
"size", options->effect_size,
|
2017-05-16 19:30:01 -04:00
|
|
|
"hardness", options->effect_hardness / 100.0,
|
2017-05-19 04:24:01 -04:00
|
|
|
"strength", options->effect_strength,
|
2019-03-03 15:53:28 -05:00
|
|
|
/* we implement spacing manually.
|
|
|
|
* anything > 1 will do.
|
|
|
|
*/
|
|
|
|
"spacing", 10.0,
|
2013-05-22 23:26:07 +02:00
|
|
|
"stroke", wt->current_stroke,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_warp_tool_add_op (wt, new_op);
|
2013-05-22 23:37:22 +02:00
|
|
|
g_object_unref (new_op);
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
gimp_item_get_offset (GIMP_ITEM (tool->drawables->data), &off_x, &off_y);
|
2013-05-22 22:43:32 +02:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_warp_tool_stroke_append (wt,
|
|
|
|
'M', wt->last_pos.x - off_x,
|
|
|
|
wt->last_pos.y - off_y);
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
gimp_warp_tool_start_stroke_timer (wt);
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_tool_control_activate (tool->control);
|
2013-05-22 00:33:50 +02:00
|
|
|
}
|
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
void
|
|
|
|
gimp_warp_tool_button_release (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display)
|
2013-05-22 00:33:50 +02:00
|
|
|
{
|
2013-05-22 00:47:43 +02:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (wt));
|
|
|
|
|
|
|
|
gimp_tool_control_halt (tool->control);
|
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
gimp_warp_tool_stop_stroke_timer (wt);
|
2013-05-22 00:47:43 +02:00
|
|
|
|
2013-05-22 01:04:29 +02:00
|
|
|
#ifdef WARP_DEBUG
|
|
|
|
g_printerr ("%s\n", gegl_path_to_string (wt->current_stroke));
|
|
|
|
#endif
|
2013-05-22 00:47:43 +02:00
|
|
|
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->current_stroke);
|
2013-05-22 09:01:54 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
|
2011-05-25 01:54:38 +02:00
|
|
|
{
|
2013-05-22 23:01:51 +02:00
|
|
|
gimp_warp_tool_undo (tool, display);
|
2013-05-22 23:47:31 +02:00
|
|
|
|
|
|
|
/* the just undone stroke has no business on the redo stack */
|
2017-05-16 19:34:22 -04:00
|
|
|
gimp_warp_tool_free_op (wt->redo_stack->data);
|
2013-05-22 23:47:31 +02:00
|
|
|
wt->redo_stack = g_list_remove_link (wt->redo_stack, wt->redo_stack);
|
|
|
|
}
|
2013-07-16 23:17:35 +09:00
|
|
|
else
|
2013-05-22 23:47:31 +02:00
|
|
|
{
|
2013-07-16 23:17:35 +09:00
|
|
|
if (wt->redo_stack)
|
|
|
|
{
|
|
|
|
/* the redo stack becomes invalid by actually doing a stroke */
|
2017-05-16 19:34:22 -04:00
|
|
|
g_list_free_full (wt->redo_stack,
|
|
|
|
(GDestroyNotify) gimp_warp_tool_free_op);
|
2013-07-16 23:17:35 +09:00
|
|
|
wt->redo_stack = NULL;
|
|
|
|
}
|
2015-12-09 20:30:32 +01:00
|
|
|
|
2013-07-16 23:17:35 +09:00
|
|
|
gimp_tool_push_status (tool, tool->display,
|
|
|
|
_("Press ENTER to commit the transform"));
|
2011-05-25 01:54:38 +02:00
|
|
|
}
|
2013-05-22 00:33:50 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
2013-05-22 23:26:07 +02:00
|
|
|
|
|
|
|
/* update the undo actions / menu items */
|
|
|
|
gimp_image_flush (gimp_display_get_image (GIMP_TOOL (wt)->display));
|
2013-05-22 00:47:43 +02:00
|
|
|
}
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
2019-03-03 15:53:28 -05:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GimpVector2 old_cursor_pos;
|
|
|
|
GimpVector2 delta;
|
|
|
|
gdouble dist;
|
|
|
|
gdouble step;
|
|
|
|
gboolean stroke_changed = FALSE;
|
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
if (! wt->snap_brush)
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (wt));
|
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
old_cursor_pos = wt->cursor_pos;
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
wt->cursor_pos.x = coords->x;
|
|
|
|
wt->cursor_pos.y = coords->y;
|
2011-08-01 19:28:32 +02:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_vector2_sub (&delta, &wt->cursor_pos, &old_cursor_pos);
|
|
|
|
dist = gimp_vector2_length (&delta);
|
2017-05-19 03:32:59 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
step = options->effect_size * options->stroke_spacing / 100.0;
|
|
|
|
|
|
|
|
while (wt->total_dist + dist >= step)
|
2017-05-19 03:32:59 -04:00
|
|
|
{
|
2019-03-03 15:53:28 -05:00
|
|
|
gdouble diff = step - wt->total_dist;
|
2017-05-19 03:32:59 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_vector2_mul (&delta, diff / dist);
|
|
|
|
gimp_vector2_add (&old_cursor_pos, &old_cursor_pos, &delta);
|
2017-05-19 03:32:59 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_vector2_sub (&delta, &wt->cursor_pos, &old_cursor_pos);
|
|
|
|
dist -= diff;
|
2017-05-19 03:32:59 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
wt->last_pos = old_cursor_pos;
|
|
|
|
wt->total_dist = 0.0;
|
|
|
|
|
|
|
|
if (options->stroke_during_motion)
|
|
|
|
{
|
|
|
|
gint off_x, off_y;
|
|
|
|
|
|
|
|
if (! stroke_changed)
|
|
|
|
{
|
|
|
|
stroke_changed = TRUE;
|
|
|
|
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
gimp_item_get_offset (GIMP_ITEM (tool->drawables->data), &off_x, &off_y);
|
2019-03-03 15:53:28 -05:00
|
|
|
|
|
|
|
gimp_warp_tool_stroke_append (wt,
|
|
|
|
'L', wt->last_pos.x - off_x,
|
|
|
|
wt->last_pos.y - off_y);
|
|
|
|
}
|
2017-05-19 03:32:59 -04:00
|
|
|
}
|
2013-05-22 00:08:14 +02:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
wt->total_dist += dist;
|
|
|
|
|
|
|
|
if (stroke_changed)
|
|
|
|
{
|
|
|
|
gimp_warp_tool_start_stroke_timer (wt);
|
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
2020-02-05 21:34:28 +02:00
|
|
|
|
|
|
|
if (! wt->snap_brush)
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (wt));
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_warp_tool_key_press (GimpTool *tool,
|
|
|
|
GdkEventKey *kevent,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
switch (kevent->keyval)
|
|
|
|
{
|
|
|
|
case GDK_KEY_BackSpace:
|
2011-06-04 01:38:50 +02:00
|
|
|
return TRUE;
|
|
|
|
|
2011-08-01 19:04:57 +02:00
|
|
|
case GDK_KEY_Return:
|
2011-05-23 21:11:08 +02:00
|
|
|
case GDK_KEY_KP_Enter:
|
|
|
|
case GDK_KEY_ISO_Enter:
|
2014-04-04 22:34:26 +02:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
|
2017-07-09 16:22:18 +02:00
|
|
|
return TRUE;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
|
|
|
case GDK_KEY_Escape:
|
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_oper_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
gboolean proximity,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
|
|
|
|
|
2013-05-22 00:33:50 +02:00
|
|
|
if (proximity)
|
2011-06-03 23:53:39 +02:00
|
|
|
{
|
2013-05-22 00:33:50 +02:00
|
|
|
gimp_draw_tool_pause (draw_tool);
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2013-05-22 00:33:50 +02:00
|
|
|
if (! tool->display || display == tool->display)
|
|
|
|
{
|
2019-03-03 15:53:28 -05:00
|
|
|
wt->cursor_pos.x = coords->x;
|
|
|
|
wt->cursor_pos.y = coords->y;
|
|
|
|
|
|
|
|
wt->last_pos = wt->cursor_pos;
|
2011-06-03 23:53:39 +02:00
|
|
|
}
|
2013-05-22 00:33:50 +02:00
|
|
|
|
|
|
|
if (! gimp_draw_tool_is_active (draw_tool))
|
|
|
|
gimp_draw_tool_start (draw_tool, display);
|
|
|
|
|
|
|
|
gimp_draw_tool_resume (draw_tool);
|
|
|
|
}
|
|
|
|
else if (gimp_draw_tool_is_active (draw_tool))
|
|
|
|
{
|
|
|
|
gimp_draw_tool_stop (draw_tool);
|
2011-06-03 23:53:39 +02:00
|
|
|
}
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_warp_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
2011-05-23 21:11:08 +02:00
|
|
|
{
|
2017-05-19 03:32:59 -04:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
2013-05-22 23:59:44 +02:00
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (tool);
|
2017-05-30 19:18:24 -04:00
|
|
|
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
if (! gimp_warp_tool_can_stroke (wt, display, FALSE))
|
|
|
|
{
|
|
|
|
modifier = GIMP_CURSOR_MODIFIER_BAD;
|
|
|
|
}
|
|
|
|
else if (display == tool->display)
|
2013-05-22 00:47:43 +02:00
|
|
|
{
|
2017-05-30 19:18:24 -04:00
|
|
|
#if 0
|
2013-05-22 23:59:44 +02:00
|
|
|
/* FIXME have better cursors */
|
|
|
|
|
|
|
|
switch (options->behavior)
|
|
|
|
{
|
|
|
|
case GIMP_WARP_BEHAVIOR_MOVE:
|
2018-12-12 11:13:48 -05:00
|
|
|
case GIMP_WARP_BEHAVIOR_GROW:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SHRINK:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SWIRL_CW:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SWIRL_CCW:
|
|
|
|
case GIMP_WARP_BEHAVIOR_ERASE:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SMOOTH:
|
2013-05-22 23:59:44 +02:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_MOVE;
|
|
|
|
break;
|
|
|
|
}
|
2017-05-30 19:54:11 -04:00
|
|
|
#else
|
|
|
|
(void) options;
|
2017-05-30 19:18:24 -04:00
|
|
|
#endif
|
2013-05-22 00:47:43 +02:00
|
|
|
}
|
2011-06-16 14:15:52 +02:00
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
if (! wt->show_cursor && modifier != GIMP_CURSOR_MODIFIER_BAD)
|
|
|
|
{
|
|
|
|
gimp_tool_set_cursor (tool, display,
|
|
|
|
GIMP_CURSOR_NONE,
|
|
|
|
GIMP_TOOL_CURSOR_NONE,
|
|
|
|
GIMP_CURSOR_MODIFIER_NONE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_tool_control_set_cursor_modifier (tool->control, modifier);
|
2011-07-08 11:06:49 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
|
|
|
|
}
|
2011-08-01 19:28:32 +02:00
|
|
|
|
2013-05-22 23:01:51 +02:00
|
|
|
const gchar *
|
2017-07-04 18:48:03 +02:00
|
|
|
gimp_warp_tool_can_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
2013-05-22 23:01:51 +02:00
|
|
|
{
|
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GeglNode *to_delete;
|
|
|
|
const gchar *type;
|
|
|
|
|
|
|
|
if (! wt->render_node)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
to_delete = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
|
|
|
type = gegl_node_get_operation (to_delete);
|
|
|
|
|
|
|
|
if (strcmp (type, "gegl:warp"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return _("Warp Tool Stroke");
|
|
|
|
}
|
|
|
|
|
|
|
|
const gchar *
|
2017-07-04 18:48:03 +02:00
|
|
|
gimp_warp_tool_can_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
2013-05-22 23:01:51 +02:00
|
|
|
{
|
2013-05-22 23:26:07 +02:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
|
|
|
|
if (! wt->render_node || ! wt->redo_stack)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return _("Warp Tool Stroke");
|
2013-05-22 23:01:51 +02:00
|
|
|
}
|
|
|
|
|
2014-06-20 01:14:53 +02:00
|
|
|
static gboolean
|
2013-05-22 23:01:51 +02:00
|
|
|
gimp_warp_tool_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
2013-05-22 23:26:07 +02:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GeglNode *to_delete;
|
2017-05-16 19:34:22 -04:00
|
|
|
GeglNode *prev_node;
|
2013-05-22 23:01:51 +02:00
|
|
|
|
|
|
|
to_delete = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
|
|
|
|
2017-05-16 19:34:22 -04:00
|
|
|
wt->redo_stack = g_list_prepend (wt->redo_stack, to_delete);
|
2013-05-22 23:26:07 +02:00
|
|
|
|
2017-05-16 19:34:22 -04:00
|
|
|
/* we connect render_node to the previous node, but keep the current node
|
|
|
|
* in the graph, connected to the previous node as well, so that it doesn't
|
|
|
|
* get invalidated and maintains its cache. this way, redoing it doesn't
|
|
|
|
* require reprocessing.
|
|
|
|
*/
|
|
|
|
prev_node = gegl_node_get_producer (to_delete, "input", NULL);
|
2014-11-08 00:47:39 +01:00
|
|
|
|
2023-06-13 19:58:42 +02:00
|
|
|
gegl_node_connect (prev_node, "output",
|
|
|
|
wt->render_node, "aux");
|
2013-05-22 23:01:51 +02:00
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
gimp_warp_tool_update_bounds (wt);
|
2013-05-22 23:47:31 +02:00
|
|
|
gimp_warp_tool_update_stroke (wt, to_delete);
|
|
|
|
|
2013-05-22 23:01:51 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-06-20 01:14:53 +02:00
|
|
|
static gboolean
|
2013-05-22 23:01:51 +02:00
|
|
|
gimp_warp_tool_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
2013-05-22 23:26:07 +02:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GeglNode *to_add;
|
|
|
|
|
|
|
|
to_add = wt->redo_stack->data;
|
|
|
|
|
2023-06-13 19:58:42 +02:00
|
|
|
gegl_node_connect (to_add, "output",
|
|
|
|
wt->render_node, "aux");
|
2013-05-22 23:26:07 +02:00
|
|
|
|
|
|
|
wt->redo_stack = g_list_remove_link (wt->redo_stack, wt->redo_stack);
|
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
gimp_warp_tool_update_bounds (wt);
|
2013-05-22 23:47:31 +02:00
|
|
|
gimp_warp_tool_update_stroke (wt, to_add);
|
2013-05-22 23:26:07 +02:00
|
|
|
|
|
|
|
return TRUE;
|
2013-05-22 23:01:51 +02:00
|
|
|
}
|
|
|
|
|
2014-06-20 01:14:53 +02:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_options_notify (GimpTool *tool,
|
|
|
|
GimpToolOptions *options,
|
|
|
|
const GParamSpec *pspec)
|
|
|
|
{
|
2017-05-19 18:14:45 -04:00
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
|
|
|
GimpWarpOptions *wt_options = GIMP_WARP_OPTIONS (options);
|
|
|
|
|
2014-06-20 01:14:53 +02:00
|
|
|
GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
|
|
|
|
|
|
|
|
if (! strcmp (pspec->name, "effect-size"))
|
|
|
|
{
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
2017-05-19 18:14:45 -04:00
|
|
|
else if (! strcmp (pspec->name, "interpolation"))
|
|
|
|
{
|
2019-06-03 09:55:37 -04:00
|
|
|
gimp_warp_tool_set_sampler (wt, /* commit = */ FALSE);
|
2017-05-19 18:14:45 -04:00
|
|
|
}
|
|
|
|
else if (! strcmp (pspec->name, "abyss-policy"))
|
|
|
|
{
|
|
|
|
if (wt->render_node)
|
|
|
|
{
|
|
|
|
gegl_node_set (wt->render_node,
|
|
|
|
"abyss-policy", wt_options->abyss_policy,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_warp_tool_update_stroke (wt, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (! strcmp (pspec->name, "high-quality-preview"))
|
|
|
|
{
|
|
|
|
gimp_warp_tool_set_sampler (wt, /* commit = */ FALSE);
|
|
|
|
}
|
2014-06-20 01:14:53 +02:00
|
|
|
}
|
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_draw (GimpDrawTool *draw_tool)
|
|
|
|
{
|
|
|
|
GimpWarpTool *wt = GIMP_WARP_TOOL (draw_tool);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
2020-02-05 21:34:28 +02:00
|
|
|
gdouble x, y;
|
|
|
|
|
|
|
|
if (wt->snap_brush)
|
|
|
|
{
|
|
|
|
x = wt->last_pos.x;
|
|
|
|
y = wt->last_pos.y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = wt->cursor_pos.x;
|
|
|
|
y = wt->cursor_pos.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wt->draw_brush)
|
|
|
|
{
|
|
|
|
gimp_draw_tool_add_arc (draw_tool,
|
|
|
|
FALSE,
|
|
|
|
x - options->effect_size * 0.5,
|
|
|
|
y - options->effect_size * 0.5,
|
|
|
|
options->effect_size,
|
|
|
|
options->effect_size,
|
|
|
|
0.0, 2.0 * G_PI);
|
|
|
|
}
|
|
|
|
else if (! wt->show_cursor)
|
|
|
|
{
|
|
|
|
/* don't leave the user without any indication and draw
|
|
|
|
* a fallback crosshair
|
|
|
|
*/
|
|
|
|
gimp_draw_tool_add_handle (draw_tool,
|
|
|
|
GIMP_HANDLE_CROSSHAIR,
|
|
|
|
x, y,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CROSSHAIR,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CROSSHAIR,
|
|
|
|
GIMP_HANDLE_ANCHOR_CENTER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_cursor_notify (GimpDisplayConfig *config,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (wt));
|
|
|
|
|
|
|
|
wt->show_cursor = config->show_paint_tool_cursor;
|
|
|
|
wt->draw_brush = config->show_brush_outline;
|
|
|
|
wt->snap_brush = config->snap_brush_outline;
|
2011-06-08 14:15:21 +02:00
|
|
|
|
2020-02-05 21:34:28 +02:00
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (wt));
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
2015-12-09 20:30:32 +01:00
|
|
|
static gboolean
|
2017-05-19 03:32:59 -04:00
|
|
|
gimp_warp_tool_can_stroke (GimpWarpTool *wt,
|
|
|
|
GimpDisplay *display,
|
|
|
|
gboolean show_message)
|
2011-05-23 21:11:08 +02:00
|
|
|
{
|
2022-02-15 15:42:44 +01:00
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GimpGuiConfig *config = GIMP_GUI_CONFIG (display->gimp->config);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
GimpItem *locked_item = NULL;
|
|
|
|
GList *drawables = gimp_image_get_selected_drawables (image);
|
2020-05-25 11:30:31 +02:00
|
|
|
GimpDrawable *drawable;
|
|
|
|
|
|
|
|
if (g_list_length (drawables) != 1)
|
2020-03-21 17:25:36 +01:00
|
|
|
{
|
|
|
|
if (show_message)
|
|
|
|
{
|
2020-05-25 11:30:31 +02:00
|
|
|
if (g_list_length (drawables) > 1)
|
2020-03-21 17:25:36 +01:00
|
|
|
gimp_tool_message_literal (tool, display,
|
|
|
|
_("Cannot warp multiple layers. Select only one layer."));
|
|
|
|
else
|
|
|
|
gimp_tool_message_literal (tool, display,
|
2020-05-25 11:30:31 +02:00
|
|
|
_("No active drawables."));
|
2020-03-21 17:25:36 +01:00
|
|
|
}
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
g_list_free (drawables);
|
|
|
|
|
2020-03-21 17:25:36 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
2020-05-25 11:30:31 +02:00
|
|
|
|
|
|
|
drawable = drawables->data;
|
|
|
|
g_list_free (drawables);
|
|
|
|
|
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
|
2015-12-09 20:30:32 +01:00
|
|
|
{
|
2017-05-19 03:32:59 -04:00
|
|
|
if (show_message)
|
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, display,
|
|
|
|
_("Cannot warp layer groups."));
|
|
|
|
}
|
|
|
|
|
2015-12-09 20:30:32 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-02-15 15:42:44 +01:00
|
|
|
if (gimp_item_is_content_locked (GIMP_ITEM (drawable), &locked_item))
|
2015-12-09 20:30:32 +01:00
|
|
|
{
|
2017-05-19 03:32:59 -04:00
|
|
|
if (show_message)
|
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, display,
|
2022-02-15 15:42:44 +01:00
|
|
|
_("The selected item's pixels are locked."));
|
2018-12-10 08:22:50 -05:00
|
|
|
|
2022-02-15 15:42:44 +01:00
|
|
|
gimp_tools_blink_lock_box (display->gimp, locked_item);
|
2017-05-19 03:32:59 -04:00
|
|
|
}
|
|
|
|
|
2015-12-09 20:30:32 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-06-24 15:57:21 +02:00
|
|
|
if (! gimp_item_is_visible (GIMP_ITEM (drawable)) &&
|
|
|
|
! config->edit_non_visible)
|
2015-12-09 20:30:32 +01:00
|
|
|
{
|
2017-05-19 03:32:59 -04:00
|
|
|
if (show_message)
|
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, display,
|
2022-02-15 15:42:44 +01:00
|
|
|
_("The selected item is not visible."));
|
2017-05-19 03:32:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! options->stroke_during_motion &&
|
|
|
|
! options->stroke_periodically)
|
|
|
|
{
|
|
|
|
if (show_message)
|
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, display,
|
|
|
|
_("No stroke events selected."));
|
2018-12-12 10:51:04 -05:00
|
|
|
|
2022-07-27 10:45:40 +02:00
|
|
|
gimp_tools_show_tool_options (display->gimp);
|
2018-12-12 10:51:04 -05:00
|
|
|
gimp_widget_blink (options->stroke_frame);
|
2017-05-19 03:32:59 -04:00
|
|
|
}
|
|
|
|
|
2015-12-09 20:30:32 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-12-12 11:17:49 -05:00
|
|
|
if (! wt->filter || ! gimp_tool_can_undo (tool, display))
|
|
|
|
{
|
|
|
|
const gchar *message = NULL;
|
|
|
|
|
|
|
|
switch (options->behavior)
|
|
|
|
{
|
|
|
|
case GIMP_WARP_BEHAVIOR_MOVE:
|
|
|
|
case GIMP_WARP_BEHAVIOR_GROW:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SHRINK:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SWIRL_CW:
|
|
|
|
case GIMP_WARP_BEHAVIOR_SWIRL_CCW:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_WARP_BEHAVIOR_ERASE:
|
|
|
|
message = _("No warp to erase.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_WARP_BEHAVIOR_SMOOTH:
|
|
|
|
message = _("No warp to smooth.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message)
|
|
|
|
{
|
|
|
|
if (show_message)
|
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, display, message);
|
|
|
|
|
2022-07-27 10:45:40 +02:00
|
|
|
gimp_tools_show_tool_options (display->gimp);
|
2018-12-12 11:17:49 -05:00
|
|
|
gimp_widget_blink (options->behavior_combo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_warp_tool_start (GimpWarpTool *wt,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
2020-05-25 11:30:31 +02:00
|
|
|
GimpDrawable *drawable;
|
2017-05-19 03:32:59 -04:00
|
|
|
const Babl *format;
|
|
|
|
GeglRectangle bbox;
|
|
|
|
|
|
|
|
if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
tool->display = display;
|
|
|
|
g_list_free (tool->drawables);
|
|
|
|
tool->drawables = gimp_image_get_selected_drawables (image);
|
|
|
|
|
|
|
|
drawable = tool->drawables->data;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
/* Create the coords buffer, with the size of the selection */
|
|
|
|
format = babl_format_n (babl_type ("float"), 2);
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2013-05-23 19:03:31 +02:00
|
|
|
gimp_item_mask_intersect (GIMP_ITEM (drawable), &bbox.x, &bbox.y,
|
|
|
|
&bbox.width, &bbox.height);
|
2011-06-14 23:49:58 +02:00
|
|
|
|
2013-05-22 01:04:29 +02:00
|
|
|
#ifdef WARP_DEBUG
|
|
|
|
g_printerr ("Initialize coordinate buffer (%d,%d) at %d,%d\n",
|
|
|
|
bbox.width, bbox.height, bbox.x, bbox.y);
|
|
|
|
#endif
|
|
|
|
|
2013-05-22 00:47:43 +02:00
|
|
|
wt->coords_buffer = gegl_buffer_new (&bbox, format);
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_warp_tool_create_filter (wt, drawable);
|
2013-05-22 00:47:43 +02:00
|
|
|
|
|
|
|
if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (wt)))
|
|
|
|
gimp_draw_tool_start (GIMP_DRAW_TOOL (wt), display);
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
if (options->animate_button)
|
|
|
|
{
|
|
|
|
g_signal_connect_swapped (options->animate_button, "clicked",
|
|
|
|
G_CALLBACK (gimp_warp_tool_animate),
|
|
|
|
wt);
|
|
|
|
|
|
|
|
gtk_widget_set_sensitive (options->animate_button, TRUE);
|
|
|
|
}
|
2015-12-09 20:30:32 +01:00
|
|
|
|
|
|
|
return TRUE;
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-05-22 00:47:43 +02:00
|
|
|
gimp_warp_tool_halt (GimpWarpTool *wt)
|
2011-05-23 21:11:08 +02:00
|
|
|
{
|
2014-11-08 00:47:39 +01:00
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->coords_buffer);
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->graph);
|
|
|
|
wt->render_node = NULL;
|
2011-05-23 21:11:08 +02:00
|
|
|
|
2016-05-12 01:49:53 +02:00
|
|
|
if (wt->filter)
|
2013-05-22 00:47:43 +02:00
|
|
|
{
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_drawable_filter_abort (wt->filter);
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->filter);
|
2013-05-22 00:47:43 +02:00
|
|
|
|
|
|
|
gimp_image_flush (gimp_display_get_image (tool->display));
|
|
|
|
}
|
|
|
|
|
2013-05-22 23:26:07 +02:00
|
|
|
if (wt->redo_stack)
|
|
|
|
{
|
2017-05-16 19:34:22 -04:00
|
|
|
g_list_free (wt->redo_stack);
|
2013-05-22 23:26:07 +02:00
|
|
|
wt->redo_stack = NULL;
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
tool->display = NULL;
|
|
|
|
g_list_free (tool->drawables);
|
|
|
|
tool->drawables = NULL;
|
2013-05-22 00:47:43 +02:00
|
|
|
|
|
|
|
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (wt)))
|
|
|
|
gimp_draw_tool_stop (GIMP_DRAW_TOOL (wt));
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
if (options->animate_button)
|
|
|
|
{
|
|
|
|
gtk_widget_set_sensitive (options->animate_button, FALSE);
|
|
|
|
|
|
|
|
g_signal_handlers_disconnect_by_func (options->animate_button,
|
|
|
|
gimp_warp_tool_animate,
|
|
|
|
wt);
|
|
|
|
}
|
2011-05-23 21:11:08 +02:00
|
|
|
}
|
|
|
|
|
2014-04-04 22:34:26 +02:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_commit (GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
|
2018-09-29 12:35:27 -04:00
|
|
|
/* don't commit a nop */
|
|
|
|
if (tool->display && gimp_tool_can_undo (tool, tool->display))
|
2014-04-04 22:34:26 +02:00
|
|
|
{
|
|
|
|
gimp_tool_control_push_preserve (tool->control, TRUE);
|
|
|
|
|
2017-05-19 18:14:45 -04:00
|
|
|
gimp_warp_tool_set_sampler (wt, /* commit = */ TRUE);
|
2017-05-19 03:30:37 -04:00
|
|
|
|
2023-06-19 14:54:21 +00:00
|
|
|
gimp_drawable_filter_commit (wt->filter, FALSE,
|
|
|
|
GIMP_PROGRESS (tool), FALSE);
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->filter);
|
2014-04-04 22:34:26 +02:00
|
|
|
|
|
|
|
gimp_tool_control_pop_preserve (tool->control);
|
|
|
|
|
|
|
|
gimp_image_flush (gimp_display_get_image (tool->display));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_start_stroke_timer (GimpWarpTool *wt)
|
2011-06-16 14:15:52 +02:00
|
|
|
{
|
2017-05-17 12:18:12 -04:00
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
2013-05-22 02:21:07 +02:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
gimp_warp_tool_stop_stroke_timer (wt);
|
|
|
|
|
|
|
|
if (options->stroke_periodically &&
|
|
|
|
options->stroke_periodically_rate > 0.0 &&
|
|
|
|
! (options->behavior == GIMP_WARP_BEHAVIOR_MOVE &&
|
|
|
|
options->stroke_during_motion))
|
2017-05-17 12:18:12 -04:00
|
|
|
{
|
2017-05-19 03:32:59 -04:00
|
|
|
gdouble fps;
|
2011-06-16 14:15:52 +02:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
fps = STROKE_TIMER_MAX_FPS * options->stroke_periodically_rate / 100.0;
|
2017-05-17 12:18:12 -04:00
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
wt->stroke_timer = g_timeout_add (1000.0 / fps,
|
|
|
|
(GSourceFunc) gimp_warp_tool_stroke_timer,
|
|
|
|
wt);
|
2017-05-17 12:18:12 -04:00
|
|
|
}
|
2017-05-19 03:32:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_stop_stroke_timer (GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
if (wt->stroke_timer)
|
|
|
|
g_source_remove (wt->stroke_timer);
|
|
|
|
|
|
|
|
wt->stroke_timer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_warp_tool_stroke_timer (GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
gint off_x, off_y;
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
g_return_val_if_fail (g_list_length (tool->drawables) == 1, FALSE);
|
|
|
|
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (tool->drawables->data), &off_x, &off_y);
|
2017-05-19 03:32:59 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_warp_tool_stroke_append (wt,
|
|
|
|
'L', wt->last_pos.x - off_x,
|
|
|
|
wt->last_pos.y - off_y);
|
2011-08-01 19:28:32 +02:00
|
|
|
|
2011-06-18 18:13:30 +02:00
|
|
|
return TRUE;
|
2011-06-16 14:15:52 +02:00
|
|
|
}
|
|
|
|
|
2011-05-25 01:54:38 +02:00
|
|
|
static void
|
2011-06-08 14:15:21 +02:00
|
|
|
gimp_warp_tool_create_graph (GimpWarpTool *wt)
|
2011-05-25 01:54:38 +02:00
|
|
|
{
|
2017-05-19 18:14:45 -04:00
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
2018-03-24 16:49:01 -04:00
|
|
|
GeglNode *graph; /* Wrapper to be returned */
|
2017-05-19 18:14:45 -04:00
|
|
|
GeglNode *input, *output; /* Proxy nodes */
|
|
|
|
GeglNode *coords, *render; /* Render nodes */
|
2011-05-25 01:54:38 +02:00
|
|
|
|
|
|
|
/* render_node is not supposed to be recreated */
|
2013-05-22 00:08:14 +02:00
|
|
|
g_return_if_fail (wt->graph == NULL);
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2011-06-08 14:15:21 +02:00
|
|
|
graph = gegl_node_new ();
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2011-06-08 14:15:21 +02:00
|
|
|
input = gegl_node_get_input_proxy (graph, "input");
|
|
|
|
output = gegl_node_get_output_proxy (graph, "output");
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2011-06-08 14:15:21 +02:00
|
|
|
coords = gegl_node_new_child (graph,
|
2011-05-25 01:54:38 +02:00
|
|
|
"operation", "gegl:buffer-source",
|
|
|
|
"buffer", wt->coords_buffer,
|
|
|
|
NULL);
|
|
|
|
|
2011-06-08 14:15:21 +02:00
|
|
|
render = gegl_node_new_child (graph,
|
2017-05-19 03:30:37 -04:00
|
|
|
"operation", "gegl:map-relative",
|
2017-05-19 18:14:45 -04:00
|
|
|
"abyss-policy", options->abyss_policy,
|
2011-05-25 01:54:38 +02:00
|
|
|
NULL);
|
|
|
|
|
2023-06-13 17:05:02 +02:00
|
|
|
gegl_node_link_many (input, render, output, NULL);
|
2023-06-13 19:58:42 +02:00
|
|
|
gegl_node_connect (coords, "output",
|
|
|
|
render, "aux");
|
2011-05-25 01:54:38 +02:00
|
|
|
|
|
|
|
|
2013-05-22 00:08:14 +02:00
|
|
|
wt->graph = graph;
|
2011-06-08 14:15:21 +02:00
|
|
|
wt->render_node = render;
|
2011-05-25 01:54:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_warp_tool_create_filter (GimpWarpTool *wt,
|
|
|
|
GimpDrawable *drawable)
|
2011-05-25 01:54:38 +02:00
|
|
|
{
|
2013-05-18 14:45:35 +02:00
|
|
|
if (! wt->graph)
|
2011-06-08 14:15:21 +02:00
|
|
|
gimp_warp_tool_create_graph (wt);
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2017-05-19 18:14:45 -04:00
|
|
|
gimp_warp_tool_set_sampler (wt, /* commit = */ FALSE);
|
2017-05-19 03:30:37 -04:00
|
|
|
|
2016-05-12 01:49:53 +02:00
|
|
|
wt->filter = gimp_drawable_filter_new (drawable,
|
|
|
|
_("Warp transform"),
|
|
|
|
wt->graph,
|
2017-02-28 19:31:27 +01:00
|
|
|
GIMP_ICON_TOOL_WARP);
|
2025-05-31 14:14:02 +02:00
|
|
|
gimp_drawable_filter_set_temporary (wt->filter, TRUE);
|
2011-05-25 01:54:38 +02:00
|
|
|
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_drawable_filter_set_region (wt->filter, GIMP_FILTER_REGION_DRAWABLE);
|
2013-05-23 19:30:46 +02:00
|
|
|
|
2013-05-22 01:04:29 +02:00
|
|
|
#if 0
|
2016-05-12 01:49:53 +02:00
|
|
|
g_object_set (wt->filter, "gegl-caching", TRUE, NULL);
|
2013-05-22 01:04:29 +02:00
|
|
|
#endif
|
2011-07-23 13:40:29 +02:00
|
|
|
|
2016-05-12 01:49:53 +02:00
|
|
|
g_signal_connect (wt->filter, "flush",
|
|
|
|
G_CALLBACK (gimp_warp_tool_filter_flush),
|
2011-05-25 01:54:38 +02:00
|
|
|
wt);
|
|
|
|
}
|
|
|
|
|
2013-05-22 23:47:31 +02:00
|
|
|
static void
|
2017-05-19 18:14:45 -04:00
|
|
|
gimp_warp_tool_set_sampler (GimpWarpTool *wt,
|
|
|
|
gboolean commit)
|
2013-05-22 23:47:31 +02:00
|
|
|
{
|
2017-05-19 18:14:45 -04:00
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GeglSamplerType sampler;
|
|
|
|
GeglSamplerType old_sampler;
|
|
|
|
|
|
|
|
if (! wt->render_node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (commit || options->high_quality_preview)
|
|
|
|
sampler = (GeglSamplerType) options->interpolation;
|
|
|
|
else
|
|
|
|
sampler = PREVIEW_SAMPLER;
|
|
|
|
|
|
|
|
gegl_node_get (wt->render_node,
|
|
|
|
"sampler-type", &old_sampler,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (sampler != old_sampler)
|
|
|
|
{
|
|
|
|
gegl_node_set (wt->render_node,
|
|
|
|
"sampler-type", sampler,
|
|
|
|
NULL);
|
2019-06-03 09:55:37 -04:00
|
|
|
|
|
|
|
gimp_warp_tool_update_bounds (wt);
|
|
|
|
gimp_warp_tool_update_stroke (wt, NULL);
|
2017-05-19 18:14:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GeglRectangle
|
|
|
|
gimp_warp_tool_get_stroke_bounds (GeglNode *node)
|
|
|
|
{
|
|
|
|
GeglRectangle bbox = {0, 0, 0, 0};
|
|
|
|
GeglPath *stroke;
|
|
|
|
gdouble size;
|
2013-05-22 23:47:31 +02:00
|
|
|
|
|
|
|
gegl_node_get (node,
|
|
|
|
"stroke", &stroke,
|
|
|
|
"size", &size,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (stroke)
|
|
|
|
{
|
|
|
|
gdouble min_x;
|
|
|
|
gdouble max_x;
|
|
|
|
gdouble min_y;
|
|
|
|
gdouble max_y;
|
|
|
|
|
|
|
|
gegl_path_get_bounds (stroke, &min_x, &max_x, &min_y, &max_y);
|
|
|
|
g_object_unref (stroke);
|
|
|
|
|
2017-05-19 03:32:59 -04:00
|
|
|
bbox.x = floor (min_x - size * 0.5);
|
|
|
|
bbox.y = floor (min_y - size * 0.5);
|
|
|
|
bbox.width = ceil (max_x + size * 0.5) - bbox.x;
|
|
|
|
bbox.height = ceil (max_y + size * 0.5) - bbox.y;
|
2017-05-19 18:14:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return bbox;
|
|
|
|
}
|
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
static GeglRectangle
|
|
|
|
gimp_warp_tool_get_node_bounds (GeglNode *node)
|
|
|
|
{
|
|
|
|
GeglRectangle *bounds;
|
|
|
|
|
|
|
|
if (! node || strcmp (gegl_node_get_operation (node), "gegl:warp"))
|
|
|
|
return *GEGL_RECTANGLE (0, 0, 0, 0);
|
|
|
|
|
|
|
|
bounds = g_object_get_data (G_OBJECT (node), "gimp-warp-tool-bounds");
|
|
|
|
|
|
|
|
if (! bounds)
|
|
|
|
{
|
|
|
|
GeglNode *input_node;
|
|
|
|
GeglRectangle input_bounds;
|
|
|
|
GeglRectangle stroke_bounds;
|
|
|
|
|
|
|
|
input_node = gegl_node_get_producer (node, "input", NULL);
|
|
|
|
input_bounds = gimp_warp_tool_get_node_bounds (input_node);
|
|
|
|
|
|
|
|
stroke_bounds = gimp_warp_tool_get_stroke_bounds (node);
|
|
|
|
|
|
|
|
gegl_rectangle_bounding_box (&input_bounds,
|
|
|
|
&input_bounds, &stroke_bounds);
|
|
|
|
|
|
|
|
bounds = gegl_rectangle_dup (&input_bounds);
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (node), "gimp-warp-tool-bounds",
|
|
|
|
bounds, g_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
return *bounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_clear_node_bounds (GeglNode *node)
|
|
|
|
{
|
|
|
|
if (node && ! strcmp (gegl_node_get_operation (node), "gegl:warp"))
|
|
|
|
g_object_set_data (G_OBJECT (node), "gimp-warp-tool-bounds", NULL);
|
|
|
|
}
|
|
|
|
|
2019-03-04 08:25:39 -05:00
|
|
|
static GeglRectangle
|
|
|
|
gimp_warp_tool_get_invalidated_by_change (GimpWarpTool *wt,
|
|
|
|
const GeglRectangle *area)
|
|
|
|
{
|
|
|
|
GeglRectangle result = *area;
|
|
|
|
|
|
|
|
if (! wt->filter)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (wt->render_node)
|
|
|
|
{
|
|
|
|
GeglOperation *operation = gegl_node_get_gegl_operation (wt->render_node);
|
|
|
|
|
|
|
|
result = gegl_operation_get_invalidated_by_change (operation,
|
|
|
|
"aux", area);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_update_bounds (GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
GeglRectangle bounds = {0, 0, 0, 0};
|
|
|
|
|
|
|
|
if (! wt->filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (wt->render_node)
|
|
|
|
{
|
|
|
|
GeglNode *node = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
|
|
|
|
|
|
|
bounds = gimp_warp_tool_get_node_bounds (node);
|
2019-03-04 08:25:39 -05:00
|
|
|
|
|
|
|
bounds = gimp_warp_tool_get_invalidated_by_change (wt, &bounds);
|
2018-12-30 05:10:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
gimp_drawable_filter_set_crop (wt->filter, &bounds, FALSE);
|
|
|
|
}
|
|
|
|
|
2019-03-03 14:10:40 -05:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_update_area (GimpWarpTool *wt,
|
2019-03-03 16:18:50 -05:00
|
|
|
const GeglRectangle *area,
|
|
|
|
gboolean synchronous)
|
2019-03-03 14:10:40 -05:00
|
|
|
{
|
2024-02-09 17:48:53 +00:00
|
|
|
GeglRectangle rect;
|
|
|
|
GimpContainer *filters;
|
2019-03-03 14:10:40 -05:00
|
|
|
|
|
|
|
if (! wt->filter)
|
|
|
|
return;
|
|
|
|
|
2019-03-04 08:25:39 -05:00
|
|
|
rect = gimp_warp_tool_get_invalidated_by_change (wt, area);
|
2019-03-03 14:10:40 -05:00
|
|
|
|
2024-02-09 17:48:53 +00:00
|
|
|
/* Move this operation below any non-destructive filters that
|
|
|
|
* may be active, so that it's directly affect the raw pixels. */
|
|
|
|
filters =
|
|
|
|
gimp_drawable_get_filters (gimp_drawable_filter_get_drawable (wt->filter));
|
|
|
|
|
|
|
|
if (gimp_container_have (filters, GIMP_OBJECT (wt->filter)))
|
|
|
|
{
|
|
|
|
gint end_index = gimp_container_get_n_children (filters) - 1;
|
|
|
|
gint index = gimp_container_get_child_index (filters,
|
|
|
|
GIMP_OBJECT (wt->filter));
|
|
|
|
|
|
|
|
if (end_index > 0 && index != end_index)
|
|
|
|
gimp_container_reorder (filters, GIMP_OBJECT (wt->filter),
|
|
|
|
end_index);
|
|
|
|
}
|
|
|
|
|
2019-03-03 16:18:50 -05:00
|
|
|
if (synchronous)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
GimpImage *image = gimp_display_get_image (tool->display);
|
|
|
|
|
|
|
|
g_signal_handlers_block_by_func (wt->filter,
|
|
|
|
gimp_warp_tool_filter_flush,
|
|
|
|
wt);
|
|
|
|
|
|
|
|
gimp_drawable_filter_apply (wt->filter, &rect);
|
|
|
|
|
|
|
|
gimp_projection_flush_now (gimp_image_get_projection (image), TRUE);
|
|
|
|
gimp_display_flush_now (tool->display);
|
|
|
|
|
|
|
|
g_signal_handlers_unblock_by_func (wt->filter,
|
|
|
|
gimp_warp_tool_filter_flush,
|
|
|
|
wt);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_drawable_filter_apply (wt->filter, &rect);
|
|
|
|
}
|
2019-03-03 14:10:40 -05:00
|
|
|
}
|
|
|
|
|
2017-05-19 18:14:45 -04:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_update_stroke (GimpWarpTool *wt,
|
|
|
|
GeglNode *node)
|
|
|
|
{
|
2018-12-30 05:10:28 -05:00
|
|
|
GeglRectangle bounds = {0, 0, 0, 0};
|
2013-05-22 23:47:31 +02:00
|
|
|
|
2017-05-19 18:14:45 -04:00
|
|
|
if (! wt->filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
/* update just this stroke */
|
2018-12-30 05:10:28 -05:00
|
|
|
bounds = gimp_warp_tool_get_stroke_bounds (node);
|
2017-05-19 18:14:45 -04:00
|
|
|
}
|
|
|
|
else if (wt->render_node)
|
|
|
|
{
|
2018-12-30 05:10:28 -05:00
|
|
|
node = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
2017-05-19 18:14:45 -04:00
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
bounds = gimp_warp_tool_get_node_bounds (node);
|
2017-05-19 18:14:45 -04:00
|
|
|
}
|
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
if (! gegl_rectangle_is_empty (&bounds))
|
2017-05-19 18:14:45 -04:00
|
|
|
{
|
2014-06-26 14:01:35 +02:00
|
|
|
#ifdef WARP_DEBUG
|
|
|
|
g_printerr ("update stroke: (%d,%d), %dx%d\n",
|
2018-12-30 05:10:28 -05:00
|
|
|
bounds.x, bounds.y,
|
|
|
|
bounds.width, bounds.height);
|
2014-06-26 14:01:35 +02:00
|
|
|
#endif
|
|
|
|
|
2019-03-03 16:18:50 -05:00
|
|
|
gimp_warp_tool_update_area (wt, &bounds, FALSE);
|
2013-05-22 23:47:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-30 20:06:12 +02:00
|
|
|
static void
|
2019-03-03 15:53:28 -05:00
|
|
|
gimp_warp_tool_stroke_append (GimpWarpTool *wt,
|
|
|
|
gchar type,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
2011-06-30 20:06:12 +02:00
|
|
|
{
|
2019-03-03 15:53:28 -05:00
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GeglRectangle area;
|
|
|
|
|
|
|
|
if (! wt->filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gegl_path_append (wt->current_stroke, type, x, y);
|
2017-05-17 10:52:49 -04:00
|
|
|
|
2019-03-03 15:53:28 -05:00
|
|
|
area.x = floor (x - options->effect_size * 0.5);
|
|
|
|
area.y = floor (y - options->effect_size * 0.5);
|
|
|
|
area.width = ceil (x + options->effect_size * 0.5) - area.x;
|
|
|
|
area.height = ceil (y + options->effect_size * 0.5) - area.y;
|
2011-08-01 19:28:32 +02:00
|
|
|
|
2013-05-22 01:04:29 +02:00
|
|
|
#ifdef WARP_DEBUG
|
|
|
|
g_printerr ("update rect: (%d,%d), %dx%d\n",
|
2019-03-03 15:53:28 -05:00
|
|
|
area.x, area.y,
|
|
|
|
area.width, area.height);
|
2013-05-22 01:04:29 +02:00
|
|
|
#endif
|
2011-07-26 16:59:57 +02:00
|
|
|
|
2018-12-30 05:10:28 -05:00
|
|
|
if (wt->render_node)
|
|
|
|
{
|
|
|
|
GeglNode *node = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
|
|
|
|
|
|
|
gimp_warp_tool_clear_node_bounds (node);
|
|
|
|
|
|
|
|
gimp_warp_tool_update_bounds (wt);
|
|
|
|
}
|
|
|
|
|
2019-03-03 16:18:50 -05:00
|
|
|
gimp_warp_tool_update_area (wt, &area, options->real_time_preview);
|
2011-06-30 20:06:12 +02:00
|
|
|
}
|
|
|
|
|
2011-05-25 01:54:38 +02:00
|
|
|
static void
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_warp_tool_filter_flush (GimpDrawableFilter *filter,
|
|
|
|
GimpTool *tool)
|
2011-05-25 01:54:38 +02:00
|
|
|
{
|
|
|
|
GimpImage *image = gimp_display_get_image (tool->display);
|
|
|
|
|
2013-05-21 23:46:33 +02:00
|
|
|
gimp_projection_flush (gimp_image_get_projection (image));
|
2011-05-25 01:54:38 +02:00
|
|
|
}
|
|
|
|
|
2011-06-08 14:15:21 +02:00
|
|
|
static void
|
2013-05-22 23:26:07 +02:00
|
|
|
gimp_warp_tool_add_op (GimpWarpTool *wt,
|
2014-11-08 00:47:39 +01:00
|
|
|
GeglNode *op)
|
2011-06-08 14:15:21 +02:00
|
|
|
{
|
2013-05-22 23:26:07 +02:00
|
|
|
GeglNode *last_op;
|
2011-06-08 14:15:21 +02:00
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_NODE (wt->render_node));
|
|
|
|
|
2014-11-08 00:47:39 +01:00
|
|
|
gegl_node_add_child (wt->graph, op);
|
2011-06-08 14:15:21 +02:00
|
|
|
|
|
|
|
last_op = gegl_node_get_producer (wt->render_node, "aux", NULL);
|
|
|
|
|
|
|
|
gegl_node_disconnect (wt->render_node, "aux");
|
2023-06-13 17:05:02 +02:00
|
|
|
gegl_node_link (last_op, op);
|
2023-06-13 19:58:42 +02:00
|
|
|
gegl_node_connect (op, "output",
|
|
|
|
wt->render_node, "aux");
|
2014-11-08 00:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_warp_tool_remove_op (GimpWarpTool *wt,
|
|
|
|
GeglNode *op)
|
|
|
|
{
|
|
|
|
GeglNode *previous;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_NODE (wt->render_node));
|
|
|
|
|
|
|
|
previous = gegl_node_get_producer (op, "input", NULL);
|
|
|
|
|
|
|
|
gegl_node_disconnect (op, "input");
|
2023-06-13 19:58:42 +02:00
|
|
|
gegl_node_connect (previous, "output",
|
|
|
|
wt->render_node, "aux");
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
gegl_node_remove_child (wt->graph, op);
|
|
|
|
}
|
|
|
|
|
2017-05-16 19:34:22 -04:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_free_op (GeglNode *op)
|
|
|
|
{
|
|
|
|
GeglNode *parent;
|
|
|
|
|
|
|
|
parent = gegl_node_get_parent (op);
|
|
|
|
|
2018-02-11 22:23:10 +01:00
|
|
|
gimp_assert (parent != NULL);
|
2017-05-16 19:34:22 -04:00
|
|
|
|
|
|
|
gegl_node_remove_child (parent, op);
|
|
|
|
}
|
|
|
|
|
2014-11-08 00:47:39 +01:00
|
|
|
static void
|
|
|
|
gimp_warp_tool_animate (GimpWarpTool *wt)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (wt);
|
|
|
|
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
|
|
|
GimpImage *orig_image;
|
|
|
|
GimpImage *image;
|
|
|
|
GimpLayer *layer;
|
|
|
|
GimpLayer *first_layer;
|
|
|
|
GeglNode *scale_node;
|
|
|
|
GimpProgress *progress;
|
|
|
|
GtkWidget *widget;
|
|
|
|
gint i;
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
g_return_if_fail (g_list_length (tool->drawables) == 1);
|
|
|
|
|
2017-07-04 18:48:03 +02:00
|
|
|
if (! gimp_warp_tool_can_undo (tool, tool->display))
|
2014-11-08 00:47:39 +01:00
|
|
|
{
|
|
|
|
gimp_tool_message_literal (tool, tool->display,
|
|
|
|
_("Please add some warp strokes first."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get rid of the image map so we can use wt->graph */
|
2016-05-12 01:49:53 +02:00
|
|
|
if (wt->filter)
|
2014-11-08 00:47:39 +01:00
|
|
|
{
|
2016-05-12 01:49:53 +02:00
|
|
|
gimp_drawable_filter_abort (wt->filter);
|
2017-07-15 18:38:01 +02:00
|
|
|
g_clear_object (&wt->filter);
|
2014-11-08 00:47:39 +01:00
|
|
|
}
|
|
|
|
|
2017-05-19 18:14:45 -04:00
|
|
|
gimp_warp_tool_set_sampler (wt, /* commit = */ TRUE);
|
2017-05-19 03:30:37 -04:00
|
|
|
|
2014-11-08 00:47:39 +01:00
|
|
|
gimp_progress_start (GIMP_PROGRESS (tool), FALSE,
|
|
|
|
_("Rendering Frame %d"), 1);
|
|
|
|
|
2020-05-25 11:30:31 +02:00
|
|
|
orig_image = gimp_item_get_image (GIMP_ITEM (tool->drawables->data));
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
image = gimp_create_image (orig_image->gimp,
|
2020-05-25 11:30:31 +02:00
|
|
|
gimp_item_get_width (GIMP_ITEM (tool->drawables->data)),
|
|
|
|
gimp_item_get_height (GIMP_ITEM (tool->drawables->data)),
|
|
|
|
gimp_drawable_get_base_type (tool->drawables->data),
|
|
|
|
gimp_drawable_get_precision (tool->drawables->data),
|
2014-11-08 00:47:39 +01:00
|
|
|
TRUE);
|
|
|
|
|
|
|
|
/* the first frame is always the unwarped image */
|
2020-05-25 11:30:31 +02:00
|
|
|
layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (tool->drawables->data), image,
|
2014-11-08 00:47:39 +01:00
|
|
|
GIMP_TYPE_LAYER));
|
|
|
|
gimp_object_take_name (GIMP_OBJECT (layer),
|
|
|
|
g_strdup_printf (_("Frame %d"), 1));
|
|
|
|
|
|
|
|
gimp_item_set_offset (GIMP_ITEM (layer), 0, 0);
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (layer), TRUE, FALSE);
|
2017-08-21 20:04:25 +02:00
|
|
|
gimp_layer_set_mode (layer, gimp_image_get_default_new_layer_mode (image),
|
|
|
|
FALSE);
|
2014-11-08 00:47:39 +01:00
|
|
|
gimp_layer_set_opacity (layer, GIMP_OPACITY_OPAQUE, FALSE);
|
|
|
|
gimp_image_add_layer (image, layer, NULL, 0, FALSE);
|
|
|
|
|
|
|
|
first_layer = layer;
|
|
|
|
|
|
|
|
scale_node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:scalar-multiply",
|
|
|
|
"n-components", 2,
|
|
|
|
NULL);
|
|
|
|
gimp_warp_tool_add_op (wt, scale_node);
|
|
|
|
|
|
|
|
progress = gimp_sub_progress_new (GIMP_PROGRESS (tool));
|
|
|
|
|
|
|
|
for (i = 1; i < options->n_animation_frames; i++)
|
|
|
|
{
|
|
|
|
gimp_progress_set_text (GIMP_PROGRESS (tool),
|
|
|
|
_("Rendering Frame %d"), i + 1);
|
|
|
|
|
|
|
|
gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (progress),
|
|
|
|
i, options->n_animation_frames);
|
|
|
|
|
|
|
|
layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (first_layer),
|
|
|
|
GIMP_TYPE_LAYER));
|
|
|
|
gimp_object_take_name (GIMP_OBJECT (layer),
|
|
|
|
g_strdup_printf (_("Frame %d"), i + 1));
|
|
|
|
|
|
|
|
gegl_node_set (scale_node,
|
|
|
|
"factor", (gdouble) i /
|
|
|
|
(gdouble) (options->n_animation_frames - 1),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (gimp_drawable_get_buffer (GIMP_DRAWABLE (first_layer)),
|
|
|
|
progress,
|
|
|
|
_("Frame"),
|
|
|
|
wt->graph,
|
|
|
|
gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
|
2018-03-22 13:31:00 -04:00
|
|
|
NULL, FALSE);
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
gimp_image_add_layer (image, layer, NULL, 0, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (progress);
|
|
|
|
|
|
|
|
gimp_warp_tool_remove_op (wt, scale_node);
|
|
|
|
|
|
|
|
gimp_progress_end (GIMP_PROGRESS (tool));
|
|
|
|
|
|
|
|
/* recreate the image map */
|
2020-05-25 11:30:31 +02:00
|
|
|
gimp_warp_tool_create_filter (wt, tool->drawables->data);
|
2017-05-19 18:14:45 -04:00
|
|
|
gimp_warp_tool_update_stroke (wt, NULL);
|
2014-11-08 00:47:39 +01:00
|
|
|
|
|
|
|
widget = GTK_WIDGET (gimp_display_get_shell (tool->display));
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
gimp_create_display (orig_image->gimp, image, gimp_unit_pixel (), 1.0,
|
2018-04-29 17:27:47 +02:00
|
|
|
G_OBJECT (gimp_widget_get_monitor (widget)));
|
2014-11-08 00:47:39 +01:00
|
|
|
g_object_unref (image);
|
2011-06-30 20:06:12 +02:00
|
|
|
}
|