app: add option to stroke the warp tool during cursor motion ...
... and to disable/control the rate of the periodic stroke. The warp tool is now fast enough to enable stroking directly in the motion handler, which gives better-quality response to motion than stroking periodically. It's not quite fast enough to enable exact motion, though :/ Allow individually enabling/disabling stroking during motion and periodically, and allow controlling the rate of the periodical stroke.
This commit is contained in:
@ -44,18 +44,21 @@ enum
|
||||
PROP_EFFECT_SIZE,
|
||||
PROP_EFFECT_HARDNESS,
|
||||
PROP_STROKE_SPACING,
|
||||
PROP_STROKE_DURING_MOTION,
|
||||
PROP_STROKE_PERIODICALLY,
|
||||
PROP_STROKE_PERIODICALLY_RATE,
|
||||
PROP_N_ANIMATION_FRAMES
|
||||
};
|
||||
|
||||
|
||||
static void gimp_warp_options_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_warp_options_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_warp_options_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_warp_options_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GimpWarpOptions, gimp_warp_options,
|
||||
@ -108,6 +111,27 @@ gimp_warp_options_class_init (GimpWarpOptionsClass *klass)
|
||||
1.0, 100.0, 20.0,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
|
||||
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_STROKE_DURING_MOTION,
|
||||
"stroke-during-motion",
|
||||
_("During motion"),
|
||||
_("Apply effect during motion"),
|
||||
TRUE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
|
||||
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_STROKE_PERIODICALLY,
|
||||
"stroke-periodically",
|
||||
_("Periodically"),
|
||||
_("Apply effect periodically"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
|
||||
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_STROKE_PERIODICALLY_RATE,
|
||||
"stroke-periodically-rate",
|
||||
_("Rate"),
|
||||
_("Periodical stroke rate"),
|
||||
0.0, 100.0, 50.0,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
|
||||
GIMP_CONFIG_PROP_INT (object_class, PROP_N_ANIMATION_FRAMES,
|
||||
"n-animation-frames",
|
||||
_("Frames"),
|
||||
@ -146,6 +170,15 @@ gimp_warp_options_set_property (GObject *object,
|
||||
case PROP_STROKE_SPACING:
|
||||
options->stroke_spacing = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_STROKE_DURING_MOTION:
|
||||
options->stroke_during_motion = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_STROKE_PERIODICALLY:
|
||||
options->stroke_periodically = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_STROKE_PERIODICALLY_RATE:
|
||||
options->stroke_periodically_rate = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_N_ANIMATION_FRAMES:
|
||||
options->n_animation_frames = g_value_get_int (value);
|
||||
break;
|
||||
@ -181,6 +214,15 @@ gimp_warp_options_get_property (GObject *object,
|
||||
case PROP_STROKE_SPACING:
|
||||
g_value_set_double (value, options->stroke_spacing);
|
||||
break;
|
||||
case PROP_STROKE_DURING_MOTION:
|
||||
g_value_set_boolean (value, options->stroke_during_motion);
|
||||
break;
|
||||
case PROP_STROKE_PERIODICALLY:
|
||||
g_value_set_boolean (value, options->stroke_periodically);
|
||||
break;
|
||||
case PROP_STROKE_PERIODICALLY_RATE:
|
||||
g_value_set_double (value, options->stroke_periodically_rate);
|
||||
break;
|
||||
case PROP_N_ANIMATION_FRAMES:
|
||||
g_value_set_int (value, options->n_animation_frames);
|
||||
break;
|
||||
@ -198,7 +240,8 @@ gimp_warp_options_gui (GimpToolOptions *tool_options)
|
||||
GObject *config = G_OBJECT (tool_options);
|
||||
GtkWidget *vbox = gimp_tool_options_gui (tool_options);
|
||||
GtkWidget *frame;
|
||||
GtkWidget *anim_vbox;
|
||||
GtkWidget *vbox2;
|
||||
GtkWidget *button;
|
||||
GtkWidget *combo;
|
||||
GtkWidget *scale;
|
||||
|
||||
@ -231,24 +274,46 @@ gimp_warp_options_gui (GimpToolOptions *tool_options)
|
||||
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
/* the stroke frame */
|
||||
frame = gimp_frame_new (_("Stroke"));
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
||||
gtk_container_add (GTK_CONTAINER (frame), vbox2);
|
||||
gtk_widget_show (vbox2);
|
||||
|
||||
button = gimp_prop_check_button_new (config, "stroke-during-motion", NULL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
|
||||
gtk_widget_show (button);
|
||||
|
||||
scale = gimp_prop_spin_scale_new (config, "stroke-periodically-rate", NULL,
|
||||
1, 10, 1);
|
||||
gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 0.0, 100.0);
|
||||
|
||||
frame = gimp_prop_expanding_frame_new (config, "stroke-periodically", NULL,
|
||||
scale, NULL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
/* the animation frame */
|
||||
frame = gimp_frame_new (_("Animate"));
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
anim_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
||||
gtk_container_add (GTK_CONTAINER (frame), anim_vbox);
|
||||
gtk_widget_show (anim_vbox);
|
||||
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
||||
gtk_container_add (GTK_CONTAINER (frame), vbox2);
|
||||
gtk_widget_show (vbox2);
|
||||
|
||||
scale = gimp_prop_spin_scale_new (config, "n-animation-frames", NULL,
|
||||
1.0, 10.0, 0);
|
||||
gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 3.0, 100.0);
|
||||
gtk_box_pack_start (GTK_BOX (anim_vbox), scale, FALSE, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
options->animate_button = gtk_button_new_with_label (_("Create Animation"));
|
||||
gtk_widget_set_sensitive (options->animate_button, FALSE);
|
||||
gtk_box_pack_start (GTK_BOX (anim_vbox), options->animate_button,
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), options->animate_button,
|
||||
FALSE, FALSE, 0);
|
||||
gtk_widget_show (options->animate_button);
|
||||
|
||||
|
@ -45,6 +45,10 @@ struct _GimpWarpOptions
|
||||
gdouble effect_hardness;
|
||||
gdouble stroke_spacing;
|
||||
|
||||
gboolean stroke_during_motion;
|
||||
gboolean stroke_periodically;
|
||||
gdouble stroke_periodically_rate;
|
||||
|
||||
gint n_animation_frames;
|
||||
|
||||
/* options gui */
|
||||
|
@ -52,9 +52,9 @@
|
||||
#include "gimp-intl.h"
|
||||
|
||||
|
||||
#define STROKE_PERIOD 100
|
||||
#define PREVIEW_SAMPLER GEGL_SAMPLER_NEAREST
|
||||
#define COMMIT_SAMPLER GEGL_SAMPLER_CUBIC
|
||||
#define STROKE_TIMER_MAX_FPS 20
|
||||
#define PREVIEW_SAMPLER GEGL_SAMPLER_NEAREST
|
||||
#define COMMIT_SAMPLER GEGL_SAMPLER_CUBIC
|
||||
|
||||
|
||||
static void gimp_warp_tool_control (GimpTool *tool,
|
||||
@ -103,11 +103,17 @@ static void gimp_warp_tool_options_notify (GimpTool *tool
|
||||
|
||||
static void gimp_warp_tool_draw (GimpDrawTool *draw_tool);
|
||||
|
||||
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);
|
||||
@ -243,6 +249,9 @@ gimp_warp_tool_button_press (GimpTool *tool,
|
||||
return;
|
||||
}
|
||||
|
||||
if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
|
||||
return;
|
||||
|
||||
wt->current_stroke = gegl_path_new ();
|
||||
|
||||
new_op = gegl_node_new_child (NULL,
|
||||
@ -267,11 +276,7 @@ gimp_warp_tool_button_press (GimpTool *tool,
|
||||
gegl_path_append (wt->current_stroke,
|
||||
'M', coords->x - off_x, coords->y - off_y);
|
||||
|
||||
wt->cursor_moved = FALSE;
|
||||
|
||||
wt->stroke_timer = g_timeout_add (STROKE_PERIOD,
|
||||
(GSourceFunc) gimp_warp_tool_stroke_timer,
|
||||
wt);
|
||||
gimp_warp_tool_start_stroke_timer (wt);
|
||||
|
||||
gimp_tool_control_activate (tool->control);
|
||||
}
|
||||
@ -290,8 +295,7 @@ gimp_warp_tool_button_release (GimpTool *tool,
|
||||
|
||||
gimp_tool_control_halt (tool->control);
|
||||
|
||||
g_source_remove (wt->stroke_timer);
|
||||
wt->stroke_timer = 0;
|
||||
gimp_warp_tool_stop_stroke_timer (wt);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (wt->current_stroke,
|
||||
gimp_warp_tool_stroke_changed,
|
||||
@ -339,13 +343,25 @@ gimp_warp_tool_motion (GimpTool *tool,
|
||||
GdkModifierType state,
|
||||
GimpDisplay *display)
|
||||
{
|
||||
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
||||
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
||||
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
||||
|
||||
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
||||
|
||||
wt->cursor_x = coords->x;
|
||||
wt->cursor_y = coords->y;
|
||||
wt->cursor_moved = TRUE;
|
||||
wt->cursor_x = coords->x;
|
||||
wt->cursor_y = coords->y;
|
||||
|
||||
if (options->stroke_during_motion)
|
||||
{
|
||||
gint off_x, off_y;
|
||||
|
||||
gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
|
||||
|
||||
gegl_path_append (wt->current_stroke,
|
||||
'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
|
||||
|
||||
gimp_warp_tool_start_stroke_timer (wt);
|
||||
}
|
||||
|
||||
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
||||
}
|
||||
@ -414,10 +430,15 @@ gimp_warp_tool_cursor_update (GimpTool *tool,
|
||||
GdkModifierType state,
|
||||
GimpDisplay *display)
|
||||
{
|
||||
GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
|
||||
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (tool);
|
||||
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_PLUS;
|
||||
|
||||
if (display == tool->display)
|
||||
if (! gimp_warp_tool_can_stroke (wt, display, FALSE))
|
||||
{
|
||||
modifier = GIMP_CURSOR_MODIFIER_BAD;
|
||||
}
|
||||
else if (display == tool->display)
|
||||
{
|
||||
/* FIXME have better cursors */
|
||||
|
||||
@ -434,18 +455,6 @@ gimp_warp_tool_cursor_update (GimpTool *tool,
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GimpImage *image = gimp_display_get_image (display);
|
||||
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
||||
|
||||
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) ||
|
||||
gimp_item_is_content_locked (GIMP_ITEM (drawable)) ||
|
||||
! gimp_item_is_visible (GIMP_ITEM (drawable)))
|
||||
{
|
||||
modifier = GIMP_CURSOR_MODIFIER_BAD;
|
||||
}
|
||||
}
|
||||
|
||||
gimp_tool_control_set_cursor_modifier (tool->control, modifier);
|
||||
|
||||
@ -570,6 +579,64 @@ gimp_warp_tool_draw (GimpDrawTool *draw_tool)
|
||||
0.0, 2.0 * G_PI);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_warp_tool_can_stroke (GimpWarpTool *wt,
|
||||
GimpDisplay *display,
|
||||
gboolean show_message)
|
||||
{
|
||||
GimpTool *tool = GIMP_TOOL (wt);
|
||||
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
||||
GimpImage *image = gimp_display_get_image (display);
|
||||
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
||||
|
||||
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
|
||||
{
|
||||
if (show_message)
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("Cannot warp layer groups."));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
|
||||
{
|
||||
if (show_message)
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("The active layer's pixels are locked."));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (! gimp_item_is_visible (GIMP_ITEM (drawable)))
|
||||
{
|
||||
if (show_message)
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("The active layer is not visible."));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (! options->stroke_during_motion &&
|
||||
! options->stroke_periodically)
|
||||
{
|
||||
if (show_message)
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("No stroke events selected."));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_warp_tool_start (GimpWarpTool *wt,
|
||||
GimpDisplay *display)
|
||||
@ -581,26 +648,8 @@ gimp_warp_tool_start (GimpWarpTool *wt,
|
||||
const Babl *format;
|
||||
GeglRectangle bbox;
|
||||
|
||||
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("Cannot warp layer groups."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("The active layer's pixels are locked."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (! gimp_item_is_visible (GIMP_ITEM (drawable)))
|
||||
{
|
||||
gimp_tool_message_literal (tool, display,
|
||||
_("The active layer is not visible."));
|
||||
return FALSE;
|
||||
}
|
||||
if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
|
||||
return FALSE;
|
||||
|
||||
tool->display = display;
|
||||
tool->drawable = drawable;
|
||||
@ -711,26 +760,47 @@ gimp_warp_tool_commit (GimpWarpTool *wt)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_warp_tool_start_stroke_timer (GimpWarpTool *wt)
|
||||
{
|
||||
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
||||
|
||||
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))
|
||||
{
|
||||
gdouble fps;
|
||||
|
||||
fps = STROKE_TIMER_MAX_FPS * options->stroke_periodically_rate / 100.0;
|
||||
|
||||
wt->stroke_timer = g_timeout_add (1000.0 / fps,
|
||||
(GSourceFunc) gimp_warp_tool_stroke_timer,
|
||||
wt);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
|
||||
gint off_x, off_y;
|
||||
GimpTool *tool = GIMP_TOOL (wt);
|
||||
gint off_x, off_y;
|
||||
|
||||
/* don't append the current point to the path if we're using the MOVE
|
||||
* behavior, and the cursor didn't move since last time; it's a nop, and
|
||||
* results in an unnecessary update.
|
||||
*/
|
||||
if (options->behavior != GIMP_WARP_BEHAVIOR_MOVE || wt->cursor_moved)
|
||||
{
|
||||
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,
|
||||
'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
|
||||
|
||||
wt->cursor_moved = FALSE;
|
||||
}
|
||||
gegl_path_append (wt->current_stroke,
|
||||
'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -826,10 +896,10 @@ gimp_warp_tool_update_stroke (GimpWarpTool *wt,
|
||||
gegl_path_get_bounds (stroke, &min_x, &max_x, &min_y, &max_y);
|
||||
g_object_unref (stroke);
|
||||
|
||||
bbox.x = min_x - size * 0.5;
|
||||
bbox.y = min_y - size * 0.5;
|
||||
bbox.width = max_x - min_x + size;
|
||||
bbox.height = max_y - min_y + size;
|
||||
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;
|
||||
|
||||
#ifdef WARP_DEBUG
|
||||
g_printerr ("update stroke: (%d,%d), %dx%d\n",
|
||||
|
@ -43,7 +43,6 @@ struct _GimpWarpTool
|
||||
|
||||
gdouble cursor_x; /* Hold the cursor x position */
|
||||
gdouble cursor_y; /* Hold the cursor y position */
|
||||
gboolean cursor_moved; /* Did the cursor move since the last stroke? */
|
||||
|
||||
GeglBuffer *coords_buffer; /* Buffer where coordinates are stored */
|
||||
|
||||
|
Reference in New Issue
Block a user