app: add "Composited preview" option to transform tools

Add a "Composited preview" option to all transform-grid tools,
which displays the transform preview as part of the image
composition (i.e., as it would actually look when applying the
transformation, keeping the layer at the right position in the
stack, and with the right layer mode), instead of using an overlay.

This option is off by default, since it's generally slower to
render than an overlay, due to the lack of mipmap rendering.  We're
also still using an overlay when transfoming a selection, and not a
whole layer.
This commit is contained in:
Ell 2020-01-16 00:17:56 +02:00
parent 2e26603a55
commit a335827896
4 changed files with 452 additions and 66 deletions

View File

@ -50,6 +50,7 @@ enum
PROP_DIRECTION,
PROP_DIRECTION_LINKED,
PROP_SHOW_PREVIEW,
PROP_COMPOSITED_PREVIEW,
PROP_PREVIEW_OPACITY,
PROP_GRID_TYPE,
PROP_GRID_SIZE,
@ -111,6 +112,13 @@ gimp_transform_grid_options_class_init (GimpTransformGridOptionsClass *klass)
TRUE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COMPOSITED_PREVIEW,
"composited-preview",
_("Composited preview"),
_("Show preview as part of the image composition"),
FALSE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PREVIEW_OPACITY,
"preview-opacity",
_("Image opacity"),
@ -228,6 +236,9 @@ gimp_transform_grid_options_set_property (GObject *object,
case PROP_SHOW_PREVIEW:
options->show_preview = g_value_get_boolean (value);
break;
case PROP_COMPOSITED_PREVIEW:
options->composited_preview = g_value_get_boolean (value);
break;
case PROP_PREVIEW_OPACITY:
options->preview_opacity = g_value_get_double (value);
break;
@ -293,6 +304,9 @@ gimp_transform_grid_options_get_property (GObject *object,
case PROP_SHOW_PREVIEW:
g_value_set_boolean (value, options->show_preview);
break;
case PROP_COMPOSITED_PREVIEW:
g_value_set_boolean (value, options->composited_preview);
break;
case PROP_PREVIEW_OPACITY:
g_value_set_double (value, options->preview_opacity);
break;
@ -352,6 +366,8 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
GObject *config = G_OBJECT (tool_options);
GimpTransformGridToolClass *tg_class;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *button;
GtkWidget *frame;
GtkWidget *combo;
GtkWidget *scale;
@ -367,9 +383,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
if (tg_class->matrix_to_info)
{
GimpTransformOptions *tr_options = GIMP_TRANSFORM_OPTIONS (tool_options);
GtkWidget *vbox2;
GtkWidget *hbox;
GtkWidget *button;
vbox2 = gtk_bin_get_child (GTK_BIN (tr_options->direction_frame));
g_object_ref (vbox2);
@ -397,12 +411,25 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
g_type_class_unref (tg_class);
/* the preview frame */
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
button = gimp_prop_check_button_new (config, "composited-preview", NULL);
gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
gtk_widget_show (button);
scale = gimp_prop_spin_scale_new (config, "preview-opacity", NULL,
0.01, 0.1, 0);
gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_object_bind_property (config, "composited-preview",
scale, "sensitive",
G_BINDING_SYNC_CREATE |
G_BINDING_INVERT_BOOLEAN);
frame = gimp_prop_expanding_frame_new (config, "show-preview", NULL,
scale, NULL);
vbox2, NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
/* the guides frame */
@ -431,8 +458,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
if (tool_options->tool_info->tool_type == GIMP_TYPE_ROTATE_TOOL)
{
GtkWidget *button;
gchar *label;
gchar *label;
label = g_strdup_printf (_("15 degrees (%s)"),
gimp_get_mod_string (extend_mask));
@ -447,8 +473,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
}
else if (tool_options->tool_info->tool_type == GIMP_TYPE_SCALE_TOOL)
{
GtkWidget *button;
gchar *label;
gchar *label;
label = g_strdup_printf (_("Keep aspect (%s)"),
gimp_get_mod_string (extend_mask));
@ -474,8 +499,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
}
else if (tool_options->tool_info->tool_type == GIMP_TYPE_PERSPECTIVE_TOOL)
{
GtkWidget *button;
gchar *label;
gchar *label;
label = g_strdup_printf (_("Constrain handles (%s)"),
gimp_get_mod_string (extend_mask));
@ -541,9 +565,8 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
N_("Lock pivot position to canvas") },
};
GtkWidget *button;
gchar *label;
gint i;
gchar *label;
gint i;
frame = NULL;

View File

@ -39,6 +39,7 @@ struct _GimpTransformGridOptions
gboolean direction_linked;
gboolean show_preview;
gboolean composited_preview;
gdouble preview_opacity;
GimpGuidesType grid_type;
gint grid_size;

View File

@ -26,9 +26,19 @@
#include "tools-types.h"
#include "gegl/gimpapplicator.h"
#include "gegl/gimp-gegl-nodes.h"
#include "gegl/gimp-gegl-utils.h"
#include "core/gimp.h"
#include "core/gimp-transform-resize.h"
#include "core/gimp-transform-utils.h"
#include "core/gimpboundary.h"
#include "core/gimpcontainer.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimperror.h"
#include "core/gimpfilter.h"
#include "core/gimpgrouplayer.h"
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
@ -68,6 +78,22 @@
#define UNDO_COMPRESS_TIME (0.5 * G_TIME_SPAN_SECOND)
typedef struct
{
GimpTransformGridTool *tg_tool;
GimpDrawable *drawable;
GimpDrawableFilter *filter;
GimpDrawable *clip_drawable;
GeglNode *transform_node;
GeglNode *crop_node;
GimpMatrix3 transform;
GeglRectangle bounds;
} Filter;
typedef struct
{
gint64 time;
@ -155,6 +181,9 @@ static void gimp_transform_grid_tool_widget_response (GimpToolWidget
gint response_id,
GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_filter_flush (GimpDrawableFilter *filter,
GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_commit (GimpTransformGridTool *tg_tool);
@ -169,12 +198,27 @@ static void gimp_transform_grid_tool_response (GimpToolGui
gint response_id,
GimpTransformGridTool *tg_tool);
static gboolean gimp_transform_grid_tool_composited_preview (GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
GimpObject *object);
static void gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_add_filter (GimpDrawable *drawable,
GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_remove_filter (GimpDrawable *drawable,
GimpTransformGridTool *tg_tool);
static void gimp_transform_grid_tool_effective_mode_changed
(GimpLayer *layer,
GimpTransformGridTool *tg_tool);
static Filter * filter_new (GimpTransformGridTool *tg_tool,
GimpDrawable *drawable,
gboolean add_filter);
static void filter_free (Filter *filter);
static UndoInfo * undo_info_new (void);
static void undo_info_free (UndoInfo *info);
@ -242,9 +286,10 @@ gimp_transform_grid_tool_init (GimpTransformGridTool *tg_tool)
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
gimp_tool_control_set_preserve (tool->control, FALSE);
gimp_tool_control_set_dirty_mask (tool->control,
GIMP_DIRTY_IMAGE_SIZE |
GIMP_DIRTY_DRAWABLE |
GIMP_DIRTY_SELECTION |
GIMP_DIRTY_IMAGE_SIZE |
GIMP_DIRTY_IMAGE_STRUCTURE |
GIMP_DIRTY_DRAWABLE |
GIMP_DIRTY_SELECTION |
GIMP_DIRTY_ACTIVE_DRAWABLE);
gimp_tool_control_set_active_modifiers (tool->control,
GIMP_TOOL_ACTIVE_MODIFIERS_SAME);
@ -618,32 +663,35 @@ gimp_transform_grid_tool_options_notify (GimpTool *tool,
/* recalculate the tool's transformation matrix */
gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
}
else if (! strcmp (pspec->name, "show-preview"))
else if (! strcmp (pspec->name, "show-preview") ||
! strcmp (pspec->name, "composited-preview"))
{
if (tg_tool->preview)
{
GimpDisplay *display;
GimpObject *object;
gboolean show_preview;
show_preview = gimp_transform_grid_options_show_preview (tg_options) &&
tr_tool->transform_valid;
gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
display = tool->display;
object = gimp_transform_tool_get_active_object (tr_tool, display);
if (object)
{
if (gimp_transform_grid_options_show_preview (tg_options))
gimp_transform_grid_tool_hide_active_object (tg_tool, object);
if (tg_options->show_preview &&
! gimp_transform_grid_tool_composited_preview (tg_tool))
{
gimp_transform_grid_tool_hide_active_object (tg_tool, object);
}
else
gimp_transform_grid_tool_show_active_object (tg_tool);
{
gimp_transform_grid_tool_show_active_object (tg_tool);
}
}
gimp_transform_grid_tool_update_preview (tg_tool);
}
}
else if (! strcmp (pspec->name, "clip") ||
else if (! strcmp (pspec->name, "interpolation") ||
! strcmp (pspec->name, "clip") ||
! strcmp (pspec->name, "preview-opacity"))
{
gimp_transform_grid_tool_update_preview (tg_tool);
@ -673,10 +721,10 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
gimp_matrix3_invert (&matrix);
if (tg_tool->widget)
if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER ||
tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
{
GimpPickable *pickable;
gboolean show_preview;
if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
{
@ -690,9 +738,6 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
pickable = GIMP_PICKABLE (tool->drawable);
}
show_preview = gimp_transform_grid_options_show_preview (options) &&
tr_tool->transform_valid;
tg_tool->preview =
gimp_draw_tool_add_transform_preview (draw_tool,
pickable,
@ -703,10 +748,6 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
tr_tool->y2);
g_object_add_weak_pointer (G_OBJECT (tg_tool->preview),
(gpointer) &tg_tool->preview);
gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
}
if (tr_options->type == GIMP_TRANSFORM_TYPE_SELECTION)
@ -786,6 +827,8 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
}
}
GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
gimp_transform_grid_tool_update_preview (tg_tool);
}
@ -1058,6 +1101,16 @@ gimp_transform_grid_tool_widget_response (GimpToolWidget *widget,
}
}
static void
gimp_transform_grid_tool_filter_flush (GimpDrawableFilter *filter,
GimpTransformGridTool *tg_tool)
{
GimpTool *tool = GIMP_TOOL (tg_tool);
GimpImage *image = gimp_display_get_image (tool->display);
gimp_projection_flush (gimp_image_get_projection (image));
}
static void
gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
{
@ -1070,6 +1123,8 @@ gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), NULL);
g_clear_object (&tg_tool->widget);
g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
if (tg_tool->gui)
gimp_tool_gui_hide (tg_tool->gui);
@ -1405,6 +1460,19 @@ gimp_transform_grid_tool_response (GimpToolGui *gui,
}
}
static gboolean
gimp_transform_grid_tool_composited_preview (GimpTransformGridTool *tg_tool)
{
GimpTool *tool = GIMP_TOOL (tg_tool);
GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
GimpImage *image = gimp_display_get_image (tool->display);
return tg_options->composited_preview &&
tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
gimp_channel_is_empty (gimp_image_get_mask (image));
}
static void
gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
{
@ -1432,26 +1500,127 @@ gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
static void
gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool)
{
GimpTool *tool = GIMP_TOOL (tg_tool);
GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
gint i;
if (tg_options->show_preview &&
gimp_transform_grid_tool_composited_preview (tg_tool) &&
tr_tool->transform_valid)
{
GHashTableIter iter;
GimpDrawable *drawable;
Filter *filter;
if (! tg_tool->filters)
{
tg_tool->filters = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) filter_free);
gimp_transform_grid_tool_add_filter (tool->drawable, tg_tool);
}
g_hash_table_iter_init (&iter, tg_tool->filters);
while (g_hash_table_iter_next (&iter,
(gpointer *) &drawable,
(gpointer *) &filter))
{
GimpMatrix3 transform;
GeglRectangle bounds;
gint offset_x;
gint offset_y;
gint x1, y1;
gint x2, y2;
gboolean update = FALSE;
if (! filter->filter)
continue;
gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
gimp_matrix3_identity (&transform);
gimp_matrix3_translate (&transform, +offset_x, +offset_y);
gimp_matrix3_mult (&tr_tool->transform, &transform);
gimp_matrix3_translate (&transform, -offset_x, -offset_y);
gimp_transform_resize_boundary (&tr_tool->transform,
gimp_item_get_clip (
GIMP_ITEM (filter->clip_drawable),
tr_options->clip),
tr_tool->x1, tr_tool->y1,
tr_tool->x2, tr_tool->y2,
&x1, &y1,
&x2, &y2);
bounds.x = x1 - offset_x;
bounds.y = y1 - offset_y;
bounds.width = x2 - x1;
bounds.height = y2 - y1;
if (! gimp_matrix3_equal (&transform, &filter->transform))
{
filter->transform = transform;
gimp_gegl_node_set_matrix (filter->transform_node, &transform);
update = TRUE;
}
if (! gegl_rectangle_equal (&bounds, &filter->bounds))
{
filter->bounds = bounds;
gegl_node_set (filter->crop_node,
"x", (gdouble) bounds.x,
"y", (gdouble) bounds.y,
"width", (gdouble) bounds.width,
"height", (gdouble) bounds.height,
NULL);
update = TRUE;
}
if (GIMP_IS_LAYER (drawable))
{
gimp_drawable_filter_set_add_alpha (
filter->filter,
tr_options->interpolation != GIMP_INTERPOLATION_NONE);
}
if (update)
gimp_drawable_filter_apply (filter->filter, NULL);
}
}
else
{
g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
}
if (tg_tool->preview)
{
gboolean show_preview;
show_preview = gimp_transform_grid_options_show_preview (tg_options) &&
tr_tool->transform_valid;
gimp_canvas_item_begin_change (tg_tool->preview);
gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
g_object_set (tg_tool->preview,
"transform", &tr_tool->transform,
"clip", tr_options->clip,
"opacity", tg_options->preview_opacity,
NULL);
gimp_canvas_item_end_change (tg_tool->preview);
if (tg_options->show_preview &&
! gimp_transform_grid_tool_composited_preview (tg_tool) &&
tr_tool->transform_valid)
{
gimp_canvas_item_begin_change (tg_tool->preview);
gimp_canvas_item_set_visible (tg_tool->preview, TRUE);
g_object_set (
tg_tool->preview,
"transform", &tr_tool->transform,
"clip", gimp_item_get_clip (GIMP_ITEM (tool->drawable),
tr_options->clip),
"opacity", tg_options->preview_opacity,
NULL);
gimp_canvas_item_end_change (tg_tool->preview);
}
else
{
gimp_canvas_item_set_visible (tg_tool->preview, FALSE);
}
}
if (tg_tool->boundary_in)
@ -1502,6 +1671,7 @@ gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
{
/* hide only complete layers and channels, not layer masks */
if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
! options->composited_preview &&
GIMP_IS_DRAWABLE (object) &&
! GIMP_IS_LAYER_MASK (object) &&
gimp_item_get_visible (GIMP_ITEM (object)) &&
@ -1550,6 +1720,197 @@ gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool)
}
}
static void
gimp_transform_grid_tool_add_filter (GimpDrawable *drawable,
GimpTransformGridTool *tg_tool)
{
Filter *filter;
GimpLayerMode mode = GIMP_LAYER_MODE_NORMAL;
if (GIMP_IS_LAYER (drawable))
{
gimp_layer_get_effective_mode (GIMP_LAYER (drawable),
&mode, NULL, NULL, NULL);
}
if (mode != GIMP_LAYER_MODE_PASS_THROUGH)
{
filter = filter_new (tg_tool, drawable, TRUE);
}
else
{
GimpContainer *container;
filter = filter_new (tg_tool, drawable, FALSE);
container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
gimp_container_foreach (container,
(GFunc) gimp_transform_grid_tool_add_filter,
tg_tool);
}
filter->clip_drawable = drawable;
g_hash_table_insert (tg_tool->filters, drawable, filter);
if (GIMP_IS_LAYER (drawable))
{
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
if (mask)
{
Filter *mask_filter;
gimp_transform_grid_tool_add_filter (GIMP_DRAWABLE (mask), tg_tool);
mask_filter = g_hash_table_lookup (tg_tool->filters, mask);
mask_filter->clip_drawable = drawable;
}
}
}
static void
gimp_transform_grid_tool_remove_filter (GimpDrawable *drawable,
GimpTransformGridTool *tg_tool)
{
Filter *filter = g_hash_table_lookup (tg_tool->filters, drawable);
if (GIMP_IS_LAYER (drawable))
{
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
if (mask)
gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (mask), tg_tool);
}
if (! filter->filter)
{
GimpContainer *container;
container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
gimp_container_foreach (container,
(GFunc) gimp_transform_grid_tool_remove_filter,
tg_tool);
}
g_hash_table_remove (tg_tool->filters, drawable);
}
static void
gimp_transform_grid_tool_effective_mode_changed (GimpLayer *layer,
GimpTransformGridTool *tg_tool)
{
Filter *filter = g_hash_table_lookup (tg_tool->filters, layer);
GimpLayerMode mode;
gboolean old_pass_through;
gboolean new_pass_through;
gimp_layer_get_effective_mode (layer, &mode, NULL, NULL, NULL);
old_pass_through = ! filter->filter;
new_pass_through = mode == GIMP_LAYER_MODE_PASS_THROUGH;
if (old_pass_through != new_pass_through)
{
gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (layer), tg_tool);
gimp_transform_grid_tool_add_filter (GIMP_DRAWABLE (layer), tg_tool);
gimp_transform_grid_tool_update_preview (tg_tool);
}
}
static Filter *
filter_new (GimpTransformGridTool *tg_tool,
GimpDrawable *drawable,
gboolean add_filter)
{
Filter *filter = g_slice_new0 (Filter);
GeglNode *node;
GeglNode *input_node;
GeglNode *output_node;
filter->tg_tool = tg_tool;
filter->drawable = drawable;
if (add_filter)
{
node = gegl_node_new ();
input_node = gegl_node_get_input_proxy (node, "input");
output_node = gegl_node_get_input_proxy (node, "output");
filter->transform_node = gegl_node_new_child (
node,
"operation", "gegl:transform",
"near-z", GIMP_TRANSFORM_NEAR_Z,
"sampler", GEGL_SAMPLER_NEAREST,
NULL);
filter->crop_node = gegl_node_new_child (
node,
"operation", "gegl:crop",
NULL);
gegl_node_link_many (input_node,
filter->transform_node,
filter->crop_node,
output_node,
NULL);
gimp_gegl_node_set_underlying_operation (node, filter->transform_node);
filter->filter = gimp_drawable_filter_new (
drawable,
GIMP_TRANSFORM_TOOL_GET_CLASS (tg_tool)->undo_desc,
node,
gimp_tool_get_icon_name (GIMP_TOOL (tg_tool)));
gimp_drawable_filter_set_clip (filter->filter, FALSE);
gimp_drawable_filter_set_override_constraints (filter->filter, TRUE);
g_signal_connect (
filter->filter, "flush",
G_CALLBACK (gimp_transform_grid_tool_filter_flush),
tg_tool);
g_object_unref (node);
}
if (GIMP_IS_GROUP_LAYER (drawable))
{
g_signal_connect (
drawable, "effective-mode-changed",
G_CALLBACK (gimp_transform_grid_tool_effective_mode_changed),
tg_tool);
}
return filter;
}
static void
filter_free (Filter *filter)
{
if (filter->filter)
{
gimp_drawable_filter_abort (filter->filter);
g_object_unref (filter->filter);
}
if (GIMP_IS_GROUP_LAYER (filter->drawable))
{
g_signal_handlers_disconnect_by_func (
filter->drawable,
gimp_transform_grid_tool_effective_mode_changed,
filter->tg_tool);
}
g_slice_free (Filter, filter);
}
static UndoInfo *
undo_info_new (void)
{

View File

@ -45,28 +45,29 @@ typedef struct _GimpTransformGridToolClass GimpTransformGridToolClass;
struct _GimpTransformGridTool
{
GimpTransformTool parent_instance;
GimpTransformTool parent_instance;
TransInfo init_trans_info; /* initial transformation info */
TransInfo trans_infos[2]; /* forward/backward transformation info */
gdouble *trans_info; /* current transformation info */
GList *undo_list; /* list of all states,
head is current == prev_trans_info,
tail is original == old_trans_info */
GList *redo_list; /* list of all undone states,
NULL when nothing undone */
TransInfo init_trans_info; /* initial transformation info */
TransInfo trans_infos[2]; /* forward/backward transformation info */
gdouble *trans_info; /* current transformation info */
GList *undo_list; /* list of all states,
head is current == prev_trans_info,
tail is original == old_trans_info */
GList *redo_list; /* list of all undone states,
NULL when nothing undone */
GimpObject *hidden_object; /* the object that was hidden during
the transform */
GimpObject *hidden_object; /* the object that was hidden during
the transform */
GimpToolWidget *widget;
GimpToolWidget *grab_widget;
GimpCanvasItem *preview;
GimpCanvasItem *boundary_in;
GimpCanvasItem *boundary_out;
GPtrArray *strokes;
GimpToolWidget *widget;
GimpToolWidget *grab_widget;
GHashTable *filters;
GimpCanvasItem *preview;
GimpCanvasItem *boundary_in;
GimpCanvasItem *boundary_out;
GPtrArray *strokes;
GimpToolGui *gui;
GimpToolGui *gui;
};
struct _GimpTransformGridToolClass