app: properly implement "spacing" option in the warp tool

The "spacing" option of the warp tool used to be handled by the
gegl:warp op, and have little effect.  Instead, implement it in the
warp tool directly, having the same effect as the other paint
tools.

Having a properly-working "spacing" option allows us to use EXACT
motion mode without cirppling down performance, which means that
the stroke now follows the pointer exactly, even when processing
takes a while.

Decrease the default "spacing" value to 10.
This commit is contained in:
Ell 2019-03-03 15:53:28 -05:00
parent aa91528084
commit e8a39d5c49
3 changed files with 98 additions and 49 deletions

View File

@ -111,7 +111,7 @@ gimp_warp_options_class_init (GimpWarpOptionsClass *klass)
"stroke-spacing", "stroke-spacing",
_("Spacing"), _("Spacing"),
_("Stroke Spacing"), _("Stroke Spacing"),
1.0, 100.0, 20.0, 1.0, 100.0, 10.0,
GIMP_PARAM_STATIC_STRINGS); GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_INTERPOLATION, GIMP_CONFIG_PROP_ENUM (object_class, PROP_INTERPOLATION,

View File

@ -131,9 +131,10 @@ static void gimp_warp_tool_update_area (GimpWarpTool *wt,
const GeglRectangle *area); const GeglRectangle *area);
static void gimp_warp_tool_update_stroke (GimpWarpTool *wt, static void gimp_warp_tool_update_stroke (GimpWarpTool *wt,
GeglNode *node); GeglNode *node);
static void gimp_warp_tool_stroke_changed (GeglPath *stroke, static void gimp_warp_tool_stroke_append (GimpWarpTool *wt,
const GeglRectangle *roi, gchar type,
GimpWarpTool *wt); gdouble x,
gdouble y);
static void gimp_warp_tool_filter_flush (GimpDrawableFilter *filter, static void gimp_warp_tool_filter_flush (GimpDrawableFilter *filter,
GimpTool *tool); GimpTool *tool);
static void gimp_warp_tool_add_op (GimpWarpTool *wt, static void gimp_warp_tool_add_op (GimpWarpTool *wt,
@ -194,6 +195,7 @@ gimp_warp_tool_init (GimpWarpTool *self)
{ {
GimpTool *tool = GIMP_TOOL (self); GimpTool *tool = GIMP_TOOL (self);
gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
gimp_tool_control_set_scroll_lock (tool->control, TRUE); gimp_tool_control_set_scroll_lock (tool->control, TRUE);
gimp_tool_control_set_preserve (tool->control, FALSE); gimp_tool_control_set_preserve (tool->control, FALSE);
gimp_tool_control_set_dirty_mask (tool->control, gimp_tool_control_set_dirty_mask (tool->control,
@ -266,27 +268,32 @@ gimp_warp_tool_button_press (GimpTool *tool,
wt->current_stroke = gegl_path_new (); wt->current_stroke = gegl_path_new ();
wt->last_pos.x = coords->x;
wt->last_pos.y = coords->y;
wt->total_dist = 0.0;
new_op = gegl_node_new_child (NULL, new_op = gegl_node_new_child (NULL,
"operation", "gegl:warp", "operation", "gegl:warp",
"behavior", options->behavior, "behavior", options->behavior,
"size", options->effect_size, "size", options->effect_size,
"hardness", options->effect_hardness / 100.0, "hardness", options->effect_hardness / 100.0,
"strength", options->effect_strength, "strength", options->effect_strength,
"spacing", options->stroke_spacing / 100.0, /* we implement spacing manually.
* anything > 1 will do.
*/
"spacing", 10.0,
"stroke", wt->current_stroke, "stroke", wt->current_stroke,
NULL); NULL);
gimp_warp_tool_add_op (wt, new_op); gimp_warp_tool_add_op (wt, new_op);
g_object_unref (new_op); g_object_unref (new_op);
g_signal_connect (wt->current_stroke, "changed",
G_CALLBACK (gimp_warp_tool_stroke_changed),
wt);
gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y); gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
gegl_path_append (wt->current_stroke, gimp_warp_tool_stroke_append (wt,
'M', coords->x - off_x, coords->y - off_y); 'M', wt->last_pos.x - off_x,
wt->last_pos.y - off_y);
gimp_warp_tool_start_stroke_timer (wt); gimp_warp_tool_start_stroke_timer (wt);
@ -309,10 +316,6 @@ gimp_warp_tool_button_release (GimpTool *tool,
gimp_warp_tool_stop_stroke_timer (wt); gimp_warp_tool_stop_stroke_timer (wt);
g_signal_handlers_disconnect_by_func (wt->current_stroke,
gimp_warp_tool_stroke_changed,
wt);
#ifdef WARP_DEBUG #ifdef WARP_DEBUG
g_printerr ("%s\n", gegl_path_to_string (wt->current_stroke)); g_printerr ("%s\n", gegl_path_to_string (wt->current_stroke));
#endif #endif
@ -354,27 +357,64 @@ gimp_warp_tool_motion (GimpTool *tool,
GdkModifierType state, GdkModifierType state,
GimpDisplay *display) GimpDisplay *display)
{ {
GimpWarpTool *wt = GIMP_WARP_TOOL (tool); GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt); GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
GimpVector2 old_cursor_pos;
GimpVector2 delta;
gdouble dist;
gdouble step;
gboolean stroke_changed = FALSE;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); old_cursor_pos = wt->cursor_pos;
wt->cursor_x = coords->x; wt->cursor_pos.x = coords->x;
wt->cursor_y = coords->y; wt->cursor_pos.y = coords->y;
if (options->stroke_during_motion) gimp_vector2_sub (&delta, &wt->cursor_pos, &old_cursor_pos);
dist = gimp_vector2_length (&delta);
step = options->effect_size * options->stroke_spacing / 100.0;
while (wt->total_dist + dist >= step)
{ {
gint off_x, off_y; gdouble diff = step - wt->total_dist;
gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y); gimp_vector2_mul (&delta, diff / dist);
gimp_vector2_add (&old_cursor_pos, &old_cursor_pos, &delta);
gegl_path_append (wt->current_stroke, gimp_vector2_sub (&delta, &wt->cursor_pos, &old_cursor_pos);
'L', wt->cursor_x - off_x, wt->cursor_y - off_y); dist -= diff;
gimp_warp_tool_start_stroke_timer (wt); 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));
}
gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
gimp_warp_tool_stroke_append (wt,
'L', wt->last_pos.x - off_x,
wt->last_pos.y - off_y);
}
} }
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); wt->total_dist += dist;
if (stroke_changed)
{
gimp_warp_tool_start_stroke_timer (wt);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
} }
static gboolean static gboolean
@ -420,8 +460,10 @@ gimp_warp_tool_oper_update (GimpTool *tool,
if (! tool->display || display == tool->display) if (! tool->display || display == tool->display)
{ {
wt->cursor_x = coords->x; wt->cursor_pos.x = coords->x;
wt->cursor_y = coords->y; wt->cursor_pos.y = coords->y;
wt->last_pos = wt->cursor_pos;
} }
if (! gimp_draw_tool_is_active (draw_tool)) if (! gimp_draw_tool_is_active (draw_tool))
@ -607,8 +649,8 @@ gimp_warp_tool_draw (GimpDrawTool *draw_tool)
gimp_draw_tool_add_arc (draw_tool, gimp_draw_tool_add_arc (draw_tool,
FALSE, FALSE,
wt->cursor_x - options->effect_size * 0.5, wt->last_pos.x - options->effect_size * 0.5,
wt->cursor_y - options->effect_size * 0.5, wt->last_pos.y - options->effect_size * 0.5,
options->effect_size, options->effect_size,
options->effect_size, options->effect_size,
0.0, 2.0 * G_PI); 0.0, 2.0 * G_PI);
@ -859,8 +901,9 @@ gimp_warp_tool_stroke_timer (GimpWarpTool *wt)
gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y); gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
gegl_path_append (wt->current_stroke, gimp_warp_tool_stroke_append (wt,
'L', wt->cursor_x - off_x, wt->cursor_y - off_y); 'L', wt->last_pos.x - off_x,
wt->last_pos.y - off_y);
return TRUE; return TRUE;
} }
@ -1099,24 +1142,28 @@ gimp_warp_tool_update_stroke (GimpWarpTool *wt,
} }
static void static void
gimp_warp_tool_stroke_changed (GeglPath *path, gimp_warp_tool_stroke_append (GimpWarpTool *wt,
const GeglRectangle *roi, gchar type,
GimpWarpTool *wt) gdouble x,
gdouble y)
{ {
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt); GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
GeglRectangle update_region; GeglRectangle area;
update_region.x = floor (roi->x - options->effect_size * 0.5); if (! wt->filter)
update_region.y = floor (roi->y - options->effect_size * 0.5); return;
update_region.width = ceil (roi->x + roi->width +
options->effect_size * 0.5) - update_region.x; gegl_path_append (wt->current_stroke, type, x, y);
update_region.height = ceil (roi->y + roi->height +
options->effect_size * 0.5) - update_region.y; 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;
#ifdef WARP_DEBUG #ifdef WARP_DEBUG
g_printerr ("update rect: (%d,%d), %dx%d\n", g_printerr ("update rect: (%d,%d), %dx%d\n",
update_region.x, update_region.y, area.x, area.y,
update_region.width, update_region.height); area.width, area.height);
#endif #endif
if (wt->render_node) if (wt->render_node)
@ -1128,7 +1175,7 @@ gimp_warp_tool_stroke_changed (GeglPath *path,
gimp_warp_tool_update_bounds (wt); gimp_warp_tool_update_bounds (wt);
} }
gimp_warp_tool_update_area (wt, &update_region); gimp_warp_tool_update_area (wt, &area);
} }
static void static void

View File

@ -41,8 +41,7 @@ struct _GimpWarpTool
{ {
GimpDrawTool parent_instance; GimpDrawTool parent_instance;
gdouble cursor_x; /* Hold the cursor x position */ GimpVector2 cursor_pos; /* Hold the cursor position */
gdouble cursor_y; /* Hold the cursor y position */
GeglBuffer *coords_buffer; /* Buffer where coordinates are stored */ GeglBuffer *coords_buffer; /* Buffer where coordinates are stored */
@ -52,6 +51,9 @@ struct _GimpWarpTool
GeglPath *current_stroke; GeglPath *current_stroke;
guint stroke_timer; guint stroke_timer;
GimpVector2 last_pos;
gdouble total_dist;
GimpDrawableFilter *filter; GimpDrawableFilter *filter;
GList *redo_stack; GList *redo_stack;