1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25 
26 #include "tools-types.h"
27 
28 #include "widgets/gimphelp-ids.h"
29 
30 #include "display/gimpdisplay.h"
31 #include "display/gimpdisplayshell.h"
32 #include "display/gimpdisplayshell-transform.h"
33 #include "display/gimptoolgui.h"
34 #include "display/gimptooltransformgrid.h"
35 
36 #include "gimpperspectivetool.h"
37 #include "gimptoolcontrol.h"
38 #include "gimptransformgridoptions.h"
39 
40 #include "gimp-intl.h"
41 
42 
43 /*  index into trans_info array  */
44 enum
45 {
46   X0,
47   Y0,
48   X1,
49   Y1,
50   X2,
51   Y2,
52   X3,
53   Y3
54 };
55 
56 
57 /*  local function prototypes  */
58 
59 static void             gimp_perspective_tool_matrix_to_info (GimpTransformGridTool    *tg_tool,
60                                                               const GimpMatrix3        *transform);
61 static void             gimp_perspective_tool_prepare        (GimpTransformGridTool    *tg_tool);
62 static void             gimp_perspective_tool_readjust       (GimpTransformGridTool    *tg_tool);
63 static GimpToolWidget * gimp_perspective_tool_get_widget     (GimpTransformGridTool    *tg_tool);
64 static void             gimp_perspective_tool_update_widget  (GimpTransformGridTool    *tg_tool);
65 static void             gimp_perspective_tool_widget_changed (GimpTransformGridTool    *tg_tool);
66 
67 static void             gimp_perspective_tool_info_to_points (GimpGenericTransformTool *generic);
68 
69 
G_DEFINE_TYPE(GimpPerspectiveTool,gimp_perspective_tool,GIMP_TYPE_GENERIC_TRANSFORM_TOOL)70 G_DEFINE_TYPE (GimpPerspectiveTool, gimp_perspective_tool,
71                GIMP_TYPE_GENERIC_TRANSFORM_TOOL)
72 
73 #define parent_class gimp_perspective_tool_parent_class
74 
75 
76 void
77 gimp_perspective_tool_register (GimpToolRegisterCallback  callback,
78                                 gpointer                  data)
79 {
80   (* callback) (GIMP_TYPE_PERSPECTIVE_TOOL,
81                 GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
82                 gimp_transform_grid_options_gui,
83                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
84                 "gimp-perspective-tool",
85                 _("Perspective"),
86                 _("Perspective Tool: "
87                   "Change perspective of the layer, selection or path"),
88                 N_("_Perspective"), "<shift>P",
89                 NULL, GIMP_HELP_TOOL_PERSPECTIVE,
90                 GIMP_ICON_TOOL_PERSPECTIVE,
91                 data);
92 }
93 
94 static void
gimp_perspective_tool_class_init(GimpPerspectiveToolClass * klass)95 gimp_perspective_tool_class_init (GimpPerspectiveToolClass *klass)
96 {
97   GimpTransformToolClass        *tr_class      = GIMP_TRANSFORM_TOOL_CLASS (klass);
98   GimpTransformGridToolClass    *tg_class      = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
99   GimpGenericTransformToolClass *generic_class = GIMP_GENERIC_TRANSFORM_TOOL_CLASS (klass);
100 
101   tg_class->matrix_to_info      = gimp_perspective_tool_matrix_to_info;
102   tg_class->prepare             = gimp_perspective_tool_prepare;
103   tg_class->readjust            = gimp_perspective_tool_readjust;
104   tg_class->get_widget          = gimp_perspective_tool_get_widget;
105   tg_class->update_widget       = gimp_perspective_tool_update_widget;
106   tg_class->widget_changed      = gimp_perspective_tool_widget_changed;
107 
108   generic_class->info_to_points = gimp_perspective_tool_info_to_points;
109 
110   tr_class->undo_desc           = C_("undo-type", "Perspective");
111   tr_class->progress_text       = _("Perspective transformation");
112 }
113 
114 static void
gimp_perspective_tool_init(GimpPerspectiveTool * perspective_tool)115 gimp_perspective_tool_init (GimpPerspectiveTool *perspective_tool)
116 {
117   GimpTool *tool = GIMP_TOOL (perspective_tool);
118 
119   gimp_tool_control_set_tool_cursor (tool->control,
120                                      GIMP_TOOL_CURSOR_PERSPECTIVE);
121 }
122 
123 static void
gimp_perspective_tool_matrix_to_info(GimpTransformGridTool * tg_tool,const GimpMatrix3 * transform)124 gimp_perspective_tool_matrix_to_info (GimpTransformGridTool *tg_tool,
125                                       const GimpMatrix3     *transform)
126 {
127   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
128 
129   gimp_matrix3_transform_point (transform,
130                                 tr_tool->x1,
131                                 tr_tool->y1,
132                                 &tg_tool->trans_info[X0],
133                                 &tg_tool->trans_info[Y0]);
134   gimp_matrix3_transform_point (transform,
135                                 tr_tool->x2,
136                                 tr_tool->y1,
137                                 &tg_tool->trans_info[X1],
138                                 &tg_tool->trans_info[Y1]);
139   gimp_matrix3_transform_point (transform,
140                                 tr_tool->x1,
141                                 tr_tool->y2,
142                                 &tg_tool->trans_info[X2],
143                                 &tg_tool->trans_info[Y2]);
144   gimp_matrix3_transform_point (transform,
145                                 tr_tool->x2,
146                                 tr_tool->y2,
147                                 &tg_tool->trans_info[X3],
148                                 &tg_tool->trans_info[Y3]);
149 }
150 
151 static void
gimp_perspective_tool_prepare(GimpTransformGridTool * tg_tool)152 gimp_perspective_tool_prepare (GimpTransformGridTool *tg_tool)
153 {
154   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
155 
156   GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->prepare (tg_tool);
157 
158   tg_tool->trans_info[X0] = (gdouble) tr_tool->x1;
159   tg_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
160   tg_tool->trans_info[X1] = (gdouble) tr_tool->x2;
161   tg_tool->trans_info[Y1] = (gdouble) tr_tool->y1;
162   tg_tool->trans_info[X2] = (gdouble) tr_tool->x1;
163   tg_tool->trans_info[Y2] = (gdouble) tr_tool->y2;
164   tg_tool->trans_info[X3] = (gdouble) tr_tool->x2;
165   tg_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
166 }
167 
168 static void
gimp_perspective_tool_readjust(GimpTransformGridTool * tg_tool)169 gimp_perspective_tool_readjust (GimpTransformGridTool *tg_tool)
170 {
171   GimpTool         *tool  = GIMP_TOOL (tg_tool);
172   GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
173   gdouble           x;
174   gdouble           y;
175   gdouble           r;
176 
177   x = shell->disp_width  / 2.0;
178   y = shell->disp_height / 2.0;
179   r = MAX (MIN (x, y) / G_SQRT2 -
180            GIMP_TOOL_TRANSFORM_GRID_MAX_HANDLE_SIZE / 2.0,
181            GIMP_TOOL_TRANSFORM_GRID_MAX_HANDLE_SIZE / 2.0);
182 
183   gimp_display_shell_untransform_xy_f (shell,
184                                        x - r, y - r,
185                                        &tg_tool->trans_info[X0],
186                                        &tg_tool->trans_info[Y0]);
187   gimp_display_shell_untransform_xy_f (shell,
188                                        x + r, y - r,
189                                        &tg_tool->trans_info[X1],
190                                        &tg_tool->trans_info[Y1]);
191   gimp_display_shell_untransform_xy_f (shell,
192                                        x - r, y + r,
193                                        &tg_tool->trans_info[X2],
194                                        &tg_tool->trans_info[Y2]);
195   gimp_display_shell_untransform_xy_f (shell,
196                                        x + r, y + r,
197                                        &tg_tool->trans_info[X3],
198                                        &tg_tool->trans_info[Y3]);
199 }
200 
201 static GimpToolWidget *
gimp_perspective_tool_get_widget(GimpTransformGridTool * tg_tool)202 gimp_perspective_tool_get_widget (GimpTransformGridTool *tg_tool)
203 {
204   GimpTool          *tool    = GIMP_TOOL (tg_tool);
205   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
206   GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
207   GimpToolWidget    *widget;
208 
209   widget = gimp_tool_transform_grid_new (shell,
210                                          &tr_tool->transform,
211                                          tr_tool->x1,
212                                          tr_tool->y1,
213                                          tr_tool->x2,
214                                          tr_tool->y2);
215 
216   g_object_set (widget,
217                 "inside-function",         GIMP_TRANSFORM_FUNCTION_PERSPECTIVE,
218                 "outside-function",        GIMP_TRANSFORM_FUNCTION_PERSPECTIVE,
219                 "use-perspective-handles", TRUE,
220                 "use-center-handle",       TRUE,
221                 NULL);
222 
223   return widget;
224 }
225 
226 static void
gimp_perspective_tool_update_widget(GimpTransformGridTool * tg_tool)227 gimp_perspective_tool_update_widget (GimpTransformGridTool *tg_tool)
228 {
229   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
230 
231   GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->update_widget (tg_tool);
232 
233   g_object_set (tg_tool->widget,
234                 "x1", (gdouble) tr_tool->x1,
235                 "y1", (gdouble) tr_tool->y1,
236                 "x2", (gdouble) tr_tool->x2,
237                 "y2", (gdouble) tr_tool->y2,
238                 NULL);
239 }
240 
241 static void
gimp_perspective_tool_widget_changed(GimpTransformGridTool * tg_tool)242 gimp_perspective_tool_widget_changed (GimpTransformGridTool *tg_tool)
243 {
244   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
245   GimpMatrix3       *transform;
246 
247   g_object_get (tg_tool->widget,
248                 "transform", &transform,
249                 NULL);
250 
251   gimp_matrix3_transform_point (transform,
252                                 tr_tool->x1, tr_tool->y1,
253                                 &tg_tool->trans_info[X0],
254                                 &tg_tool->trans_info[Y0]);
255   gimp_matrix3_transform_point (transform,
256                                 tr_tool->x2, tr_tool->y1,
257                                 &tg_tool->trans_info[X1],
258                                 &tg_tool->trans_info[Y1]);
259   gimp_matrix3_transform_point (transform,
260                                 tr_tool->x1, tr_tool->y2,
261                                 &tg_tool->trans_info[X2],
262                                 &tg_tool->trans_info[Y2]);
263   gimp_matrix3_transform_point (transform,
264                                 tr_tool->x2, tr_tool->y2,
265                                 &tg_tool->trans_info[X3],
266                                 &tg_tool->trans_info[Y3]);
267 
268   g_free (transform);
269 
270   GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->widget_changed (tg_tool);
271 }
272 
273 static void
gimp_perspective_tool_info_to_points(GimpGenericTransformTool * generic)274 gimp_perspective_tool_info_to_points (GimpGenericTransformTool *generic)
275 {
276   GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (generic);
277   GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (generic);
278 
279   generic->input_points[0]  = (GimpVector2) {tr_tool->x1, tr_tool->y1};
280   generic->input_points[1]  = (GimpVector2) {tr_tool->x2, tr_tool->y1};
281   generic->input_points[2]  = (GimpVector2) {tr_tool->x1, tr_tool->y2};
282   generic->input_points[3]  = (GimpVector2) {tr_tool->x2, tr_tool->y2};
283 
284   generic->output_points[0] = (GimpVector2) {tg_tool->trans_info[X0],
285                                              tg_tool->trans_info[Y0]};
286   generic->output_points[1] = (GimpVector2) {tg_tool->trans_info[X1],
287                                              tg_tool->trans_info[Y1]};
288   generic->output_points[2] = (GimpVector2) {tg_tool->trans_info[X2],
289                                              tg_tool->trans_info[Y2]};
290   generic->output_points[3] = (GimpVector2) {tg_tool->trans_info[X3],
291                                              tg_tool->trans_info[Y3]};
292 }
293