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 <math.h>
21 
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 
27 #include "display-types.h"
28 
29 #include "core/gimpboundary.h"
30 #include "core/gimpdrawable.h"
31 #include "core/gimpimage.h"
32 #include "core/gimp-utils.h"
33 
34 #include "gimpdisplay.h"
35 #include "gimpdisplayshell.h"
36 #include "gimpdisplayshell-scroll.h"
37 #include "gimpdisplayshell-transform.h"
38 
39 
40 /*  local function prototypes  */
41 
42 static void gimp_display_shell_transform_xy_f_noround (GimpDisplayShell *shell,
43                                                        gdouble           x,
44                                                        gdouble           y,
45                                                        gdouble          *nx,
46                                                        gdouble          *ny);
47 
48 /*  public functions  */
49 
50 /**
51  * gimp_display_shell_zoom_coords:
52  * @shell:          a #GimpDisplayShell
53  * @image_coords:   image coordinates
54  * @display_coords: returns the corresponding display coordinates
55  *
56  * Zooms from image coordinates to display coordinates, so that
57  * objects can be rendered at the correct points on the display.
58  **/
59 void
gimp_display_shell_zoom_coords(GimpDisplayShell * shell,const GimpCoords * image_coords,GimpCoords * display_coords)60 gimp_display_shell_zoom_coords (GimpDisplayShell *shell,
61                                 const GimpCoords *image_coords,
62                                 GimpCoords       *display_coords)
63 {
64   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
65   g_return_if_fail (image_coords != NULL);
66   g_return_if_fail (display_coords != NULL);
67 
68   *display_coords = *image_coords;
69 
70   display_coords->x = SCALEX (shell, image_coords->x);
71   display_coords->y = SCALEY (shell, image_coords->y);
72 
73   display_coords->x -= shell->offset_x;
74   display_coords->y -= shell->offset_y;
75 }
76 
77 /**
78  * gimp_display_shell_unzoom_coords:
79  * @shell:          a #GimpDisplayShell
80  * @display_coords: display coordinates
81  * @image_coords:   returns the corresponding image coordinates
82  *
83  * Zooms from display coordinates to image coordinates, so that
84  * points on the display can be mapped to points in the image.
85  **/
86 void
gimp_display_shell_unzoom_coords(GimpDisplayShell * shell,const GimpCoords * display_coords,GimpCoords * image_coords)87 gimp_display_shell_unzoom_coords (GimpDisplayShell *shell,
88                                   const GimpCoords *display_coords,
89                                   GimpCoords       *image_coords)
90 {
91   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
92   g_return_if_fail (display_coords != NULL);
93   g_return_if_fail (image_coords != NULL);
94 
95   *image_coords = *display_coords;
96 
97   image_coords->x += shell->offset_x;
98   image_coords->y += shell->offset_y;
99 
100   image_coords->x /= shell->scale_x;
101   image_coords->y /= shell->scale_y;
102 }
103 
104 /**
105  * gimp_display_shell_zoom_xy:
106  * @shell:
107  * @x:
108  * @y:
109  * @nx:
110  * @ny:
111  *
112  * Zooms an image coordinate to a shell coordinate.
113  **/
114 void
gimp_display_shell_zoom_xy(GimpDisplayShell * shell,gdouble x,gdouble y,gint * nx,gint * ny)115 gimp_display_shell_zoom_xy (GimpDisplayShell *shell,
116                             gdouble           x,
117                             gdouble           y,
118                             gint             *nx,
119                             gint             *ny)
120 {
121   gint64 tx;
122   gint64 ty;
123 
124   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
125   g_return_if_fail (nx != NULL);
126   g_return_if_fail (ny != NULL);
127 
128   tx = x * shell->scale_x;
129   ty = y * shell->scale_y;
130 
131   tx -= shell->offset_x;
132   ty -= shell->offset_y;
133 
134   /*  The projected coordinates might overflow a gint in the case of
135    *  big images at high zoom levels, so we clamp them here to avoid
136    *  problems.
137    */
138   *nx = CLAMP (tx, G_MININT, G_MAXINT);
139   *ny = CLAMP (ty, G_MININT, G_MAXINT);
140 }
141 
142 /**
143  * gimp_display_shell_unzoom_xy:
144  * @shell:       a #GimpDisplayShell
145  * @x:           x coordinate in display coordinates
146  * @y:           y coordinate in display coordinates
147  * @nx:          returns x oordinate in image coordinates
148  * @ny:          returns y coordinate in image coordinates
149  * @round:       if %TRUE, round the results to the nearest integer;
150  *               if %FALSE, simply cast them to @gint.
151  *
152  * Zoom from display coordinates to image coordinates, so that
153  * points on the display can be mapped to the corresponding points
154  * in the image.
155  **/
156 void
gimp_display_shell_unzoom_xy(GimpDisplayShell * shell,gint x,gint y,gint * nx,gint * ny,gboolean round)157 gimp_display_shell_unzoom_xy (GimpDisplayShell *shell,
158                               gint              x,
159                               gint              y,
160                               gint             *nx,
161                               gint             *ny,
162                               gboolean          round)
163 {
164   gint64 tx;
165   gint64 ty;
166 
167   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
168   g_return_if_fail (nx != NULL);
169   g_return_if_fail (ny != NULL);
170 
171   if (round)
172     {
173       tx = SIGNED_ROUND (((gdouble) x + shell->offset_x) / shell->scale_x);
174       ty = SIGNED_ROUND (((gdouble) y + shell->offset_y) / shell->scale_y);
175     }
176   else
177     {
178       tx = ((gint64) x + shell->offset_x) / shell->scale_x;
179       ty = ((gint64) y + shell->offset_y) / shell->scale_y;
180     }
181 
182   *nx = CLAMP (tx, G_MININT, G_MAXINT);
183   *ny = CLAMP (ty, G_MININT, G_MAXINT);
184 }
185 
186 /**
187  * gimp_display_shell_zoom_xy_f:
188  * @shell: a #GimpDisplayShell
189  * @x:     image x coordinate of point
190  * @y:     image y coordinate of point
191  * @nx:    returned shell canvas x coordinate
192  * @ny:    returned shell canvas y coordinate
193  *
194  * Zooms from image coordinates to display shell canvas
195  * coordinates.
196  **/
197 void
gimp_display_shell_zoom_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)198 gimp_display_shell_zoom_xy_f (GimpDisplayShell *shell,
199                               gdouble           x,
200                               gdouble           y,
201                               gdouble          *nx,
202                               gdouble          *ny)
203 {
204   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
205   g_return_if_fail (nx != NULL);
206   g_return_if_fail (ny != NULL);
207 
208   *nx = SCALEX (shell, x) - shell->offset_x;
209   *ny = SCALEY (shell, y) - shell->offset_y;
210 }
211 
212 /**
213  * gimp_display_shell_unzoom_xy_f:
214  * @shell:       a #GimpDisplayShell
215  * @x:           x coordinate in display coordinates
216  * @y:           y coordinate in display coordinates
217  * @nx:          place to return x coordinate in image coordinates
218  * @ny:          place to return y coordinate in image coordinates
219  *
220  * This function is identical to gimp_display_shell_unzoom_xy(),
221  * except that the input and output coordinates are doubles rather than
222  * ints, and consequently there is no option related to rounding.
223  **/
224 void
gimp_display_shell_unzoom_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)225 gimp_display_shell_unzoom_xy_f (GimpDisplayShell *shell,
226                                 gdouble           x,
227                                 gdouble           y,
228                                 gdouble          *nx,
229                                 gdouble          *ny)
230 {
231   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
232   g_return_if_fail (nx != NULL);
233   g_return_if_fail (ny != NULL);
234 
235   *nx = (x + shell->offset_x) / shell->scale_x;
236   *ny = (y + shell->offset_y) / shell->scale_y;
237 }
238 
239 /**
240  * gimp_display_shell_zoom_segments:
241  * @shell:       a #GimpDisplayShell
242  * @src_segs:    array of segments in image coordinates
243  * @dest_segs:   returns the corresponding segments in display coordinates
244  * @n_segs:      number of segments
245  *
246  * Zooms from image coordinates to display coordinates, so that
247  * objects can be rendered at the correct points on the display.
248  **/
249 void
gimp_display_shell_zoom_segments(GimpDisplayShell * shell,const GimpBoundSeg * src_segs,GimpSegment * dest_segs,gint n_segs,gdouble offset_x,gdouble offset_y)250 gimp_display_shell_zoom_segments (GimpDisplayShell   *shell,
251                                   const GimpBoundSeg *src_segs,
252                                   GimpSegment        *dest_segs,
253                                   gint                n_segs,
254                                   gdouble             offset_x,
255                                   gdouble             offset_y)
256 {
257   gint i;
258 
259   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
260 
261   for (i = 0; i < n_segs ; i++)
262     {
263       gdouble x1, x2;
264       gdouble y1, y2;
265 
266       x1 = src_segs[i].x1 + offset_x;
267       x2 = src_segs[i].x2 + offset_x;
268       y1 = src_segs[i].y1 + offset_y;
269       y2 = src_segs[i].y2 + offset_y;
270 
271       dest_segs[i].x1 = SCALEX (shell, x1) - shell->offset_x;
272       dest_segs[i].x2 = SCALEX (shell, x2) - shell->offset_x;
273       dest_segs[i].y1 = SCALEY (shell, y1) - shell->offset_y;
274       dest_segs[i].y2 = SCALEY (shell, y2) - shell->offset_y;
275     }
276 }
277 
278 /**
279  * gimp_display_shell_rotate_coords:
280  * @shell:          a #GimpDisplayShell
281  * @image_coords:   unrotated display coordinates
282  * @display_coords: returns the corresponding rotated display coordinates
283  *
284  * Rotates from unrotated display coordinates to rotated display
285  * coordinates, so that objects can be rendered at the correct points
286  * on the display.
287  **/
288 void
gimp_display_shell_rotate_coords(GimpDisplayShell * shell,const GimpCoords * unrotated_coords,GimpCoords * rotated_coords)289 gimp_display_shell_rotate_coords (GimpDisplayShell *shell,
290                                   const GimpCoords *unrotated_coords,
291                                   GimpCoords       *rotated_coords)
292 {
293   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
294   g_return_if_fail (unrotated_coords != NULL);
295   g_return_if_fail (rotated_coords != NULL);
296 
297   *rotated_coords = *unrotated_coords;
298 
299   if (shell->rotate_transform)
300     cairo_matrix_transform_point (shell->rotate_transform,
301                                   &rotated_coords->x,
302                                   &rotated_coords->y);
303 }
304 
305 /**
306  * gimp_display_shell_unrotate_coords:
307  * @shell:          a #GimpDisplayShell
308  * @display_coords: rotated display coordinates
309  * @image_coords:   returns the corresponding unrotated display coordinates
310  *
311  * Rotates from rotated display coordinates to unrotated display coordinates.
312  **/
313 void
gimp_display_shell_unrotate_coords(GimpDisplayShell * shell,const GimpCoords * rotated_coords,GimpCoords * unrotated_coords)314 gimp_display_shell_unrotate_coords (GimpDisplayShell *shell,
315                                     const GimpCoords *rotated_coords,
316                                     GimpCoords       *unrotated_coords)
317 {
318   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
319   g_return_if_fail (rotated_coords != NULL);
320   g_return_if_fail (unrotated_coords != NULL);
321 
322   *unrotated_coords = *rotated_coords;
323 
324   if (shell->rotate_untransform)
325     cairo_matrix_transform_point (shell->rotate_untransform,
326                                   &unrotated_coords->x,
327                                   &unrotated_coords->y);
328 }
329 
330 /**
331  * gimp_display_shell_rotate_xy:
332  * @shell:
333  * @x:
334  * @y:
335  * @nx:
336  * @ny:
337  *
338  * Rotates an unrotated display coordinate to a rotated shell coordinate.
339  **/
340 void
gimp_display_shell_rotate_xy(GimpDisplayShell * shell,gdouble x,gdouble y,gint * nx,gint * ny)341 gimp_display_shell_rotate_xy (GimpDisplayShell *shell,
342                               gdouble           x,
343                               gdouble           y,
344                               gint             *nx,
345                               gint             *ny)
346 {
347   gint64 tx;
348   gint64 ty;
349 
350   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
351   g_return_if_fail (nx != NULL);
352   g_return_if_fail (ny != NULL);
353 
354   if (shell->rotate_transform)
355     cairo_matrix_transform_point (shell->rotate_transform, &x, &y);
356 
357   tx = x;
358   ty = y;
359 
360   /*  The projected coordinates might overflow a gint in the case of
361    *  big images at high zoom levels, so we clamp them here to avoid
362    *  problems.
363    */
364   *nx = CLAMP (tx, G_MININT, G_MAXINT);
365   *ny = CLAMP (ty, G_MININT, G_MAXINT);
366 }
367 
368 /**
369  * gimp_display_shell_unrotate_xy:
370  * @shell:       a #GimpDisplayShell
371  * @x:           x coordinate in rotated display coordinates
372  * @y:           y coordinate in rotated display coordinates
373  * @nx:          returns x oordinate in unrotated display coordinates
374  * @ny:          returns y coordinate in unrotated display coordinates
375  *
376  * Rotate from rotated display coordinates to unrotated display
377  * coordinates.
378  **/
379 void
gimp_display_shell_unrotate_xy(GimpDisplayShell * shell,gint x,gint y,gint * nx,gint * ny)380 gimp_display_shell_unrotate_xy (GimpDisplayShell *shell,
381                                 gint              x,
382                                 gint              y,
383                                 gint             *nx,
384                                 gint             *ny)
385 {
386   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
387   g_return_if_fail (nx != NULL);
388   g_return_if_fail (ny != NULL);
389 
390   if (shell->rotate_untransform)
391     {
392       gdouble fx = x;
393       gdouble fy = y;
394 
395       cairo_matrix_transform_point (shell->rotate_untransform, &fx, &fy);
396 
397       *nx = CLAMP (fx, G_MININT, G_MAXINT);
398       *ny = CLAMP (fy, G_MININT, G_MAXINT);
399     }
400   else
401     {
402       *nx = x;
403       *ny = y;
404     }
405 }
406 
407 /**
408  * gimp_display_shell_rotate_xy_f:
409  * @shell: a #GimpDisplayShell
410  * @x:     image x coordinate of point
411  * @y:     image y coordinate of point
412  * @nx:    returned shell canvas x coordinate
413  * @ny:    returned shell canvas y coordinate
414  *
415  * Rotates from untransformed display coordinates to rotated display
416  * coordinates.
417  **/
418 void
gimp_display_shell_rotate_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)419 gimp_display_shell_rotate_xy_f (GimpDisplayShell *shell,
420                                 gdouble           x,
421                                 gdouble           y,
422                                 gdouble          *nx,
423                                 gdouble          *ny)
424 {
425   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
426   g_return_if_fail (nx != NULL);
427   g_return_if_fail (ny != NULL);
428 
429   *nx = x;
430   *ny = y;
431 
432   if (shell->rotate_transform)
433     cairo_matrix_transform_point (shell->rotate_transform, nx, ny);
434 }
435 
436 /**
437  * gimp_display_shell_unrotate_xy_f:
438  * @shell:       a #GimpDisplayShell
439  * @x:           x coordinate in rotated display coordinates
440  * @y:           y coordinate in rotated display coordinates
441  * @nx:          place to return x coordinate in unrotated display coordinates
442  * @ny:          place to return y coordinate in unrotated display  coordinates
443  *
444  * This function is identical to gimp_display_shell_unrotate_xy(),
445  * except that the input and output coordinates are doubles rather
446  * than ints.
447  **/
448 void
gimp_display_shell_unrotate_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)449 gimp_display_shell_unrotate_xy_f (GimpDisplayShell *shell,
450                                   gdouble           x,
451                                   gdouble           y,
452                                   gdouble          *nx,
453                                   gdouble          *ny)
454 {
455   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
456   g_return_if_fail (nx != NULL);
457   g_return_if_fail (ny != NULL);
458 
459   *nx = x;
460   *ny = y;
461 
462   if (shell->rotate_untransform)
463     cairo_matrix_transform_point (shell->rotate_untransform, nx, ny);
464 }
465 
466 void
gimp_display_shell_rotate_bounds(GimpDisplayShell * shell,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)467 gimp_display_shell_rotate_bounds (GimpDisplayShell *shell,
468                                   gdouble           x1,
469                                   gdouble           y1,
470                                   gdouble           x2,
471                                   gdouble           y2,
472                                   gdouble          *nx1,
473                                   gdouble          *ny1,
474                                   gdouble          *nx2,
475                                   gdouble          *ny2)
476 {
477 
478   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
479 
480   if (shell->rotate_transform)
481     {
482       gdouble tx1 = x1;
483       gdouble ty1 = y1;
484       gdouble tx2 = x1;
485       gdouble ty2 = y2;
486       gdouble tx3 = x2;
487       gdouble ty3 = y1;
488       gdouble tx4 = x2;
489       gdouble ty4 = y2;
490 
491       cairo_matrix_transform_point (shell->rotate_transform, &tx1, &ty1);
492       cairo_matrix_transform_point (shell->rotate_transform, &tx2, &ty2);
493       cairo_matrix_transform_point (shell->rotate_transform, &tx3, &ty3);
494       cairo_matrix_transform_point (shell->rotate_transform, &tx4, &ty4);
495 
496       *nx1 = MIN4 (tx1, tx2, tx3, tx4);
497       *ny1 = MIN4 (ty1, ty2, ty3, ty4);
498       *nx2 = MAX4 (tx1, tx2, tx3, tx4);
499       *ny2 = MAX4 (ty1, ty2, ty3, ty4);
500     }
501   else
502     {
503       *nx1 = x1;
504       *ny1 = y1;
505       *nx2 = x2;
506       *ny2 = y2;
507     }
508 }
509 
510 void
gimp_display_shell_unrotate_bounds(GimpDisplayShell * shell,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)511 gimp_display_shell_unrotate_bounds (GimpDisplayShell *shell,
512                                     gdouble           x1,
513                                     gdouble           y1,
514                                     gdouble           x2,
515                                     gdouble           y2,
516                                     gdouble          *nx1,
517                                     gdouble          *ny1,
518                                     gdouble          *nx2,
519                                     gdouble          *ny2)
520 {
521   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
522 
523   if (shell->rotate_untransform)
524     {
525       gdouble tx1 = x1;
526       gdouble ty1 = y1;
527       gdouble tx2 = x1;
528       gdouble ty2 = y2;
529       gdouble tx3 = x2;
530       gdouble ty3 = y1;
531       gdouble tx4 = x2;
532       gdouble ty4 = y2;
533 
534       cairo_matrix_transform_point (shell->rotate_untransform, &tx1, &ty1);
535       cairo_matrix_transform_point (shell->rotate_untransform, &tx2, &ty2);
536       cairo_matrix_transform_point (shell->rotate_untransform, &tx3, &ty3);
537       cairo_matrix_transform_point (shell->rotate_untransform, &tx4, &ty4);
538 
539       *nx1 = MIN4 (tx1, tx2, tx3, tx4);
540       *ny1 = MIN4 (ty1, ty2, ty3, ty4);
541       *nx2 = MAX4 (tx1, tx2, tx3, tx4);
542       *ny2 = MAX4 (ty1, ty2, ty3, ty4);
543     }
544   else
545     {
546       *nx1 = x1;
547       *ny1 = y1;
548       *nx2 = x2;
549       *ny2 = y2;
550     }
551 }
552 
553 /**
554  * gimp_display_shell_transform_coords:
555  * @shell:          a #GimpDisplayShell
556  * @image_coords:   image coordinates
557  * @display_coords: returns the corresponding display coordinates
558  *
559  * Transforms from image coordinates to display coordinates, so that
560  * objects can be rendered at the correct points on the display.
561  **/
562 void
gimp_display_shell_transform_coords(GimpDisplayShell * shell,const GimpCoords * image_coords,GimpCoords * display_coords)563 gimp_display_shell_transform_coords (GimpDisplayShell *shell,
564                                      const GimpCoords *image_coords,
565                                      GimpCoords       *display_coords)
566 {
567   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
568   g_return_if_fail (image_coords != NULL);
569   g_return_if_fail (display_coords != NULL);
570 
571   *display_coords = *image_coords;
572 
573   display_coords->x = SCALEX (shell, image_coords->x);
574   display_coords->y = SCALEY (shell, image_coords->y);
575 
576   display_coords->x -= shell->offset_x;
577   display_coords->y -= shell->offset_y;
578 
579   if (shell->rotate_transform)
580     cairo_matrix_transform_point (shell->rotate_transform,
581                                   &display_coords->x,
582                                   &display_coords->y);
583 }
584 
585 /**
586  * gimp_display_shell_untransform_coords:
587  * @shell:          a #GimpDisplayShell
588  * @display_coords: display coordinates
589  * @image_coords:   returns the corresponding image coordinates
590  *
591  * Transforms from display coordinates to image coordinates, so that
592  * points on the display can be mapped to points in the image.
593  **/
594 void
gimp_display_shell_untransform_coords(GimpDisplayShell * shell,const GimpCoords * display_coords,GimpCoords * image_coords)595 gimp_display_shell_untransform_coords (GimpDisplayShell *shell,
596                                        const GimpCoords *display_coords,
597                                        GimpCoords       *image_coords)
598 {
599   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
600   g_return_if_fail (display_coords != NULL);
601   g_return_if_fail (image_coords != NULL);
602 
603   *image_coords = *display_coords;
604 
605   if (shell->rotate_untransform)
606     cairo_matrix_transform_point (shell->rotate_untransform,
607                                   &image_coords->x,
608                                   &image_coords->y);
609 
610   image_coords->x += shell->offset_x;
611   image_coords->y += shell->offset_y;
612 
613   image_coords->x /= shell->scale_x;
614   image_coords->y /= shell->scale_y;
615 
616   image_coords->xscale  = shell->scale_x;
617   image_coords->yscale  = shell->scale_y;
618   image_coords->angle   = shell->rotate_angle / 360.0;
619   image_coords->reflect = shell->flip_horizontally ^ shell->flip_vertically;
620 
621   if (shell->flip_vertically)
622     image_coords->angle += 0.5;
623 }
624 
625 /**
626  * gimp_display_shell_transform_xy:
627  * @shell:
628  * @x:
629  * @y:
630  * @nx:
631  * @ny:
632  *
633  * Transforms an image coordinate to a shell coordinate.
634  **/
635 void
gimp_display_shell_transform_xy(GimpDisplayShell * shell,gdouble x,gdouble y,gint * nx,gint * ny)636 gimp_display_shell_transform_xy (GimpDisplayShell *shell,
637                                  gdouble           x,
638                                  gdouble           y,
639                                  gint             *nx,
640                                  gint             *ny)
641 {
642   gint64 tx;
643   gint64 ty;
644 
645   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
646   g_return_if_fail (nx != NULL);
647   g_return_if_fail (ny != NULL);
648 
649   tx = x * shell->scale_x;
650   ty = y * shell->scale_y;
651 
652   tx -= shell->offset_x;
653   ty -= shell->offset_y;
654 
655   if (shell->rotate_transform)
656     {
657       gdouble fx = tx;
658       gdouble fy = ty;
659 
660       cairo_matrix_transform_point (shell->rotate_transform, &fx, &fy);
661 
662       tx = fx;
663       ty = fy;
664     }
665 
666   /*  The projected coordinates might overflow a gint in the case of
667    *  big images at high zoom levels, so we clamp them here to avoid
668    *  problems.
669    */
670   *nx = CLAMP (tx, G_MININT, G_MAXINT);
671   *ny = CLAMP (ty, G_MININT, G_MAXINT);
672 }
673 
674 /**
675  * gimp_display_shell_untransform_xy:
676  * @shell:       a #GimpDisplayShell
677  * @x:           x coordinate in display coordinates
678  * @y:           y coordinate in display coordinates
679  * @nx:          returns x oordinate in image coordinates
680  * @ny:          returns y coordinate in image coordinates
681  * @round:       if %TRUE, round the results to the nearest integer;
682  *               if %FALSE, simply cast them to @gint.
683  *
684  * Transform from display coordinates to image coordinates, so that
685  * points on the display can be mapped to the corresponding points
686  * in the image.
687  **/
688 void
gimp_display_shell_untransform_xy(GimpDisplayShell * shell,gint x,gint y,gint * nx,gint * ny,gboolean round)689 gimp_display_shell_untransform_xy (GimpDisplayShell *shell,
690                                    gint              x,
691                                    gint              y,
692                                    gint             *nx,
693                                    gint             *ny,
694                                    gboolean          round)
695 {
696   gint64 tx;
697   gint64 ty;
698 
699   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
700   g_return_if_fail (nx != NULL);
701   g_return_if_fail (ny != NULL);
702 
703   if (shell->rotate_untransform)
704     {
705       gdouble fx = x;
706       gdouble fy = y;
707 
708       cairo_matrix_transform_point (shell->rotate_untransform, &fx, &fy);
709 
710       x = fx;
711       y = fy;
712     }
713 
714   if (round)
715     {
716       tx = SIGNED_ROUND (((gdouble) x + shell->offset_x) / shell->scale_x);
717       ty = SIGNED_ROUND (((gdouble) y + shell->offset_y) / shell->scale_y);
718     }
719   else
720     {
721       tx = ((gint64) x + shell->offset_x) / shell->scale_x;
722       ty = ((gint64) y + shell->offset_y) / shell->scale_y;
723     }
724 
725   *nx = CLAMP (tx, G_MININT, G_MAXINT);
726   *ny = CLAMP (ty, G_MININT, G_MAXINT);
727 }
728 
729 /**
730  * gimp_display_shell_transform_xy_f:
731  * @shell: a #GimpDisplayShell
732  * @x:     image x coordinate of point
733  * @y:     image y coordinate of point
734  * @nx:    returned shell canvas x coordinate
735  * @ny:    returned shell canvas y coordinate
736  *
737  * Transforms from image coordinates to display shell canvas
738  * coordinates.
739  **/
740 void
gimp_display_shell_transform_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)741 gimp_display_shell_transform_xy_f (GimpDisplayShell *shell,
742                                    gdouble           x,
743                                    gdouble           y,
744                                    gdouble          *nx,
745                                    gdouble          *ny)
746 {
747   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
748   g_return_if_fail (nx != NULL);
749   g_return_if_fail (ny != NULL);
750 
751   *nx = SCALEX (shell, x) - shell->offset_x;
752   *ny = SCALEY (shell, y) - shell->offset_y;
753 
754   if (shell->rotate_transform)
755     cairo_matrix_transform_point (shell->rotate_transform, nx, ny);
756 }
757 
758 /**
759  * gimp_display_shell_untransform_xy_f:
760  * @shell:       a #GimpDisplayShell
761  * @x:           x coordinate in display coordinates
762  * @y:           y coordinate in display coordinates
763  * @nx:          place to return x coordinate in image coordinates
764  * @ny:          place to return y coordinate in image coordinates
765  *
766  * This function is identical to gimp_display_shell_untransform_xy(),
767  * except that the input and output coordinates are doubles rather than
768  * ints, and consequently there is no option related to rounding.
769  **/
770 void
gimp_display_shell_untransform_xy_f(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)771 gimp_display_shell_untransform_xy_f (GimpDisplayShell *shell,
772                                      gdouble           x,
773                                      gdouble           y,
774                                      gdouble          *nx,
775                                      gdouble          *ny)
776 {
777   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
778   g_return_if_fail (nx != NULL);
779   g_return_if_fail (ny != NULL);
780 
781   if (shell->rotate_untransform)
782     cairo_matrix_transform_point (shell->rotate_untransform, &x, &y);
783 
784   *nx = (x + shell->offset_x) / shell->scale_x;
785   *ny = (y + shell->offset_y) / shell->scale_y;
786 }
787 
788 void
gimp_display_shell_transform_bounds(GimpDisplayShell * shell,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)789 gimp_display_shell_transform_bounds (GimpDisplayShell *shell,
790                                      gdouble           x1,
791                                      gdouble           y1,
792                                      gdouble           x2,
793                                      gdouble           y2,
794                                      gdouble          *nx1,
795                                      gdouble          *ny1,
796                                      gdouble          *nx2,
797                                      gdouble          *ny2)
798 {
799   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
800   g_return_if_fail (nx1 != NULL);
801   g_return_if_fail (ny1 != NULL);
802   g_return_if_fail (nx2 != NULL);
803   g_return_if_fail (ny2 != NULL);
804 
805   if (shell->rotate_transform)
806     {
807       gdouble tx1, ty1;
808       gdouble tx2, ty2;
809       gdouble tx3, ty3;
810       gdouble tx4, ty4;
811 
812       gimp_display_shell_transform_xy_f_noround (shell, x1, y1, &tx1, &ty1);
813       gimp_display_shell_transform_xy_f_noround (shell, x1, y2, &tx2, &ty2);
814       gimp_display_shell_transform_xy_f_noround (shell, x2, y1, &tx3, &ty3);
815       gimp_display_shell_transform_xy_f_noround (shell, x2, y2, &tx4, &ty4);
816 
817       *nx1 = MIN4 (tx1, tx2, tx3, tx4);
818       *ny1 = MIN4 (ty1, ty2, ty3, ty4);
819       *nx2 = MAX4 (tx1, tx2, tx3, tx4);
820       *ny2 = MAX4 (ty1, ty2, ty3, ty4);
821     }
822   else
823     {
824       gimp_display_shell_transform_xy_f_noround (shell, x1, y1, nx1, ny1);
825       gimp_display_shell_transform_xy_f_noround (shell, x2, y2, nx2, ny2);
826     }
827 }
828 
829 void
gimp_display_shell_untransform_bounds(GimpDisplayShell * shell,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)830 gimp_display_shell_untransform_bounds (GimpDisplayShell *shell,
831                                        gdouble           x1,
832                                        gdouble           y1,
833                                        gdouble           x2,
834                                        gdouble           y2,
835                                        gdouble          *nx1,
836                                        gdouble          *ny1,
837                                        gdouble          *nx2,
838                                        gdouble          *ny2)
839 {
840   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
841   g_return_if_fail (nx1 != NULL);
842   g_return_if_fail (ny1 != NULL);
843   g_return_if_fail (nx2 != NULL);
844   g_return_if_fail (ny2 != NULL);
845 
846   if (shell->rotate_untransform)
847     {
848       gdouble tx1, ty1;
849       gdouble tx2, ty2;
850       gdouble tx3, ty3;
851       gdouble tx4, ty4;
852 
853       gimp_display_shell_untransform_xy_f (shell, x1, y1, &tx1, &ty1);
854       gimp_display_shell_untransform_xy_f (shell, x1, y2, &tx2, &ty2);
855       gimp_display_shell_untransform_xy_f (shell, x2, y1, &tx3, &ty3);
856       gimp_display_shell_untransform_xy_f (shell, x2, y2, &tx4, &ty4);
857 
858       *nx1 = MIN4 (tx1, tx2, tx3, tx4);
859       *ny1 = MIN4 (ty1, ty2, ty3, ty4);
860       *nx2 = MAX4 (tx1, tx2, tx3, tx4);
861       *ny2 = MAX4 (ty1, ty2, ty3, ty4);
862     }
863   else
864     {
865       gimp_display_shell_untransform_xy_f (shell, x1, y1, nx1, ny1);
866       gimp_display_shell_untransform_xy_f (shell, x2, y2, nx2, ny2);
867     }
868 }
869 
870 /* transforms a bounding box from image-space, uniformly scaled by a factor of
871  * 'scale', to display-space.  this is equivalent to, but more accurate than,
872  * dividing the input by 'scale', and using
873  * gimp_display_shell_transform_bounds(), in particular, in that if 'scale'
874  * equals 'shell->scale_x' or 'shell->scale_y', there is no loss in accuracy
875  * in the corresponding dimension due to scaling (although there might be loss
876  * of accuracy due to rotation or translation.)
877  */
878 void
gimp_display_shell_transform_bounds_with_scale(GimpDisplayShell * shell,gdouble scale,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)879 gimp_display_shell_transform_bounds_with_scale (GimpDisplayShell *shell,
880                                                 gdouble           scale,
881                                                 gdouble           x1,
882                                                 gdouble           y1,
883                                                 gdouble           x2,
884                                                 gdouble           y2,
885                                                 gdouble          *nx1,
886                                                 gdouble          *ny1,
887                                                 gdouble          *nx2,
888                                                 gdouble          *ny2)
889 {
890   gdouble factor_x;
891   gdouble factor_y;
892 
893   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
894   g_return_if_fail (scale > 0.0);
895   g_return_if_fail (nx1 != NULL);
896   g_return_if_fail (ny1 != NULL);
897   g_return_if_fail (nx2 != NULL);
898   g_return_if_fail (ny2 != NULL);
899 
900   factor_x = shell->scale_x / scale;
901   factor_y = shell->scale_y / scale;
902 
903   x1 = x1 * factor_x - shell->offset_x;
904   y1 = y1 * factor_y - shell->offset_y;
905   x2 = x2 * factor_x - shell->offset_x;
906   y2 = y2 * factor_y - shell->offset_y;
907 
908   gimp_display_shell_rotate_bounds (shell,
909                                     x1,  y1,  x2,  y2,
910                                     nx1, ny1, nx2, ny2);
911 }
912 
913 /* transforms a bounding box from display-space to image-space, uniformly
914  * scaled by a factor of 'scale'.  this is equivalent to, but more accurate
915  * than, using gimp_display_shell_untransform_bounds(), and multiplying the
916  * output by 'scale', in particular, in that if 'scale' equals 'shell->scale_x'
917  * or 'shell->scale_y', there is no loss in accuracy in the corresponding
918  * dimension due to scaling (although there might be loss of accuracy due to
919  * rotation or translation.)
920  */
921 void
gimp_display_shell_untransform_bounds_with_scale(GimpDisplayShell * shell,gdouble scale,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * nx1,gdouble * ny1,gdouble * nx2,gdouble * ny2)922 gimp_display_shell_untransform_bounds_with_scale (GimpDisplayShell *shell,
923                                                   gdouble           scale,
924                                                   gdouble           x1,
925                                                   gdouble           y1,
926                                                   gdouble           x2,
927                                                   gdouble           y2,
928                                                   gdouble          *nx1,
929                                                   gdouble          *ny1,
930                                                   gdouble          *nx2,
931                                                   gdouble          *ny2)
932 {
933   gdouble factor_x;
934   gdouble factor_y;
935 
936   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
937   g_return_if_fail (scale > 0.0);
938   g_return_if_fail (nx1 != NULL);
939   g_return_if_fail (ny1 != NULL);
940   g_return_if_fail (nx2 != NULL);
941   g_return_if_fail (ny2 != NULL);
942 
943   factor_x = scale / shell->scale_x;
944   factor_y = scale / shell->scale_y;
945 
946   gimp_display_shell_unrotate_bounds (shell,
947                                       x1,  y1,  x2,  y2,
948                                       nx1, ny1, nx2, ny2);
949 
950   *nx1 = (*nx1 + shell->offset_x) * factor_x;
951   *ny1 = (*ny1 + shell->offset_y) * factor_y;
952   *nx2 = (*nx2 + shell->offset_x) * factor_x;
953   *ny2 = (*ny2 + shell->offset_y) * factor_y;
954 }
955 
956 /**
957  * gimp_display_shell_untransform_viewport:
958  * @shell:  a #GimpDisplayShell
959  * @clip:   whether to clip the result to the image bounds
960  * @x:      returns image x coordinate of display upper left corner
961  * @y:      returns image y coordinate of display upper left corner
962  * @width:  returns width of display measured in image coordinates
963  * @height: returns height of display measured in image coordinates
964  *
965  * This function calculates the part of the image, in image coordinates,
966  * that corresponds to the display viewport.
967  **/
968 void
gimp_display_shell_untransform_viewport(GimpDisplayShell * shell,gboolean clip,gint * x,gint * y,gint * width,gint * height)969 gimp_display_shell_untransform_viewport (GimpDisplayShell *shell,
970                                          gboolean          clip,
971                                          gint             *x,
972                                          gint             *y,
973                                          gint             *width,
974                                          gint             *height)
975 {
976   gdouble x1, y1, x2, y2;
977 
978   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
979 
980   gimp_display_shell_untransform_bounds (shell,
981                                          0, 0,
982                                          shell->disp_width, shell->disp_height,
983                                          &x1, &y1,
984                                          &x2, &y2);
985 
986   x1 = floor (x1);
987   y1 = floor (y1);
988   x2 = ceil (x2);
989   y2 = ceil (y2);
990 
991   if (clip)
992     {
993       GimpImage *image = gimp_display_get_image (shell->display);
994 
995       x1 = MAX (x1, 0);
996       y1 = MAX (y1, 0);
997       x2 = MIN (x2, gimp_image_get_width  (image));
998       y2 = MIN (y2, gimp_image_get_height (image));
999     }
1000 
1001   if (x)      *x      = x1;
1002   if (y)      *y      = y1;
1003   if (width)  *width  = x2 - x1;
1004   if (height) *height = y2 - y1;
1005 }
1006 
1007 
1008 /*  private functions  */
1009 
1010 /* Same as gimp_display_shell_transform_xy_f(), but doesn't do any rounding
1011  * for the transformed coordinates.
1012  */
1013 static void
gimp_display_shell_transform_xy_f_noround(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble * nx,gdouble * ny)1014 gimp_display_shell_transform_xy_f_noround (GimpDisplayShell *shell,
1015                                            gdouble           x,
1016                                            gdouble           y,
1017                                            gdouble          *nx,
1018                                            gdouble          *ny)
1019 {
1020   *nx = shell->scale_x * x - shell->offset_x;
1021   *ny = shell->scale_y * y - shell->offset_y;
1022 
1023   if (shell->rotate_transform)
1024     cairo_matrix_transform_point (shell->rotate_transform, nx, ny);
1025 }
1026