From 2989bad35a8ac3c6f752bd89b009f41adeecb3f6 Mon Sep 17 00:00:00 2001 From: Johannes Matschke Date: Thu, 5 Mar 2015 12:29:19 +0100 Subject: [PATCH] Bug 721009 - new transform tool with freely placeable handles Add new tool GimpHandleTransformTool which allows to freely place up to 4 handles on the image, then move any one of them, which transforms the image so that the remaining handles keep their position. Did quite some cleanup on the code before pushing --Mitch --- app/dialogs/dialogs.c | 1 + app/tools/Makefile.am | 4 + app/tools/gimp-tools.c | 2 + app/tools/gimphandletransformoptions.c | 201 +++++ app/tools/gimphandletransformoptions.h | 54 ++ app/tools/gimphandletransformtool.c | 985 ++++++++++++++++++++++++ app/tools/gimphandletransformtool.h | 60 ++ app/tools/gimptransformtool.c | 9 +- app/tools/gimptransformtool.h | 2 +- app/tools/tools-enums.c | 31 + app/tools/tools-enums.h | 12 + app/widgets/gimphelp-ids.h | 1 + icons/16/gimp-tool-handle-transform.png | Bin 0 -> 1166 bytes icons/22/gimp-tool-handle-transform.png | Bin 0 -> 2084 bytes icons/22/gimp-tool-handle-transform.xcf | 0 icons/Makefile.am | 2 + libgimpwidgets/gimpstock.c | 1 + libgimpwidgets/gimpstock.h | 1 + menus/image-menu.xml.in | 1 + po/POTFILES.in | 2 + 20 files changed, 1366 insertions(+), 3 deletions(-) create mode 100644 app/tools/gimphandletransformoptions.c create mode 100644 app/tools/gimphandletransformoptions.h create mode 100644 app/tools/gimphandletransformtool.c create mode 100644 app/tools/gimphandletransformtool.h create mode 100644 icons/16/gimp-tool-handle-transform.png create mode 100644 icons/22/gimp-tool-handle-transform.png create mode 100644 icons/22/gimp-tool-handle-transform.xcf diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c index 1624a8d84c..17eae74e54 100644 --- a/app/dialogs/dialogs.c +++ b/app/dialogs/dialogs.c @@ -244,6 +244,7 @@ static const GimpDialogFactoryEntry entries[] = FOREIGN ("gimp-threshold-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-perspective-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-unified-transform-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-handle-transform-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-toolbox-color-dialog", TRUE, FALSE), FOREIGN ("gimp-gradient-editor-color-dialog", TRUE, FALSE), diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am index dcdacdeff3..08186ac43a 100644 --- a/app/tools/Makefile.am +++ b/app/tools/Makefile.am @@ -96,6 +96,10 @@ libapptools_a_sources = \ gimpfuzzyselecttool.h \ gimpgegltool.c \ gimpgegltool.h \ + gimphandletransformoptions.c \ + gimphandletransformoptions.h \ + gimphandletransformtool.c \ + gimphandletransformtool.h \ gimphealtool.c \ gimphealtool.h \ gimphistogramoptions.c \ diff --git a/app/tools/gimp-tools.c b/app/tools/gimp-tools.c index 0ed25aee53..54b44fc305 100644 --- a/app/tools/gimp-tools.c +++ b/app/tools/gimp-tools.c @@ -61,6 +61,7 @@ #include "gimpforegroundselecttool.h" #include "gimpfuzzyselecttool.h" #include "gimpgegltool.h" +#include "gimphandletransformtool.h" #include "gimphealtool.h" #include "gimphuesaturationtool.h" #include "gimpinktool.h" @@ -160,6 +161,7 @@ gimp_tools_init (Gimp *gimp) gimp_cage_tool_register, gimp_flip_tool_register, gimp_perspective_tool_register, + gimp_handle_transform_tool_register, gimp_shear_tool_register, gimp_scale_tool_register, gimp_rotate_tool_register, diff --git a/app/tools/gimphandletransformoptions.c b/app/tools/gimphandletransformoptions.c new file mode 100644 index 0000000000..78ce7c6e23 --- /dev/null +++ b/app/tools/gimphandletransformoptions.c @@ -0,0 +1,201 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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 + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "core/gimp.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimppropwidgets.h" +#include "widgets/gimpspinscale.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimphandletransformoptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_HANDLE_MODE +}; + + +static void gimp_handle_transform_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_handle_transform_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpHandleTransformOptions, gimp_handle_transform_options, + GIMP_TYPE_TRANSFORM_OPTIONS) + +#define parent_class gimp_handle_transform_options_parent_class + + +static void +gimp_handle_transform_options_class_init (GimpHandleTransformOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_handle_transform_options_set_property; + object_class->get_property = gimp_handle_transform_options_get_property; + + GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_HANDLE_MODE, + "handle-mode", + N_("Handle mode"), + GIMP_TYPE_TRANSFORM_HANDLE_MODE, + GIMP_HANDLE_MODE_TRANSFORM, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_handle_transform_options_init (GimpHandleTransformOptions *options) +{ +} + +static void +gimp_handle_transform_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpHandleTransformOptions *options = GIMP_HANDLE_TRANSFORM_OPTIONS (object); + + switch (property_id) + { + case PROP_HANDLE_MODE: + options->handle_mode = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_handle_transform_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpHandleTransformOptions *options = GIMP_HANDLE_TRANSFORM_OPTIONS (object); + + switch (property_id) + { + case PROP_HANDLE_MODE: + g_value_set_enum (value, options->handle_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gimp_handle_transform_options_gui: + * @tool_options: a #GimpToolOptions + * + * Build the Transform Tool Options. + * + * Return value: a container holding the transform tool options + **/ +GtkWidget * +gimp_handle_transform_options_gui (GimpToolOptions *tool_options) +{ + GObject *config = G_OBJECT (tool_options); + GtkWidget *vbox = gimp_transform_options_gui (tool_options); + GtkWidget *frame; + GtkWidget *button; + gint i; + + frame = gimp_prop_enum_radio_frame_new (config, "handle-mode", + _("Handle mode"), 0, 0); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* add modifier to name, add tooltip */ + button = g_object_get_data (G_OBJECT (frame), "radio-button"); + + if (GTK_IS_RADIO_BUTTON (button)) + { + GSList *list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); + + for (i = g_slist_length (list) - 1 ; list; list = list->next, i--) + { + GdkModifierType shift = gimp_get_extend_selection_mask (); + GdkModifierType ctrl = gimp_get_constrain_behavior_mask (); + GdkModifierType modifier = 0; + gchar *tooltip = ""; + gchar *tip; + gchar *label; + + switch (i) + { + case GIMP_HANDLE_MODE_ADD_MOVE: + modifier = shift; + tooltip = "Add or move transform handles"; + break; + + case GIMP_HANDLE_MODE_REMOVE: + modifier = ctrl; + tooltip = "Remove transform handles"; + break; + + case GIMP_HANDLE_MODE_TRANSFORM: + modifier = 0; + tooltip = "Transform image by moving handles"; + break; + } + + if (modifier) + { + label = g_strdup_printf ("%s (%s)", + gtk_button_get_label (GTK_BUTTON (list->data)), + gimp_get_mod_string (modifier)); + gtk_button_set_label (GTK_BUTTON (list->data), label); + g_free (label); + + tip = g_strdup_printf ("%s (%s)", + tooltip, gimp_get_mod_string (modifier)); + gimp_help_set_help_data (list->data, tip, NULL); + g_free (tip); + } + else + { + gimp_help_set_help_data (list->data, tooltip, NULL); + } + } + } + + return vbox; +} diff --git a/app/tools/gimphandletransformoptions.h b/app/tools/gimphandletransformoptions.h new file mode 100644 index 0000000000..34077404c9 --- /dev/null +++ b/app/tools/gimphandletransformoptions.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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 + * along with this program. If not, see . + */ + +#ifndef __GIMP_HANDLE_TRANSFORM_OPTIONS_H__ +#define __GIMP_HANDLE_TRANSFORM_OPTIONS_H__ + + +#include "gimptransformoptions.h" + + +#define GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS (gimp_handle_transform_options_get_type ()) +#define GIMP_HANDLE_TRANSFORM_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptions)) +#define GIMP_HANDLE_TRANSFORM_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptionsClass)) +#define GIMP_IS_HANDLE_TRANSFORM_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS)) +#define GIMP_IS_HANDLE_TRANSFORM_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS)) +#define GIMP_HANDLE_TRANSFORM_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptionsClass)) + + +typedef struct _GimpHandleTransformOptions GimpHandleTransformOptions; +typedef struct _GimpHandleTransformOptionsClass GimpHandleTransformOptionsClass; + +struct _GimpHandleTransformOptions +{ + GimpTransformOptions parent_instance; + + GimpTransformHandleMode handle_mode; +}; + +struct _GimpHandleTransformOptionsClass +{ + GimpTransformOptionsClass parent_class; +}; + + +GType gimp_handle_transform_options_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_handle_transform_options_gui (GimpToolOptions *tool_options); + + +#endif /* __GIMP_HANDLE_TRANSFORM_OPTIONS_H__ */ diff --git a/app/tools/gimphandletransformtool.c b/app/tools/gimphandletransformtool.c new file mode 100644 index 0000000000..b214149c36 --- /dev/null +++ b/app/tools/gimphandletransformtool.c @@ -0,0 +1,985 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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 + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpcanvasitem.h" +#include "display/gimpdisplay.h" +#include "display/gimptoolgui.h" + +#include "gimphandletransformoptions.h" +#include "gimphandletransformtool.h" +#include "gimptoolcontrol.h" + +#include "gimp-intl.h" + + +/* the transformation is defined by 8 points: + * + * 4 points on the original image and 4 corresponding points on the + * transformed image. The first NUM points on the transformed image + * are visible as handles. + * + * For these handles, the constants TRANSFORM_HANDLE_N, + * TRANSFORM_HANDLE_S, TRANSFORM_HANDLE_E and TRANSFORM_HANDLE_W are + * used. Actually, it makes no sense to name the handles with north, + * south, east, and west. But this way, we don't need to define even + * more enum constants. + */ + +/* index into trans_info array */ +enum +{ + X0, + Y0, + X1, + Y1, + X2, + Y2, + X3, + Y3, + OX0, + OY0, + OX1, + OY1, + OX2, + OY2, + OX3, + OY3, + NUM +}; + + +/* local function prototypes */ + +static void gimp_handle_transform_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display); +static void gimp_handle_transform_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display); +static void gimp_handle_transform_tool_modifier_key (GimpTool *tool, + GdkModifierType key, + gboolean press, + GdkModifierType state, + GimpDisplay *display); + +static void gimp_handle_transform_tool_dialog (GimpTransformTool *tr_tool); +static void gimp_handle_transform_tool_dialog_update (GimpTransformTool *tr_tool); +static void gimp_handle_transform_tool_prepare (GimpTransformTool *tr_tool); +static void gimp_handle_transform_tool_motion (GimpTransformTool *tr_tool); +static void gimp_handle_transform_tool_recalc_matrix (GimpTransformTool *tr_tool); +static gchar *gimp_handle_transform_tool_get_undo_desc (GimpTransformTool *tr_tool); +static TransformAction + gimp_handle_transform_tool_pick_function (GimpTransformTool *tr_tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display); +static void gimp_handle_transform_tool_cursor_update (GimpTransformTool *tr_tool, + GimpCursorType *cursor, + GimpCursorModifier *modifier); +static void gimp_handle_transform_tool_draw_gui (GimpTransformTool *tr_tool, + gint handle_w, + gint handle_h); + +static gboolean is_handle_position_valid (GimpTransformTool *tr_tool, + gint active_handle); +static void handle_micro_move (GimpTransformTool *tr_tool, + gint active_handle); +static inline gdouble calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by); +static inline gdouble calc_len (gdouble a, + gdouble b); +static inline gdouble calc_lineintersect_ratio (gdouble p1x, + gdouble p1y, + gdouble p2x, + gdouble p2y, + gdouble q1x, + gdouble q1y, + gdouble q2x, + gdouble q2y); +static gboolean mod_gauss (gdouble matrix[], + gdouble solution[], + gint s); + + +G_DEFINE_TYPE (GimpHandleTransformTool, gimp_handle_transform_tool, + GIMP_TYPE_TRANSFORM_TOOL) + +#define parent_class gimp_handle_transform_tool_parent_class + + +void +gimp_handle_transform_tool_register (GimpToolRegisterCallback callback, + gpointer data) +{ + (* callback) (GIMP_TYPE_HANDLE_TRANSFORM_TOOL, + GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, + gimp_handle_transform_options_gui, + GIMP_CONTEXT_BACKGROUND_MASK, + "gimp-handle-transform-tool", + _("Handle Transform"), + _("Handle Transform Tool: " + "Deform the layer, selection or path with handles"), + N_("_Handle Transform"), "H", + NULL, GIMP_HELP_TOOL_HANDLE_TRANSFORM, + GIMP_STOCK_TOOL_HANDLE_TRANSFORM, + data); +} + +static void +gimp_handle_transform_tool_class_init (GimpHandleTransformToolClass *klass) +{ + GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); + GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass); + + tool_class->button_press = gimp_handle_transform_tool_button_press; + tool_class->button_release = gimp_handle_transform_tool_button_release; + tool_class->modifier_key = gimp_handle_transform_tool_modifier_key; + + trans_class->dialog = gimp_handle_transform_tool_dialog; + trans_class->dialog_update = gimp_handle_transform_tool_dialog_update; + trans_class->prepare = gimp_handle_transform_tool_prepare; + trans_class->motion = gimp_handle_transform_tool_motion; + trans_class->recalc_matrix = gimp_handle_transform_tool_recalc_matrix; + trans_class->get_undo_desc = gimp_handle_transform_tool_get_undo_desc; + trans_class->pick_function = gimp_handle_transform_tool_pick_function; + trans_class->cursor_update = gimp_handle_transform_tool_cursor_update; + trans_class->draw_gui = gimp_handle_transform_tool_draw_gui; +} + +static void +gimp_handle_transform_tool_init (GimpHandleTransformTool *ht_tool) +{ + GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (ht_tool); + + tr_tool->progress_text = _("Handle transformation"); + tr_tool->use_grid = TRUE; + + ht_tool->saved_handle_mode = GIMP_HANDLE_MODE_TRANSFORM; +} + +static void +gimp_handle_transform_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display) +{ + GimpHandleTransformTool *ht = GIMP_HANDLE_TRANSFORM_TOOL (tool); + GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool); + GimpHandleTransformOptions *options; + gint num; + gint active_handle; + + options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool); + + num = (gint) tr_tool->trans_info[NUM]; + active_handle = tr_tool->function - TRANSFORM_HANDLE_N; + + /* There is nothing to be done on creation */ + if (tr_tool->function == TRANSFORM_CREATING) + { + GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, + state, press_type, display); + return; + } + + if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE) + { + /* add handle */ + + if (num < 4 && tr_tool->function == TRANSFORM_HANDLE_NONE) + { + tr_tool->trans_info[X0 + 2 * num] = coords->x; + tr_tool->trans_info[Y0 + 2 * num] = coords->y; + tr_tool->function = TRANSFORM_HANDLE_N + num; + tr_tool->trans_info[NUM]++; + + /* check for valid position and calculating of OX0...OY3 is + * done on button release + */ + } + + /* move handles without changing the transformation matrix */ + ht->matrix_recalculation = FALSE; + } + else if (options->handle_mode == GIMP_HANDLE_MODE_REMOVE && + num > 0 && + active_handle >= 0 && + active_handle < 4) + { + /* remove handle */ + + gdouble tempx = tr_tool->trans_info[X0 + 2 * active_handle]; + gdouble tempy = tr_tool->trans_info[Y0 + 2 * active_handle]; + gdouble tempox = tr_tool->trans_info[OX0 + 2 * active_handle]; + gdouble tempoy = tr_tool->trans_info[OY0 + 2 * active_handle]; + gint i; + + num--; + tr_tool->trans_info[NUM]--; + + for (i = active_handle; i < num; i++) + { + tr_tool->trans_info[X0 + 2 * i] = tr_tool->trans_info[X1 + 2 * i]; + tr_tool->trans_info[Y0 + 2 * i] = tr_tool->trans_info[Y1 + 2 * i]; + tr_tool->trans_info[OX0 + 2 * i] = tr_tool->trans_info[OX1 + 2 * i]; + tr_tool->trans_info[OY0 + 2 * i] = tr_tool->trans_info[OY1 + 2 * i]; + } + + tr_tool->trans_info[X0 + 2 * num] = tempx; + tr_tool->trans_info[Y0 + 2 * num] = tempy; + tr_tool->trans_info[OX0 + 2 * num] = tempox; + tr_tool->trans_info[OY0 + 2 * num] = tempoy; + } + + GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, + state, press_type, display); +} + +static void +gimp_handle_transform_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display) +{ + GimpHandleTransformTool *ht = GIMP_HANDLE_TRANSFORM_TOOL (tool); + GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool); + GimpHandleTransformOptions *options; + gint active_handle; + + options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool); + + active_handle = tr_tool->function - TRANSFORM_HANDLE_N; + + if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE && + active_handle >= 0 && + active_handle < 4) + { + GimpMatrix3 matrix; + + if (! is_handle_position_valid (tr_tool, active_handle)) + { + handle_micro_move (tr_tool, active_handle); + } + + /* handle was added or moved. calculate new original position */ + matrix = tr_tool->transform; + gimp_matrix3_invert (&matrix); + gimp_matrix3_transform_point (&matrix, + tr_tool->trans_info[X0 + 2 * active_handle], + tr_tool->trans_info[Y0 + 2 * active_handle], + &tr_tool->trans_info[OX0 + 2 * active_handle], + &tr_tool->trans_info[OY0 + 2 * active_handle]); + } + + if (release_type != GIMP_BUTTON_RELEASE_CANCEL) + { + /* force redraw */ + gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); + gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); + } + + ht->matrix_recalculation = TRUE; + + GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, + state, release_type, display); +} + +static void +gimp_handle_transform_tool_modifier_key (GimpTool *tool, + GdkModifierType key, + gboolean press, + GdkModifierType state, + GimpDisplay *display) +{ + GimpHandleTransformTool *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tool); + GimpHandleTransformOptions *options; + GdkModifierType shift = gimp_get_extend_selection_mask (); + GdkModifierType ctrl = gimp_get_constrain_behavior_mask (); + GimpTransformHandleMode handle_mode; + + options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tool); + + handle_mode = options->handle_mode; + + if (press) + { + if (key == (state & (shift | ctrl))) + { + /* first modifier pressed */ + ht_tool->saved_handle_mode = options->handle_mode; + } + } + else + { + if (! (state & (shift | ctrl))) + { + /* last modifier released */ + handle_mode = ht_tool->saved_handle_mode; + } + } + + if (state & shift) + { + handle_mode = GIMP_HANDLE_MODE_ADD_MOVE; + } + else if (state & ctrl) + { + handle_mode = GIMP_HANDLE_MODE_REMOVE; + } + + if (handle_mode != options->handle_mode) + { + g_object_set (options, "handle-mode", handle_mode, NULL); + } + + GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, + state, display); +} + +static void +gimp_handle_transform_tool_dialog (GimpTransformTool *tr_tool) +{ + GimpHandleTransformTool *handle_transform = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool); + GtkWidget *frame; + GtkWidget *table; + gint x, y; + + frame = gimp_frame_new (_("Transformation Matrix")); + gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)), frame, + FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_table_set_col_spacings (GTK_TABLE (table), 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + for (y = 0; y < 3; y++) + for (x = 0; x < 3; x++) + { + GtkWidget *label = gtk_label_new (" "); + + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.0); + gtk_label_set_width_chars (GTK_LABEL (label), 12); + gtk_table_attach (GTK_TABLE (table), label, + x, x + 1, y, y + 1, GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (label); + + handle_transform->label[y][x] = label; + } +} + +static void +gimp_handle_transform_tool_dialog_update (GimpTransformTool *tr_tool) +{ + GimpHandleTransformTool *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool); + gint x, y; + + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { + gchar buf[32]; + + g_snprintf (buf, sizeof (buf), + "%10.5f", tr_tool->transform.coeff[y][x]); + + gtk_label_set_text (GTK_LABEL (ht_tool->label[y][x]), buf); + } + } +} + +static void +gimp_handle_transform_tool_prepare (GimpTransformTool *tr_tool) +{ + GimpHandleTransformTool *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool); + + tr_tool->trans_info[X0] = (gdouble) tr_tool->x1; + tr_tool->trans_info[Y0] = (gdouble) tr_tool->y1; + tr_tool->trans_info[X1] = (gdouble) tr_tool->x2; + tr_tool->trans_info[Y1] = (gdouble) tr_tool->y1; + tr_tool->trans_info[X2] = (gdouble) tr_tool->x1; + tr_tool->trans_info[Y2] = (gdouble) tr_tool->y2; + tr_tool->trans_info[X3] = (gdouble) tr_tool->x2; + tr_tool->trans_info[Y3] = (gdouble) tr_tool->y2; + tr_tool->trans_info[OX0] = (gdouble) tr_tool->x1; + tr_tool->trans_info[OY0] = (gdouble) tr_tool->y1; + tr_tool->trans_info[OX1] = (gdouble) tr_tool->x2; + tr_tool->trans_info[OY1] = (gdouble) tr_tool->y1; + tr_tool->trans_info[OX2] = (gdouble) tr_tool->x1; + tr_tool->trans_info[OY2] = (gdouble) tr_tool->y2; + tr_tool->trans_info[OX3] = (gdouble) tr_tool->x2; + tr_tool->trans_info[OY3] = (gdouble) tr_tool->y2; + tr_tool->trans_info[NUM] = 0; + + ht_tool->matrix_recalculation = TRUE; +} + +static void +gimp_handle_transform_tool_motion (GimpTransformTool *tr_tool) +{ + GimpHandleTransformOptions *options; + gint active_handle; + gint num; + + options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool); + + active_handle = tr_tool->function - TRANSFORM_HANDLE_N; + num = (gint) tr_tool->trans_info[NUM]; + + if (active_handle >= 0 && active_handle < 4) + { + if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE) + { + tr_tool->trans_info[X0 + 2*active_handle] += tr_tool->curx - tr_tool->lastx; + tr_tool->trans_info[Y0 + 2*active_handle] += tr_tool->cury - tr_tool->lasty; + /* check for valid position and calculating of OX0...OY3 is + * done on button release hopefully this makes the code run + * faster Moving could be even faster if there was caching + * for the image preview + */ + } + else if (options->handle_mode == GIMP_HANDLE_MODE_TRANSFORM) + { + gdouble angle, angle_sin, angle_cos, scale; + gdouble fixed_handles_x[3]; + gdouble fixed_handles_y[3]; + gdouble oldpos_x[4], oldpos_y[4]; + gdouble newpos_x[4], newpos_y[4]; + gint i, j; + + for (i = 0, j = 0; i < 4; i++) + { + /* Find all visible handles that are not being moved */ + if (i < num && i != active_handle) + { + fixed_handles_x[j] = tr_tool->prev_trans_info[0][X0+i*2]; + fixed_handles_y[j] = tr_tool->prev_trans_info[0][Y0+i*2]; + j++; + } + + newpos_x[i] = oldpos_x[i] = tr_tool->prev_trans_info[0][X0+i*2]; + newpos_y[i] = oldpos_y[i] = tr_tool->prev_trans_info[0][Y0+i*2]; + } + + newpos_x[active_handle] = oldpos_x[active_handle] + tr_tool->curx - tr_tool->mousex; + newpos_y[active_handle] = oldpos_y[active_handle] + tr_tool->cury - tr_tool->mousey; + + switch (num) + { + case 1: + /* move */ + for (i = 1; i < 4; i++) + { + newpos_x[i] = oldpos_x[i] + tr_tool->curx - tr_tool->mousex; + newpos_y[i] = oldpos_y[i] + tr_tool->cury - tr_tool->mousey; + } + break; + + case 2: + /* rotate and keep-aspect-scale */ + scale = calc_len (newpos_x[active_handle] - fixed_handles_x[0], + newpos_y[active_handle] - fixed_handles_y[0]) + / calc_len (oldpos_x[active_handle] - fixed_handles_x[0], + oldpos_y[active_handle] - fixed_handles_y[0]); + + angle = calc_angle (oldpos_x[active_handle] - fixed_handles_x[0], + oldpos_y[active_handle] - fixed_handles_y[0], + newpos_x[active_handle] - fixed_handles_x[0], + newpos_y[active_handle] - fixed_handles_y[0]); + + angle_sin = sin (angle); + angle_cos = cos (angle); + + for (i = 2; i < 4; i++) + { + newpos_x[i] = fixed_handles_x[0] + + scale * (angle_cos * (oldpos_x[i]-fixed_handles_x[0]) + + angle_sin * (oldpos_y[i]-fixed_handles_y[0]) ); + newpos_y[i] = fixed_handles_y[0] + + scale * (-angle_sin * (oldpos_x[i]-fixed_handles_x[0]) + + angle_cos * (oldpos_y[i]-fixed_handles_y[0]) ); + } + break; + + case 3: + /* shear and non-aspect-scale */ + scale = calc_lineintersect_ratio (oldpos_x[3], oldpos_y[3], + oldpos_x[active_handle], oldpos_y[active_handle], + fixed_handles_x[0], fixed_handles_y[0], + fixed_handles_x[1], fixed_handles_y[1]); + + newpos_x[3] = oldpos_x[3] + scale * (tr_tool->curx - tr_tool->mousex); + newpos_y[3] = oldpos_y[3] + scale * (tr_tool->cury - tr_tool->mousey); + break; + } + + for (i = 0; i < 4; i++) + { + tr_tool->trans_info[X0 + 2*i] = newpos_x[i]; + tr_tool->trans_info[Y0 + 2*i] = newpos_y[i]; + } + } + } +} + +static void +gimp_handle_transform_tool_recalc_matrix (GimpTransformTool *tr_tool) +{ + gdouble coeff[8*9]; + gdouble sol[8]; + int i; + gdouble opos_x[4], opos_y[4]; + gdouble pos_x[4], pos_y[4]; + GimpHandleTransformTool *handle_transform = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool); + + if (handle_transform->matrix_recalculation) + { + for (i = 0; i < 4; i++) + { + pos_x[i] = tr_tool->trans_info[X0+i*2]; + pos_y[i] = tr_tool->trans_info[Y0+i*2]; + opos_x[i] = tr_tool->trans_info[OX0+i*2]; + opos_y[i] = tr_tool->trans_info[OY0+i*2]; + } + + for (i = 0; i < 4; i++) + { + coeff[i*9+0] = opos_x[i]; + coeff[i*9+1] = opos_y[i]; + coeff[i*9+2] = 1; + coeff[i*9+3] = 0; + coeff[i*9+4] = 0; + coeff[i*9+5] = 0; + coeff[i*9+6] = -opos_x[i]*pos_x[i]; + coeff[i*9+7] = -opos_y[i]*pos_x[i]; + coeff[i*9+8] = pos_x[i]; + + coeff[(i+4)*9+0] = 0; + coeff[(i+4)*9+1] = 0; + coeff[(i+4)*9+2] = 0; + coeff[(i+4)*9+3] = opos_x[i]; + coeff[(i+4)*9+4] = opos_y[i]; + coeff[(i+4)*9+5] = 1; + coeff[(i+4)*9+6] = -opos_x[i]*pos_y[i]; + coeff[(i+4)*9+7] = -opos_y[i]*pos_y[i]; + coeff[(i+4)*9+8] = pos_y[i]; + } + + if (mod_gauss(coeff, sol, 8)) + { + tr_tool->transform.coeff[0][0] = sol[0]; + tr_tool->transform.coeff[0][1] = sol[1]; + tr_tool->transform.coeff[0][2] = sol[2]; + tr_tool->transform.coeff[1][0] = sol[3]; + tr_tool->transform.coeff[1][1] = sol[4]; + tr_tool->transform.coeff[1][2] = sol[5]; + tr_tool->transform.coeff[2][0] = sol[6]; + tr_tool->transform.coeff[2][1] = sol[7]; + tr_tool->transform.coeff[2][2] = 1; + } + else + { + /* this should not happen + * reset the matrix so the user sees that something went wrong */ + gimp_matrix3_identity (&tr_tool->transform); + } + } +} + +static gchar * +gimp_handle_transform_tool_get_undo_desc (GimpTransformTool *tr_tool) +{ + return g_strdup (C_("undo-type", "Handle transform")); +} + +static TransformAction +gimp_handle_transform_tool_pick_function (GimpTransformTool *tr_tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display) +{ + TransformAction i; + + for (i = TRANSFORM_HANDLE_N; i < TRANSFORM_HANDLE_N + 4; i++) + { + if (tr_tool->handles[i] && + gimp_canvas_item_hit (tr_tool->handles[i], coords->x, coords->y)) + { + return i; + } + } + + return TRANSFORM_HANDLE_NONE; +} + +static void +gimp_handle_transform_tool_cursor_update (GimpTransformTool *tr_tool, + GimpCursorType *cursor, + GimpCursorModifier *modifier) +{ + GimpHandleTransformOptions *options; + GimpToolCursorType tool_cursor = GIMP_TOOL_CURSOR_NONE; + + options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool); + + *cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + *modifier = GIMP_CURSOR_MODIFIER_NONE; + + /* do not show modifiers when the tool isn't active */ + if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tr_tool))) + return; + + if (options->handle_mode == GIMP_HANDLE_MODE_TRANSFORM && + tr_tool->function > TRANSFORM_HANDLE_NONE) + { + switch ((gint) tr_tool->trans_info[NUM]) + { + case 1: + tool_cursor = GIMP_TOOL_CURSOR_MOVE; + break; + case 2: + tool_cursor = GIMP_TOOL_CURSOR_ROTATE; + break; + case 3: + tool_cursor = GIMP_TOOL_CURSOR_SHEAR; + break; + case 4: + tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE; + break; + } + } + else if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE) + { + if (tr_tool->function > TRANSFORM_HANDLE_NONE) + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + else + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + } + else if (options->handle_mode == GIMP_HANDLE_MODE_REMOVE) + { + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + } + + gimp_tool_control_set_tool_cursor (GIMP_TOOL (tr_tool)->control, + tool_cursor); +} + +static void +gimp_handle_transform_tool_draw_gui (GimpTransformTool *tr_tool, + gint handle_w, + gint handle_h) +{ + GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tr_tool); + gint i; + +#if 0 + /* show additional points for debugging */ + for (i = tr_tool->trans_info[NUM]; i < 4; i++) + { + gimp_draw_tool_add_handle (draw_tool, + GIMP_HANDLE_FILLED_CIRCLE, + tr_tool->trans_info[X0+2*i], + tr_tool->trans_info[Y0+2*i], + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + gimp_draw_tool_add_handle (draw_tool, + GIMP_HANDLE_FILLED_DIAMOND, + tr_tool->trans_info[OX0+2*i], + tr_tool->trans_info[OY0+2*i], + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + } + + for (i = 0; i < tr_tool->trans_info[NUM]; i++) + { + tr_tool->handles[TRANSFORM_HANDLE_N + i] = + gimp_draw_tool_add_handle (draw_tool, + GIMP_HANDLE_DIAMOND, + tr_tool->trans_info[OX0+2*i], + tr_tool->trans_info[OY0+2*i], + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + } +#endif + + for (i = 0; i < tr_tool->trans_info[NUM]; i++) + { + tr_tool->handles[TRANSFORM_HANDLE_N + i] = + gimp_draw_tool_add_handle (draw_tool, + GIMP_HANDLE_CIRCLE, + tr_tool->trans_info[X0 + 2 * i], + tr_tool->trans_info[Y0 + 2 * i], + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + } +} + +/* check if a handle is not on the connection line of two other handles */ +static gboolean +is_handle_position_valid (GimpTransformTool *tr_tool, + gint active_handle) +{ + gint i, j, k; + + if (tr_tool->trans_info[NUM] < 3) + { + /* there aren't two other handles */ + return TRUE; + } + + if (tr_tool->trans_info[NUM] == 3) + { + return ((tr_tool->trans_info[X0] - tr_tool->trans_info[X1]) * + (tr_tool->trans_info[Y1] - tr_tool->trans_info[Y2]) != + + (tr_tool->trans_info[X1] - tr_tool->trans_info[X2]) * + (tr_tool->trans_info[Y0] - tr_tool->trans_info[Y1])); + } + + /* tr_tool->trans_info[NUM] == 4 */ + for (i = 0; i < 2; i++) + { + for (j = i + 1; j < 3; j++) + { + for (k = j + 1; i < 4; i++) + { + if (active_handle == i || + active_handle == j || + active_handle == k) + { + if ((tr_tool->trans_info[X0 + 2 * i] - + tr_tool->trans_info[X0 + 2 * j]) * + (tr_tool->trans_info[Y0 + 2 * j] - + tr_tool->trans_info[Y0 + 2 * k]) == + + (tr_tool->trans_info[X0 + 2 * j] - + tr_tool->trans_info[X0 + 2 * k]) * + (tr_tool->trans_info[Y0 + 2 * i] - + tr_tool->trans_info[Y0 + 2 * j])) + { + return FALSE; + } + } + } + } + } + + return TRUE; +} + +/* three handles on a line causes problems. + * Let's move the new handle around a bit to find a better position */ +static void +handle_micro_move (GimpTransformTool *tr_tool, + gint active_handle) +{ + gdouble posx = tr_tool->trans_info[X0 + 2 * active_handle]; + gdouble posy = tr_tool->trans_info[Y0 + 2 * active_handle]; + gdouble dx, dy; + + for (dx = -0.1; dx < 0.11; dx += 0.1) + { + tr_tool->trans_info[X0 + 2 * active_handle] = posx + dx; + + for (dy = -0.1; dy < 0.11; dy += 0.1) + { + tr_tool->trans_info[Y0 + 2 * active_handle] = posy + dy; + + if (is_handle_position_valid (tr_tool, active_handle)) + { + return; + } + } + } +} + +/* finds the clockwise angle between the vectors given, 0-2π */ +static inline gdouble +calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by) +{ + gdouble angle; + gdouble direction; + gdouble length = sqrt ((ax * ax + ay * ay) * (bx * bx + by * by)); + + angle = acos ((ax * bx + ay * by) / length); + direction = ax * by - ay * bx; + + return ((direction < 0) ? angle : 2 * G_PI - angle); +} + +static inline gdouble +calc_len (gdouble a, + gdouble b) +{ + return sqrt (a * a + b * b); +} + + +/* imagine two lines, one through the points p1 and p2, the other one + * through the points q1 and q2. Find the intersection point r. + * Calculate (distance p1 to r)/(distance p2 to r) + */ +static inline gdouble +calc_lineintersect_ratio (gdouble p1x, gdouble p1y, + gdouble p2x, gdouble p2y, + gdouble q1x, gdouble q1y, + gdouble q2x, gdouble q2y) +{ + gdouble denom, u; + + denom = (q2y - q1y) * (p2x - p1x) - (q2x - q1x) * (p2y - p1y); + if (denom == 0.0) + { + /* u is infinite, so u/(u-1) is 1 */ + return 1.0; + } + + u = (q2y - q1y) * (q1x - p1x) - (q1y - p1y) * (q2x - q1x); + u /= denom; + + return u / (u - 1); +} + + +/* modified gaussian algorithm + * solves a system of linear equations + * + * Example: + * 1x + 2y + 4z = 25 + * 2x + 1y = 4 + * 3x + 5y + 2z = 23 + * Solution: x=1, y=2, z=5 + * + * Input: + * matrix = { 1,2,4,25,2,1,0,4,3,5,2,23 } + * s = 3 (Number of variables) + * Output: + * return value == TRUE (TRUE, if there is a single unique solution) + * solution == { 1,2,5 } (if the return value is FALSE, the content + * of solution is of no use) + */ +static gboolean +mod_gauss (gdouble matrix[], + gdouble solution[], + gint s) +{ + gint p[s]; /* row permutation */ + gint i, j, r, temp; + gdouble q; + gint t = s + 1; + + for (i = 0; i < s; i++) + { + p[i] = i; + } + + for (r = 0; r < s; r++) + { + /* make sure that (r,r) is not 0 */ + if (matrix[p[r] * t + r] == 0.0) + { + /* we need to permutate rows */ + for (i = r + 1; i <= s; i++) + { + if (i == s) + { + /* if this happens, the linear system has zero or + * more than one solutions. + */ + return FALSE; + } + + if (matrix[p[i] * t + r] != 0.0) + break; + } + + temp = p[r]; + p[r] = p[i]; + p[i] = temp; + } + + /* make (r,r) == 1 */ + q = 1.0 / matrix[p[r] * t + r]; + matrix[p[r] * t + r] = 1.0; + + for (j = r + 1; j < t; j++) + { + matrix[p[r] * t + j] *= q; + } + + /* make that all entries in column r are 0 (except (r,r)) */ + for (i = 0; i < s; i++) + { + if (i == r) + continue; + + for (j = r + 1; j < t ; j++) + { + matrix[p[i] * t + j] -= matrix[p[r] * t + j] * matrix[p[i] * t + r]; + } + + /* we don't need to execute the following line + * since we won't access this element again: + * + * matrix[p[i] * t + r] = 0.0; + */ + } + } + + for (i = 0; i < s; i++) + { + solution[i] = matrix[p[i] * t + s]; + } + + return TRUE; +} diff --git a/app/tools/gimphandletransformtool.h b/app/tools/gimphandletransformtool.h new file mode 100644 index 0000000000..df6157efbf --- /dev/null +++ b/app/tools/gimphandletransformtool.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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 + * along with this program. If not, see . + */ + +#ifndef __GIMP_HANDLE_TRANSFORM_TOOL_H__ +#define __GIMP_HANDLE_TRANSFORM_TOOL_H__ + + +#include "gimptransformtool.h" + + +#define GIMP_TYPE_HANDLE_TRANSFORM_TOOL (gimp_handle_transform_tool_get_type ()) +#define GIMP_HANDLE_TRANSFORM_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformTool)) +#define GIMP_HANDLE_TRANSFORM_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformToolClass)) +#define GIMP_IS_HANDLE_TRANSFORM_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HANDLE_TRANSFORM_TOOL)) +#define GIMP_IS_HANDLE_TRANSFORM_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HANDLE_TRANSFORM_TOOL)) +#define GIMP_HANDLE_TRANSFORM_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformToolClass)) + +#define GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS(t) (GIMP_HANDLE_TRANSFORM_OPTIONS (gimp_tool_get_options (GIMP_TOOL (t)))) + + +typedef struct _GimpHandleTransformTool GimpHandleTransformTool; +typedef struct _GimpHandleTransformToolClass GimpHandleTransformToolClass; + +struct _GimpHandleTransformTool +{ + GimpTransformTool parent_instance; + + GtkWidget *label[3][3]; + gboolean matrix_recalculation; + + GimpTransformHandleMode saved_handle_mode; +}; + +struct _GimpHandleTransformToolClass +{ + GimpTransformToolClass parent_class; +}; + + +void gimp_handle_transform_tool_register (GimpToolRegisterCallback callback, + gpointer data); + +GType gimp_handle_transform_tool_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_HANDLE_TRANSFORM_TOOL_H__ */ diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c index 885cc4d95b..34fcc1e524 100644 --- a/app/tools/gimptransformtool.c +++ b/app/tools/gimptransformtool.c @@ -59,6 +59,7 @@ #include "display/gimptoolgui.h" #include "gimptoolcontrol.h" +#include "gimphandletransformtool.h" #include "gimpperspectivetool.h" #include "gimpunifiedtransformtool.h" #include "gimptransformoptions.h" @@ -1088,10 +1089,15 @@ gimp_transform_tool_draw (GimpDrawTool *draw_tool) if (gimp_transform_options_show_preview (options)) { GimpMatrix3 matrix = tr_tool->transform; + gboolean perspective; if (options->direction == GIMP_TRANSFORM_BACKWARD) gimp_matrix3_invert (&matrix); + perspective = (GIMP_IS_PERSPECTIVE_TOOL (tr_tool) || + GIMP_IS_HANDLE_TRANSFORM_TOOL (tr_tool) || + GIMP_IS_UNIFIED_TRANSFORM_TOOL (tr_tool)); + gimp_draw_tool_add_transform_preview (draw_tool, tool->drawable, &matrix, @@ -1099,8 +1105,7 @@ gimp_transform_tool_draw (GimpDrawTool *draw_tool) tr_tool->y1, tr_tool->x2, tr_tool->y2, - GIMP_IS_PERSPECTIVE_TOOL (tr_tool) || - GIMP_IS_UNIFIED_TRANSFORM_TOOL (tr_tool), + perspective, options->preview_opacity); } diff --git a/app/tools/gimptransformtool.h b/app/tools/gimptransformtool.h index 2ee4634017..4ef6591fd2 100644 --- a/app/tools/gimptransformtool.h +++ b/app/tools/gimptransformtool.h @@ -54,7 +54,7 @@ typedef enum * of the enums at the top of each transformation tool, stored in * trans_info and related */ -#define TRANS_INFO_SIZE 10 +#define TRANS_INFO_SIZE 17 typedef gdouble TransInfo[TRANS_INFO_SIZE]; diff --git a/app/tools/tools-enums.c b/app/tools/tools-enums.c index 61155c7227..77b5fb8087 100644 --- a/app/tools/tools-enums.c +++ b/app/tools/tools-enums.c @@ -73,6 +73,37 @@ gimp_button_release_type_get_type (void) return type; } +GType +gimp_transform_handle_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDLE_MODE_ADD_MOVE, "GIMP_HANDLE_MODE_ADD_MOVE", "add-move" }, + { GIMP_HANDLE_MODE_REMOVE, "GIMP_HANDLE_MODE_REMOVE", "remove" }, + { GIMP_HANDLE_MODE_TRANSFORM, "GIMP_HANDLE_MODE_TRANSFORM", "transform" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDLE_MODE_ADD_MOVE, NC_("transform-handle-mode", "Add/Move"), NULL }, + { GIMP_HANDLE_MODE_REMOVE, NC_("transform-handle-mode", "Remove"), NULL }, + { GIMP_HANDLE_MODE_TRANSFORM, NC_("transform-handle-mode", "Transform"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpTransformHandleMode", values); + gimp_type_set_translation_context (type, "transform-handle-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + GType gimp_rectangle_constraint_get_type (void) { diff --git a/app/tools/tools-enums.h b/app/tools/tools-enums.h index c2d2cc06cf..60246efe0d 100644 --- a/app/tools/tools-enums.h +++ b/app/tools/tools-enums.h @@ -47,6 +47,18 @@ typedef enum } GimpButtonReleaseType; +#define GIMP_TYPE_TRANSFORM_HANDLE_MODE (gimp_transform_handle_mode_get_type ()) + +GType gimp_transform_handle_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDLE_MODE_ADD_MOVE, /*< desc="Add/Move" >*/ + GIMP_HANDLE_MODE_REMOVE, /*< desc="Remove" >*/ + GIMP_HANDLE_MODE_TRANSFORM, /*< desc="Transform" >*/ +} GimpTransformHandleMode; + + #define GIMP_TYPE_RECTANGLE_CONSTRAINT (gimp_rectangle_constraint_get_type ()) GType gimp_rectangle_constraint_get_type (void) G_GNUC_CONST; diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h index 1d6754eb75..7e7076ae4a 100644 --- a/app/widgets/gimphelp-ids.h +++ b/app/widgets/gimphelp-ids.h @@ -281,6 +281,7 @@ #define GIMP_HELP_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select" #define GIMP_HELP_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select" #define GIMP_HELP_TOOL_GEGL "gimp-tool-gegl" +#define GIMP_HELP_TOOL_HANDLE_TRANSFORM "gimp-tool-handle-transform" #define GIMP_HELP_TOOL_HEAL "gimp-tool-heal" #define GIMP_HELP_TOOL_HUE_SATURATION "gimp-tool-hue-saturation" #define GIMP_HELP_TOOL_INK "gimp-tool-ink" diff --git a/icons/16/gimp-tool-handle-transform.png b/icons/16/gimp-tool-handle-transform.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc2ed4f26047c26d8396d116a5143c5f1ab8071 GIT binary patch literal 1166 zcmah|Ye-XJ7=F%eJ7=42bDMW6sSGg+iNrD}!^~wxgcLzRrGJ7Ig8c}8bdjLjzxpQz z{p&{*^&^t(VmHJJ3$m4_-Nv+O?o6j`x}CG$Z@O5}Be&;*4WBcX| zQ@#m6hP$?UCwUu#Y@l&rIB|fS;b7h7Y9!cq<^I)6w4yZC?r8?h4h9Zw?YXpQJnG)I z$v9-m$ttYqynOo_AQrf*tLiK4oJ~|?9EHHX{**l@+ukbjTw`2MvhWbiTk&Wf=K8>0kOE-3eKHO*YANlZy&O3|~*}!pJqjzKmQ_p?) zIyws~%cJXhLt)KniO~e%H=g2Y?J!|Z07$e8@o7(i(}m&T07{Arv1V1VB1$57j>D54 zFNjBq=O~^fGfVa$Yr`qKEz{Df$dbq9utBz3A@DpjO=s01u4{;fr=SMaM0jfEYIypG zC)jl&*`uVuoEETr_r*kIc?kj&G4Lc1*EAA9axNm4s!d>t)qsT@8vVO|ue zr?i{XZlMY|2!a4H4Vqv;aZzqfdQQ4PvKsH;F!CKH$OfL02_(wAzzc}XYA_fi%c3o3 zR;5Hz0_-|7SxcfZDp`-iY%)R;`8h~Th{sG!<7%qPxL7Q{D_wx?tPt|qd0kE`yh9^! z(J{!{Zd`O+dd+#86Uk^Z=jUYNb zjW>ORSY1+xv&}oPc14l#$%}!-O?%f#7L!~>In<#!(>Xzc+JE=5I&klb>{uMi&2~x4 z@)iH1mtXRPOcys6iee}bL4W^8bDl%?1;=J-n6~?#>V}vKLMZjC$<0p literal 0 HcmV?d00001 diff --git a/icons/22/gimp-tool-handle-transform.png b/icons/22/gimp-tool-handle-transform.png new file mode 100644 index 0000000000000000000000000000000000000000..797b99b7d68d501a7677169153009374d9138be0 GIT binary patch literal 2084 zcmb7EZD?Cn7=G{fP13YY`njwh>C$v|+TrR7bN<-u64p%Ywko4Vkl_y%+z-M2P>=~j zkiqbeD1L&1xF2Fw5LZUFv36E*tSz(~{chIUu3f85nx;vU+KsC~YYYQ&{6x(%8 z3-N+*Ny}z0EL?f}SF_(*ZYP%!Q=%+M;n!Xa+A2NyTx2$CeE8MnWG0&pY$e}rE>B8G zX?0DNT3zPmR7n6omOj5%rFN^*|C1|HGW&hm!GGHwffAPu=9iKns!kr;TfE2KGpyvR zX$^SU0T0OyS@Re%^E4Gdv9qw4S7ja~!2$R0&B1Pe8JuitfX#)#ksX~kK}*E}bHS3U z6UPI@v{0j?lT9TqRXzB472nf831j1T!B<@gSAU;i`PnQgRX<6-pm3ULk`l5*Knx?mSN?hPawP`~HBBUfuH-O_fhQvw&78n15KNq9eXUwi; zv^HGiE-NuxJZhlTpXq$_IaT6tF?sOrECetNkVFwgNd!T_b_O2u^K;>`x*B+;^$^%q zX~3Zf|5^v)+a#un8BS*o*cAa}na&L=Mew|U;6We=>*Ydx4O)-Wp@PHovc^M+3kqE@ zGByL=QYScUGDxWSA5v6~UUP`Mb1|s((J_Pu+&y%jH)AU?^IViAn3$Re1@|;rgN}f> zVFOc)s(JLACL`Y+4md+tY7>;I~L`YkO$b}6+_qE^fb3JOR%b+kc!FnFSo^_{(X&ge%S4C zaG!Kc@7h)Eb@|H+*q*MzvdUM<506e)+}~Ri^?LG(hVVkivARjDVPyqD%F_rNLQXHw zc6Z(Aue;vWo5Z@2=InWo?(=NDfZ+Dfxcjqq2h%!t$DU z+?A^ewUq_j?U5ei6n#BBhN$VVDEgtN;C`JU4>K9SE|A{w{%tB9;-fMx>U-c8MvO7KMB# hzX_y{{q{yl)|rYSTz~HBo?)FPhfuIN(9wAM+~3r*qOAY` literal 0 HcmV?d00001 diff --git a/icons/22/gimp-tool-handle-transform.xcf b/icons/22/gimp-tool-handle-transform.xcf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/icons/Makefile.am b/icons/Makefile.am index a6a030e95b..71b007cb2b 100644 --- a/icons/Makefile.am +++ b/icons/Makefile.am @@ -180,6 +180,7 @@ icons16_DATA = \ 16/gimp-tool-foreground-select.png \ 16/gimp-tool-free-select.png \ 16/gimp-tool-fuzzy-select.png \ + 16/gimp-tool-handle-transform.png \ 16/gimp-tool-heal.png \ 16/gimp-tool-hue-saturation.png \ 16/gimp-tool-ink.png \ @@ -290,6 +291,7 @@ icons22_DATA = \ 22/gimp-tool-foreground-select.png \ 22/gimp-tool-free-select.png \ 22/gimp-tool-fuzzy-select.png \ + 22/gimp-tool-handle-transform.png \ 22/gimp-tool-heal.png \ 22/gimp-tool-hue-saturation.png \ 22/gimp-tool-ink.png \ diff --git a/libgimpwidgets/gimpstock.c b/libgimpwidgets/gimpstock.c index 320237180d..5a7e969072 100644 --- a/libgimpwidgets/gimpstock.c +++ b/libgimpwidgets/gimpstock.c @@ -256,6 +256,7 @@ static const GtkStockItem gimp_stock_items[] = { GIMP_STOCK_TOOL_FOREGROUND_SELECT, N_("_Select"), 0, 0, LIBGIMP_DOMAIN }, { GIMP_STOCK_TOOL_FUZZY_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN }, { GIMP_STOCK_TOOL_HUE_SATURATION, NULL, 0, 0, LIBGIMP_DOMAIN }, + { GIMP_STOCK_TOOL_HANDLE_TRANSFORM,N_("_Transform"),0, 0, LIBGIMP_DOMAIN }, { GIMP_STOCK_TOOL_HEAL, NULL, 0, 0, LIBGIMP_DOMAIN }, { GIMP_STOCK_TOOL_INK, NULL, 0, 0, LIBGIMP_DOMAIN }, { GIMP_STOCK_TOOL_ISCISSORS, NULL, 0, 0, LIBGIMP_DOMAIN }, diff --git a/libgimpwidgets/gimpstock.h b/libgimpwidgets/gimpstock.h index 97936146dd..ed23e2d07d 100644 --- a/libgimpwidgets/gimpstock.h +++ b/libgimpwidgets/gimpstock.h @@ -125,6 +125,7 @@ G_BEGIN_DECLS #define GIMP_STOCK_TOOL_FREE_SELECT "gimp-tool-free-select" #define GIMP_STOCK_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select" #define GIMP_STOCK_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select" +#define GIMP_STOCK_TOOL_HANDLE_TRANSFORM "gimp-tool-handle-transform" #define GIMP_STOCK_TOOL_HEAL "gimp-tool-heal" #define GIMP_STOCK_TOOL_HUE_SATURATION "gimp-tool-hue-saturation" #define GIMP_STOCK_TOOL_INK "gimp-tool-ink" diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in index c9162b581b..a1025b30eb 100644 --- a/menus/image-menu.xml.in +++ b/menus/image-menu.xml.in @@ -602,6 +602,7 @@ + diff --git a/po/POTFILES.in b/po/POTFILES.in index 7b5e93de12..c34dbce77e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -368,6 +368,8 @@ app/tools/gimpforegroundselecttool.c app/tools/gimpfreeselecttool.c app/tools/gimpfuzzyselecttool.c app/tools/gimpgegltool.c +app/tools/gimphandletransformoptions.c +app/tools/gimphandletransformtool.c app/tools/gimphealtool.c app/tools/gimphistogramoptions.c app/tools/gimphuesaturationtool.c