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