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 "libgimpbase/gimpbase.h"
26 #include "libgimpmath/gimpmath.h"
27 #include "libgimpwidgets/gimpwidgets.h"
28
29 #include "display-types.h"
30
31 #include "config/gimpguiconfig.h"
32
33 #include "core/gimp.h"
34 #include "core/gimpimage.h"
35
36 #include "gimpdisplay.h"
37 #include "gimpdisplayshell.h"
38 #include "gimpdisplayshell-expose.h"
39 #include "gimpdisplayshell-rotate.h"
40 #include "gimpdisplayshell-scale.h"
41 #include "gimpdisplayshell-scroll.h"
42 #include "gimpdisplayshell-transform.h"
43 #include "gimpimagewindow.h"
44
45
46 #define SCALE_TIMEOUT 2
47 #define SCALE_EPSILON 0.0001
48 #define ALMOST_CENTERED_THRESHOLD 2
49
50 #define SCALE_EQUALS(a,b) (fabs ((a) - (b)) < SCALE_EPSILON)
51
52
53 /* local function prototypes */
54
55 static void gimp_display_shell_scale_get_screen_resolution
56 (GimpDisplayShell *shell,
57 gdouble *xres,
58 gdouble *yres);
59 static void gimp_display_shell_scale_get_image_size_for_scale
60 (GimpDisplayShell *shell,
61 gdouble scale,
62 gint *w,
63 gint *h);
64 static void gimp_display_shell_calculate_scale_x_and_y
65 (GimpDisplayShell *shell,
66 gdouble scale,
67 gdouble *scale_x,
68 gdouble *scale_y);
69
70 static void gimp_display_shell_scale_to (GimpDisplayShell *shell,
71 gdouble scale,
72 gdouble viewport_x,
73 gdouble viewport_y);
74 static void gimp_display_shell_scale_fit_or_fill (GimpDisplayShell *shell,
75 gboolean fill);
76
77 static gboolean gimp_display_shell_scale_image_starts_to_fit
78 (GimpDisplayShell *shell,
79 gdouble new_scale,
80 gdouble current_scale,
81 gboolean *vertically,
82 gboolean *horizontally);
83 static gboolean gimp_display_shell_scale_viewport_coord_almost_centered
84 (GimpDisplayShell *shell,
85 gint x,
86 gint y,
87 gboolean *horizontally,
88 gboolean *vertically);
89
90 static void gimp_display_shell_scale_get_image_center_viewport
91 (GimpDisplayShell *shell,
92 gint *image_center_x,
93 gint *image_center_y);
94
95
96 static void gimp_display_shell_scale_get_zoom_focus (GimpDisplayShell *shell,
97 gdouble new_scale,
98 gdouble current_scale,
99 gdouble *x,
100 gdouble *y,
101 GimpZoomFocus zoom_focus);
102
103
104 /* public functions */
105
106 /**
107 * gimp_display_shell_scale_revert:
108 * @shell: the #GimpDisplayShell
109 *
110 * Reverts the display to the previously used scale. If no previous
111 * scale exist, then the call does nothing.
112 *
113 * Return value: %TRUE if the scale was reverted, otherwise %FALSE.
114 **/
115 gboolean
gimp_display_shell_scale_revert(GimpDisplayShell * shell)116 gimp_display_shell_scale_revert (GimpDisplayShell *shell)
117 {
118 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
119
120 /* don't bother if no scale has been set */
121 if (shell->last_scale < SCALE_EPSILON)
122 return FALSE;
123
124 shell->last_scale_time = 0;
125
126 gimp_display_shell_scale_by_values (shell,
127 shell->last_scale,
128 shell->last_offset_x,
129 shell->last_offset_y,
130 FALSE); /* don't resize the window */
131
132 return TRUE;
133 }
134
135 /**
136 * gimp_display_shell_scale_can_revert:
137 * @shell: the #GimpDisplayShell
138 *
139 * Return value: %TRUE if a previous display scale exists, otherwise %FALSE.
140 **/
141 gboolean
gimp_display_shell_scale_can_revert(GimpDisplayShell * shell)142 gimp_display_shell_scale_can_revert (GimpDisplayShell *shell)
143 {
144 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
145
146 return (shell->last_scale > SCALE_EPSILON);
147 }
148
149 /**
150 * gimp_display_shell_scale_save_revert_values:
151 * @shell:
152 *
153 * Handle the updating of the Revert Zoom variables.
154 **/
155 void
gimp_display_shell_scale_save_revert_values(GimpDisplayShell * shell)156 gimp_display_shell_scale_save_revert_values (GimpDisplayShell *shell)
157 {
158 guint now;
159
160 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
161
162 now = time (NULL);
163
164 if (now - shell->last_scale_time >= SCALE_TIMEOUT)
165 {
166 shell->last_scale = gimp_zoom_model_get_factor (shell->zoom);
167 shell->last_offset_x = shell->offset_x;
168 shell->last_offset_y = shell->offset_y;
169 }
170
171 shell->last_scale_time = now;
172 }
173
174 /**
175 * gimp_display_shell_scale_set_dot_for_dot:
176 * @shell: the #GimpDisplayShell
177 * @dot_for_dot: whether "Dot for Dot" should be enabled
178 *
179 * If @dot_for_dot is set to %TRUE then the "Dot for Dot" mode (where image and
180 * screen pixels are of the same size) is activated. Dually, the mode is
181 * disabled if @dot_for_dot is %FALSE.
182 **/
183 void
gimp_display_shell_scale_set_dot_for_dot(GimpDisplayShell * shell,gboolean dot_for_dot)184 gimp_display_shell_scale_set_dot_for_dot (GimpDisplayShell *shell,
185 gboolean dot_for_dot)
186 {
187 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
188
189 if (dot_for_dot != shell->dot_for_dot)
190 {
191 GimpDisplayConfig *config = shell->display->config;
192 gboolean resize_window;
193
194 /* Resize windows only in multi-window mode */
195 resize_window = (config->resize_windows_on_zoom &&
196 ! GIMP_GUI_CONFIG (config)->single_window_mode);
197
198 /* freeze the active tool */
199 gimp_display_shell_pause (shell);
200
201 shell->dot_for_dot = dot_for_dot;
202
203 gimp_display_shell_scale_update (shell);
204
205 gimp_display_shell_scale_resize (shell, resize_window, FALSE);
206
207 /* re-enable the active tool */
208 gimp_display_shell_resume (shell);
209 }
210 }
211
212 /**
213 * gimp_display_shell_scale_get_image_size:
214 * @shell:
215 * @w:
216 * @h:
217 *
218 * Gets the size of the rendered image after it has been scaled.
219 *
220 **/
221 void
gimp_display_shell_scale_get_image_size(GimpDisplayShell * shell,gint * w,gint * h)222 gimp_display_shell_scale_get_image_size (GimpDisplayShell *shell,
223 gint *w,
224 gint *h)
225 {
226 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
227
228 gimp_display_shell_scale_get_image_size_for_scale (shell,
229 gimp_zoom_model_get_factor (shell->zoom),
230 w, h);
231 }
232
233 /**
234 * gimp_display_shell_scale_get_image_bounds:
235 * @shell:
236 * @x:
237 * @y:
238 * @w:
239 * @h:
240 *
241 * Gets the screen-space boudning box of the image, after it has
242 * been transformed (i.e., scaled, rotated, and scrolled).
243 **/
244 void
gimp_display_shell_scale_get_image_bounds(GimpDisplayShell * shell,gint * x,gint * y,gint * w,gint * h)245 gimp_display_shell_scale_get_image_bounds (GimpDisplayShell *shell,
246 gint *x,
247 gint *y,
248 gint *w,
249 gint *h)
250 {
251 GimpImage *image;
252 gdouble x1, y1;
253 gdouble x2, y2;
254
255 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
256
257 image = gimp_display_get_image (shell->display);
258
259 gimp_display_shell_transform_bounds (shell,
260 0, 0,
261 gimp_image_get_width (image),
262 gimp_image_get_height (image),
263 &x1, &y1,
264 &x2, &y2);
265
266 x1 = ceil (x1);
267 y1 = ceil (y1);
268 x2 = floor (x2);
269 y2 = floor (y2);
270
271 if (x) *x = x1 + shell->offset_x;
272 if (y) *y = y1 + shell->offset_y;
273 if (w) *w = x2 - x1;
274 if (h) *h = y2 - y1;
275 }
276
277 /**
278 * gimp_display_shell_scale_get_image_unrotated_bounds:
279 * @shell:
280 * @x:
281 * @y:
282 * @w:
283 * @h:
284 *
285 * Gets the screen-space boudning box of the image, after it has
286 * been scaled and scrolled, but before it has been rotated.
287 **/
288 void
gimp_display_shell_scale_get_image_unrotated_bounds(GimpDisplayShell * shell,gint * x,gint * y,gint * w,gint * h)289 gimp_display_shell_scale_get_image_unrotated_bounds (GimpDisplayShell *shell,
290 gint *x,
291 gint *y,
292 gint *w,
293 gint *h)
294 {
295 GimpImage *image;
296
297 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
298
299 image = gimp_display_get_image (shell->display);
300
301 if (x) *x = -shell->offset_x;
302 if (y) *y = -shell->offset_y;
303 if (w) *w = floor (gimp_image_get_width (image) * shell->scale_x);
304 if (h) *h = floor (gimp_image_get_height (image) * shell->scale_y);
305 }
306
307 /**
308 * gimp_display_shell_scale_get_image_bounding_box:
309 * @shell:
310 * @x:
311 * @y:
312 * @w:
313 * @h:
314 *
315 * Gets the screen-space boudning box of the image content, after it has
316 * been transformed (i.e., scaled, rotated, and scrolled).
317 **/
318 void
gimp_display_shell_scale_get_image_bounding_box(GimpDisplayShell * shell,gint * x,gint * y,gint * w,gint * h)319 gimp_display_shell_scale_get_image_bounding_box (GimpDisplayShell *shell,
320 gint *x,
321 gint *y,
322 gint *w,
323 gint *h)
324 {
325 GeglRectangle bounding_box;
326 gdouble x1, y1;
327 gdouble x2, y2;
328
329 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
330
331 bounding_box = gimp_display_shell_get_bounding_box (shell);
332
333 gimp_display_shell_transform_bounds (shell,
334 bounding_box.x,
335 bounding_box.y,
336 bounding_box.x + bounding_box.width,
337 bounding_box.y + bounding_box.height,
338 &x1, &y1,
339 &x2, &y2);
340
341 if (! shell->show_all)
342 {
343 x1 = ceil (x1);
344 y1 = ceil (y1);
345 x2 = floor (x2);
346 y2 = floor (y2);
347 }
348 else
349 {
350 x1 = floor (x1);
351 y1 = floor (y1);
352 x2 = ceil (x2);
353 y2 = ceil (y2);
354 }
355
356 if (x) *x = x1 + shell->offset_x;
357 if (y) *y = y1 + shell->offset_y;
358 if (w) *w = x2 - x1;
359 if (h) *h = y2 - y1;
360 }
361
362 /**
363 * gimp_display_shell_scale_get_image_unrotated_bounding_box:
364 * @shell:
365 * @x:
366 * @y:
367 * @w:
368 * @h:
369 *
370 * Gets the screen-space boudning box of the image content, after it has
371 * been scaled and scrolled, but before it has been rotated.
372 **/
373 void
gimp_display_shell_scale_get_image_unrotated_bounding_box(GimpDisplayShell * shell,gint * x,gint * y,gint * w,gint * h)374 gimp_display_shell_scale_get_image_unrotated_bounding_box (GimpDisplayShell *shell,
375 gint *x,
376 gint *y,
377 gint *w,
378 gint *h)
379 {
380 GeglRectangle bounding_box;
381 gdouble x1, y1;
382 gdouble x2, y2;
383
384 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
385
386 bounding_box = gimp_display_shell_get_bounding_box (shell);
387
388 x1 = bounding_box.x * shell->scale_x -
389 shell->offset_x;
390 y1 = bounding_box.y * shell->scale_y -
391 shell->offset_y;
392
393 x2 = (bounding_box.x + bounding_box.width) * shell->scale_x -
394 shell->offset_x;
395 y2 = (bounding_box.y + bounding_box.height) * shell->scale_y -
396 shell->offset_y;
397
398 if (! shell->show_all)
399 {
400 x1 = ceil (x1);
401 y1 = ceil (y1);
402 x2 = floor (x2);
403 y2 = floor (y2);
404 }
405 else
406 {
407 x1 = floor (x1);
408 y1 = floor (y1);
409 x2 = ceil (x2);
410 y2 = ceil (y2);
411 }
412
413 if (x) *x = x1;
414 if (y) *y = y1;
415 if (w) *w = x2 - x1;
416 if (h) *h = y2 - y1;
417 }
418
419 /**
420 * gimp_display_shell_scale_image_is_within_viewport:
421 * @shell:
422 *
423 * Returns: %TRUE if the (scaled) image is smaller than and within the
424 * viewport.
425 **/
426 gboolean
gimp_display_shell_scale_image_is_within_viewport(GimpDisplayShell * shell,gboolean * horizontally,gboolean * vertically)427 gimp_display_shell_scale_image_is_within_viewport (GimpDisplayShell *shell,
428 gboolean *horizontally,
429 gboolean *vertically)
430 {
431 gboolean horizontally_dummy, vertically_dummy;
432
433 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
434
435 if (! horizontally) horizontally = &horizontally_dummy;
436 if (! vertically) vertically = &vertically_dummy;
437
438 if (! gimp_display_shell_get_infinite_canvas (shell))
439 {
440 gint sx, sy;
441 gint sw, sh;
442
443 gimp_display_shell_scale_get_image_bounding_box (shell,
444 &sx, &sy, &sw, &sh);
445
446 sx -= shell->offset_x;
447 sy -= shell->offset_y;
448
449 *horizontally = sx >= 0 && sx + sw <= shell->disp_width;
450 *vertically = sy >= 0 && sy + sh <= shell->disp_height;
451 }
452 else
453 {
454 *horizontally = FALSE;
455 *vertically = FALSE;
456 }
457
458 return *vertically && *horizontally;
459 }
460
461 /* We used to calculate the scale factor in the SCALEFACTOR_X() and
462 * SCALEFACTOR_Y() macros. But since these are rather frequently
463 * called and the values rarely change, we now store them in the
464 * shell and call this function whenever they need to be recalculated.
465 */
466 void
gimp_display_shell_scale_update(GimpDisplayShell * shell)467 gimp_display_shell_scale_update (GimpDisplayShell *shell)
468 {
469 GimpImage *image;
470
471 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
472
473 image = gimp_display_get_image (shell->display);
474
475 if (image)
476 {
477 gimp_display_shell_calculate_scale_x_and_y (shell,
478 gimp_zoom_model_get_factor (shell->zoom),
479 &shell->scale_x,
480 &shell->scale_y);
481 }
482 else
483 {
484 shell->scale_x = 1.0;
485 shell->scale_y = 1.0;
486 }
487 }
488
489 /**
490 * gimp_display_shell_scale:
491 * @shell: the #GimpDisplayShell
492 * @zoom_type: whether to zoom in, out or to a specific scale
493 * @scale: ignored unless @zoom_type == %GIMP_ZOOM_TO
494 *
495 * This function figures out the context of the zoom and behaves
496 * appropriately thereafter.
497 *
498 **/
499 void
gimp_display_shell_scale(GimpDisplayShell * shell,GimpZoomType zoom_type,gdouble new_scale,GimpZoomFocus zoom_focus)500 gimp_display_shell_scale (GimpDisplayShell *shell,
501 GimpZoomType zoom_type,
502 gdouble new_scale,
503 GimpZoomFocus zoom_focus)
504 {
505 GimpDisplayConfig *config;
506 gdouble current_scale;
507 gboolean resize_window;
508
509 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
510 g_return_if_fail (shell->canvas != NULL);
511
512 current_scale = gimp_zoom_model_get_factor (shell->zoom);
513
514 if (zoom_type != GIMP_ZOOM_TO)
515 new_scale = gimp_zoom_model_zoom_step (zoom_type, current_scale);
516
517 if (SCALE_EQUALS (new_scale, current_scale))
518 return;
519
520 config = shell->display->config;
521
522 /* Resize windows only in multi-window mode */
523 resize_window = (config->resize_windows_on_zoom &&
524 ! GIMP_GUI_CONFIG (config)->single_window_mode);
525
526 if (resize_window)
527 {
528 /* If the window is resized on zoom, simply do the zoom and get
529 * things rolling
530 */
531 gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, new_scale);
532
533 gimp_display_shell_scale_resize (shell, TRUE, FALSE);
534 }
535 else
536 {
537 gdouble x, y;
538 gint image_center_x;
539 gint image_center_y;
540
541 gimp_display_shell_scale_get_zoom_focus (shell,
542 new_scale,
543 current_scale,
544 &x,
545 &y,
546 zoom_focus);
547 gimp_display_shell_scale_get_image_center_viewport (shell,
548 &image_center_x,
549 &image_center_y);
550
551 gimp_display_shell_scale_to (shell, new_scale, x, y);
552
553 /* skip centering magic if pointer focus was requested */
554 if (zoom_focus != GIMP_ZOOM_FOCUS_POINTER)
555 {
556 gboolean starts_fitting_horiz;
557 gboolean starts_fitting_vert;
558 gboolean zoom_focus_almost_centered_horiz;
559 gboolean zoom_focus_almost_centered_vert;
560 gboolean image_center_almost_centered_horiz;
561 gboolean image_center_almost_centered_vert;
562
563 /* If an image axis started to fit due to zooming out or if
564 * the focus point is as good as in the center, center on
565 * that axis
566 */
567 gimp_display_shell_scale_image_starts_to_fit (shell,
568 new_scale,
569 current_scale,
570 &starts_fitting_horiz,
571 &starts_fitting_vert);
572
573 gimp_display_shell_scale_viewport_coord_almost_centered (shell,
574 x,
575 y,
576 &zoom_focus_almost_centered_horiz,
577 &zoom_focus_almost_centered_vert);
578 gimp_display_shell_scale_viewport_coord_almost_centered (shell,
579 image_center_x,
580 image_center_y,
581 &image_center_almost_centered_horiz,
582 &image_center_almost_centered_vert);
583
584 gimp_display_shell_scroll_center_image (shell,
585 starts_fitting_horiz ||
586 (zoom_focus_almost_centered_horiz &&
587 image_center_almost_centered_horiz),
588 starts_fitting_vert ||
589 (zoom_focus_almost_centered_vert &&
590 image_center_almost_centered_vert));
591 }
592 }
593 }
594
595 /**
596 * gimp_display_shell_scale_to_rectangle:
597 * @shell: the #GimpDisplayShell
598 * @zoom_type: whether to zoom in or out
599 * @x: retangle's x in image coordinates
600 * @y: retangle's y in image coordinates
601 * @width: retangle's width in image coordinates
602 * @height: retangle's height in image coordinates
603 * @resize_window: whether the display window should be resized
604 *
605 * Scales and scrolls to a specific image rectangle
606 **/
607 void
gimp_display_shell_scale_to_rectangle(GimpDisplayShell * shell,GimpZoomType zoom_type,gdouble x,gdouble y,gdouble width,gdouble height,gboolean resize_window)608 gimp_display_shell_scale_to_rectangle (GimpDisplayShell *shell,
609 GimpZoomType zoom_type,
610 gdouble x,
611 gdouble y,
612 gdouble width,
613 gdouble height,
614 gboolean resize_window)
615 {
616 gdouble current_scale;
617 gdouble new_scale;
618 gdouble factor = 1.0;
619 gint offset_x = 0;
620 gint offset_y = 0;
621
622 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
623
624 gimp_display_shell_transform_bounds (shell,
625 x, y,
626 x + width, y + height,
627 &x, &y,
628 &width, &height);
629
630 /* Convert scrolled (x1, y1, x2, y2) to unscrolled (x, y, width, height). */
631 width -= x;
632 height -= y;
633 x += shell->offset_x;
634 y += shell->offset_y;
635
636 width = MAX (1.0, width);
637 height = MAX (1.0, height);
638
639 current_scale = gimp_zoom_model_get_factor (shell->zoom);
640
641 switch (zoom_type)
642 {
643 case GIMP_ZOOM_IN:
644 factor = MIN ((shell->disp_width / width),
645 (shell->disp_height / height));
646 break;
647
648 case GIMP_ZOOM_OUT:
649 factor = MAX ((width / shell->disp_width),
650 (height / shell->disp_height));
651 break;
652
653 default:
654 g_return_if_reached ();
655 break;
656 }
657
658 new_scale = current_scale * factor;
659
660 switch (zoom_type)
661 {
662 case GIMP_ZOOM_IN:
663 /* move the center of the rectangle to the center of the
664 * viewport:
665 *
666 * new_offset = center of rectangle in new scale screen coords
667 * including offset
668 * -
669 * center of viewport in screen coords without
670 * offset
671 */
672 offset_x = RINT (factor * (x + width / 2.0) - (shell->disp_width / 2));
673 offset_y = RINT (factor * (y + height / 2.0) - (shell->disp_height / 2));
674 break;
675
676 case GIMP_ZOOM_OUT:
677 /* move the center of the viewport to the center of the
678 * rectangle:
679 *
680 * new_offset = center of viewport in new scale screen coords
681 * including offset
682 * -
683 * center of rectangle in screen coords without
684 * offset
685 */
686 offset_x = RINT (factor * (shell->offset_x + shell->disp_width / 2) -
687 ((x + width / 2.0) - shell->offset_x));
688
689 offset_y = RINT (factor * (shell->offset_y + shell->disp_height / 2) -
690 ((y + height / 2.0) - shell->offset_y));
691 break;
692
693 default:
694 break;
695 }
696
697 if (new_scale != current_scale ||
698 offset_x != shell->offset_x ||
699 offset_y != shell->offset_y)
700 {
701 gimp_display_shell_scale_by_values (shell,
702 new_scale,
703 offset_x, offset_y,
704 resize_window);
705 }
706 }
707
708 /**
709 * gimp_display_shell_scale_fit_in:
710 * @shell: the #GimpDisplayShell
711 *
712 * Sets the scale such that the entire image precisely fits in the
713 * display area.
714 **/
715 void
gimp_display_shell_scale_fit_in(GimpDisplayShell * shell)716 gimp_display_shell_scale_fit_in (GimpDisplayShell *shell)
717 {
718 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
719
720 gimp_display_shell_scale_fit_or_fill (shell,
721 /* fill = */ FALSE);
722 }
723
724 /**
725 * gimp_display_shell_scale_fill:
726 * @shell: the #GimpDisplayShell
727 *
728 * Sets the scale such that the entire display area is precisely
729 * filled by the image.
730 **/
731 void
gimp_display_shell_scale_fill(GimpDisplayShell * shell)732 gimp_display_shell_scale_fill (GimpDisplayShell *shell)
733 {
734 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
735
736 gimp_display_shell_scale_fit_or_fill (shell,
737 /* fill = */ TRUE);
738 }
739
740 /**
741 * gimp_display_shell_scale_by_values:
742 * @shell: the #GimpDisplayShell
743 * @scale: the new scale
744 * @offset_x: the new X offset
745 * @offset_y: the new Y offset
746 * @resize_window: whether the display window should be resized
747 *
748 * Directly sets the image scale and image offsets used by the display. If
749 * @resize_window is %TRUE then the display window is resized to better
750 * accommodate the image, see gimp_display_shell_shrink_wrap().
751 **/
752 void
gimp_display_shell_scale_by_values(GimpDisplayShell * shell,gdouble scale,gint offset_x,gint offset_y,gboolean resize_window)753 gimp_display_shell_scale_by_values (GimpDisplayShell *shell,
754 gdouble scale,
755 gint offset_x,
756 gint offset_y,
757 gboolean resize_window)
758 {
759 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
760
761 /* Abort early if the values are all setup already. We don't
762 * want to inadvertently resize the window (bug #164281).
763 */
764 if (SCALE_EQUALS (gimp_zoom_model_get_factor (shell->zoom), scale) &&
765 shell->offset_x == offset_x &&
766 shell->offset_y == offset_y)
767 return;
768
769 gimp_display_shell_scale_save_revert_values (shell);
770
771 /* freeze the active tool */
772 gimp_display_shell_pause (shell);
773
774 gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale);
775
776 shell->offset_x = offset_x;
777 shell->offset_y = offset_y;
778
779 gimp_display_shell_rotate_update_transform (shell);
780
781 gimp_display_shell_scale_resize (shell, resize_window, FALSE);
782
783 /* re-enable the active tool */
784 gimp_display_shell_resume (shell);
785 }
786
787 void
gimp_display_shell_scale_drag(GimpDisplayShell * shell,gdouble start_x,gdouble start_y,gdouble delta_x,gdouble delta_y)788 gimp_display_shell_scale_drag (GimpDisplayShell *shell,
789 gdouble start_x,
790 gdouble start_y,
791 gdouble delta_x,
792 gdouble delta_y)
793 {
794 gdouble scale;
795
796 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
797
798 scale = gimp_zoom_model_get_factor (shell->zoom);
799
800 gimp_display_shell_push_zoom_focus_pointer_pos (shell, start_x, start_y);
801
802 if (delta_y > 0)
803 {
804 gimp_display_shell_scale (shell,
805 GIMP_ZOOM_TO,
806 scale * 1.1,
807 GIMP_ZOOM_FOCUS_POINTER);
808 }
809 else if (delta_y < 0)
810 {
811 gimp_display_shell_scale (shell,
812 GIMP_ZOOM_TO,
813 scale * 0.9,
814 GIMP_ZOOM_FOCUS_POINTER);
815 }
816 }
817
818 /**
819 * gimp_display_shell_scale_shrink_wrap:
820 * @shell: the #GimpDisplayShell
821 *
822 * Convenience function with the same functionality as
823 * gimp_display_shell_scale_resize(@shell, TRUE, grow_only).
824 **/
825 void
gimp_display_shell_scale_shrink_wrap(GimpDisplayShell * shell,gboolean grow_only)826 gimp_display_shell_scale_shrink_wrap (GimpDisplayShell *shell,
827 gboolean grow_only)
828 {
829 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
830
831 gimp_display_shell_scale_resize (shell, TRUE, grow_only);
832 }
833
834 /**
835 * gimp_display_shell_scale_resize:
836 * @shell: the #GimpDisplayShell
837 * @resize_window: whether the display window should be resized
838 * @grow_only: whether shrinking of the window is allowed or not
839 *
840 * Function commonly called after a change in display scale to make the changes
841 * visible to the user. If @resize_window is %TRUE then the display window is
842 * resized to accommodate the display image as per
843 * gimp_display_shell_shrink_wrap().
844 **/
845 void
gimp_display_shell_scale_resize(GimpDisplayShell * shell,gboolean resize_window,gboolean grow_only)846 gimp_display_shell_scale_resize (GimpDisplayShell *shell,
847 gboolean resize_window,
848 gboolean grow_only)
849 {
850 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
851
852 /* freeze the active tool */
853 gimp_display_shell_pause (shell);
854
855 if (resize_window)
856 {
857 GimpImageWindow *window = gimp_display_shell_get_window (shell);
858
859 if (window && gimp_image_window_get_active_shell (window) == shell)
860 {
861 gimp_image_window_shrink_wrap (window, grow_only);
862 }
863 }
864
865 gimp_display_shell_scroll_clamp_and_update (shell);
866 gimp_display_shell_scaled (shell);
867
868 gimp_display_shell_expose_full (shell);
869
870 /* re-enable the active tool */
871 gimp_display_shell_resume (shell);
872 }
873
874 void
gimp_display_shell_set_initial_scale(GimpDisplayShell * shell,gdouble scale,gint * display_width,gint * display_height)875 gimp_display_shell_set_initial_scale (GimpDisplayShell *shell,
876 gdouble scale,
877 gint *display_width,
878 gint *display_height)
879 {
880 GimpImage *image;
881 GdkScreen *screen;
882 gint image_width;
883 gint image_height;
884 gint shell_width;
885 gint shell_height;
886 gint screen_width;
887 gint screen_height;
888
889 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
890
891 image = gimp_display_get_image (shell->display);
892
893 screen = gtk_widget_get_screen (GTK_WIDGET (shell));
894
895 image_width = gimp_image_get_width (image);
896 image_height = gimp_image_get_height (image);
897
898 screen_width = gdk_screen_get_width (screen) * 0.75;
899 screen_height = gdk_screen_get_height (screen) * 0.75;
900
901 /* We need to zoom before we use SCALE[XY] */
902 gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale);
903
904 shell_width = SCALEX (shell, image_width);
905 shell_height = SCALEY (shell, image_height);
906
907 if (shell->display->config->initial_zoom_to_fit)
908 {
909 /* Limit to the size of the screen... */
910 if (shell_width > screen_width || shell_height > screen_height)
911 {
912 gdouble new_scale;
913 gdouble current = gimp_zoom_model_get_factor (shell->zoom);
914
915 new_scale = current * MIN (((gdouble) screen_height) / shell_height,
916 ((gdouble) screen_width) / shell_width);
917
918 new_scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, new_scale);
919
920 /* Since zooming out might skip a zoom step we zoom in
921 * again and test if we are small enough.
922 */
923 gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO,
924 gimp_zoom_model_zoom_step (GIMP_ZOOM_IN,
925 new_scale));
926
927 if (SCALEX (shell, image_width) > screen_width ||
928 SCALEY (shell, image_height) > screen_height)
929 gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, new_scale);
930
931 shell_width = SCALEX (shell, image_width);
932 shell_height = SCALEY (shell, image_height);
933 }
934 }
935 else
936 {
937 /* Set up size like above, but do not zoom to fit. Useful when
938 * working on large images.
939 */
940 if (shell_width > screen_width)
941 shell_width = screen_width;
942
943 if (shell_height > screen_height)
944 shell_height = screen_height;
945 }
946
947 if (display_width)
948 *display_width = shell_width;
949
950 if (display_height)
951 *display_height = shell_height;
952 }
953
954 /**
955 * gimp_display_shell_get_rotated_scale:
956 * @shell: the #GimpDisplayShell
957 * @scale_x: horizontal scale output
958 * @scale_y: vertical scale output
959 *
960 * Returns the screen space horizontal and vertical scaling
961 * factors, taking rotation into account.
962 **/
963 void
gimp_display_shell_get_rotated_scale(GimpDisplayShell * shell,gdouble * scale_x,gdouble * scale_y)964 gimp_display_shell_get_rotated_scale (GimpDisplayShell *shell,
965 gdouble *scale_x,
966 gdouble *scale_y)
967 {
968 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
969
970 if (shell->rotate_angle == 0.0 || shell->scale_x == shell->scale_y)
971 {
972 if (scale_x) *scale_x = shell->scale_x;
973 if (scale_y) *scale_y = shell->scale_y;
974 }
975 else
976 {
977 gdouble a = G_PI * shell->rotate_angle / 180.0;
978 gdouble cos_a = cos (a);
979 gdouble sin_a = sin (a);
980
981 if (scale_x) *scale_x = 1.0 / sqrt (SQR (cos_a / shell->scale_x) +
982 SQR (sin_a / shell->scale_y));
983
984 if (scale_y) *scale_y = 1.0 / sqrt (SQR (cos_a / shell->scale_y) +
985 SQR (sin_a / shell->scale_x));
986 }
987 }
988
989 /**
990 * gimp_display_shell_push_zoom_focus_pointer_pos:
991 * @shell:
992 * @x:
993 * @y:
994 *
995 * When the zoom focus mechanism asks for the pointer the next time,
996 * use @x and @y.
997 **/
998 void
gimp_display_shell_push_zoom_focus_pointer_pos(GimpDisplayShell * shell,gint x,gint y)999 gimp_display_shell_push_zoom_focus_pointer_pos (GimpDisplayShell *shell,
1000 gint x,
1001 gint y)
1002 {
1003 GdkPoint *point = g_slice_new (GdkPoint);
1004 point->x = x;
1005 point->y = y;
1006
1007 g_queue_push_head (shell->zoom_focus_pointer_queue,
1008 point);
1009 }
1010
1011
1012 /* private functions */
1013
1014 static void
gimp_display_shell_scale_get_screen_resolution(GimpDisplayShell * shell,gdouble * xres,gdouble * yres)1015 gimp_display_shell_scale_get_screen_resolution (GimpDisplayShell *shell,
1016 gdouble *xres,
1017 gdouble *yres)
1018 {
1019 gdouble x, y;
1020
1021 if (shell->dot_for_dot)
1022 {
1023 gimp_image_get_resolution (gimp_display_get_image (shell->display),
1024 &x, &y);
1025 }
1026 else
1027 {
1028 x = shell->monitor_xres;
1029 y = shell->monitor_yres;
1030 }
1031
1032 if (xres) *xres = x;
1033 if (yres) *yres = y;
1034 }
1035
1036 /**
1037 * gimp_display_shell_scale_get_image_size_for_scale:
1038 * @shell:
1039 * @scale:
1040 * @w:
1041 * @h:
1042 *
1043 **/
1044 static void
gimp_display_shell_scale_get_image_size_for_scale(GimpDisplayShell * shell,gdouble scale,gint * w,gint * h)1045 gimp_display_shell_scale_get_image_size_for_scale (GimpDisplayShell *shell,
1046 gdouble scale,
1047 gint *w,
1048 gint *h)
1049 {
1050 GimpImage *image = gimp_display_get_image (shell->display);
1051 gdouble scale_x;
1052 gdouble scale_y;
1053
1054 gimp_display_shell_calculate_scale_x_and_y (shell, scale, &scale_x, &scale_y);
1055
1056 if (w) *w = scale_x * gimp_image_get_width (image);
1057 if (h) *h = scale_y * gimp_image_get_height (image);
1058 }
1059
1060 /**
1061 * gimp_display_shell_calculate_scale_x_and_y:
1062 * @shell:
1063 * @scale:
1064 * @scale_x:
1065 * @scale_y:
1066 *
1067 **/
1068 static void
gimp_display_shell_calculate_scale_x_and_y(GimpDisplayShell * shell,gdouble scale,gdouble * scale_x,gdouble * scale_y)1069 gimp_display_shell_calculate_scale_x_and_y (GimpDisplayShell *shell,
1070 gdouble scale,
1071 gdouble *scale_x,
1072 gdouble *scale_y)
1073 {
1074 GimpImage *image = gimp_display_get_image (shell->display);
1075 gdouble xres;
1076 gdouble yres;
1077 gdouble screen_xres;
1078 gdouble screen_yres;
1079
1080 gimp_image_get_resolution (image, &xres, &yres);
1081 gimp_display_shell_scale_get_screen_resolution (shell,
1082 &screen_xres, &screen_yres);
1083
1084 if (scale_x) *scale_x = scale * screen_xres / xres;
1085 if (scale_y) *scale_y = scale * screen_yres / yres;
1086 }
1087
1088 /**
1089 * gimp_display_shell_scale_to:
1090 * @shell:
1091 * @scale:
1092 * @viewport_x:
1093 * @viewport_y:
1094 *
1095 * Zooms. The display offsets are adjusted so that the point specified
1096 * by @x and @y doesn't change it's position on screen.
1097 **/
1098 static void
gimp_display_shell_scale_to(GimpDisplayShell * shell,gdouble scale,gdouble viewport_x,gdouble viewport_y)1099 gimp_display_shell_scale_to (GimpDisplayShell *shell,
1100 gdouble scale,
1101 gdouble viewport_x,
1102 gdouble viewport_y)
1103 {
1104 gdouble image_x, image_y;
1105 gdouble new_viewport_x, new_viewport_y;
1106
1107 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
1108
1109 if (! shell->display)
1110 return;
1111
1112 /* freeze the active tool */
1113 gimp_display_shell_pause (shell);
1114
1115 gimp_display_shell_untransform_xy_f (shell,
1116 viewport_x,
1117 viewport_y,
1118 &image_x,
1119 &image_y);
1120
1121 /* Note that we never come here if we need to resize_windows_on_zoom
1122 */
1123 gimp_display_shell_scale_by_values (shell,
1124 scale,
1125 shell->offset_x,
1126 shell->offset_y,
1127 FALSE);
1128
1129 gimp_display_shell_transform_xy_f (shell,
1130 image_x,
1131 image_y,
1132 &new_viewport_x,
1133 &new_viewport_y);
1134
1135 gimp_display_shell_scroll (shell,
1136 new_viewport_x - viewport_x,
1137 new_viewport_y - viewport_y);
1138
1139 /* re-enable the active tool */
1140 gimp_display_shell_resume (shell);
1141 }
1142
1143 /**
1144 * gimp_display_shell_scale_fit_or_fill:
1145 * @shell: the #GimpDisplayShell
1146 * @fill: whether to scale the image to fill the viewport,
1147 * or fit inside the viewport
1148 *
1149 * A common implementation for gimp_display_shell_scale_{fit_in,fill}().
1150 **/
1151 static void
gimp_display_shell_scale_fit_or_fill(GimpDisplayShell * shell,gboolean fill)1152 gimp_display_shell_scale_fit_or_fill (GimpDisplayShell *shell,
1153 gboolean fill)
1154 {
1155 GeglRectangle bounding_box;
1156 gdouble image_x;
1157 gdouble image_y;
1158 gdouble image_width;
1159 gdouble image_height;
1160 gdouble current_scale;
1161 gdouble zoom_factor;
1162
1163 if (! gimp_display_shell_get_infinite_canvas (shell))
1164 {
1165 GimpImage *image = gimp_display_get_image (shell->display);
1166
1167 bounding_box.x = 0;
1168 bounding_box.y = 0;
1169 bounding_box.width = gimp_image_get_width (image);
1170 bounding_box.height = gimp_image_get_height (image);
1171 }
1172 else
1173 {
1174 bounding_box = gimp_display_shell_get_bounding_box (shell);
1175 }
1176
1177 gimp_display_shell_transform_bounds (shell,
1178 bounding_box.x,
1179 bounding_box.y,
1180 bounding_box.x + bounding_box.width,
1181 bounding_box.y + bounding_box.height,
1182 &image_x,
1183 &image_y,
1184 &image_width,
1185 &image_height);
1186
1187 image_width -= image_x;
1188 image_height -= image_y;
1189
1190 current_scale = gimp_zoom_model_get_factor (shell->zoom);
1191
1192 if (fill)
1193 {
1194 zoom_factor = MAX (shell->disp_width / image_width,
1195 shell->disp_height / image_height);
1196 }
1197 else
1198 {
1199 zoom_factor = MIN (shell->disp_width / image_width,
1200 shell->disp_height / image_height);
1201 }
1202
1203 gimp_display_shell_scale (shell,
1204 GIMP_ZOOM_TO,
1205 zoom_factor * current_scale,
1206 GIMP_ZOOM_FOCUS_BEST_GUESS);
1207
1208 gimp_display_shell_scroll_center_content (shell, TRUE, TRUE);
1209 }
1210
1211 static gboolean
gimp_display_shell_scale_image_starts_to_fit(GimpDisplayShell * shell,gdouble new_scale,gdouble current_scale,gboolean * vertically,gboolean * horizontally)1212 gimp_display_shell_scale_image_starts_to_fit (GimpDisplayShell *shell,
1213 gdouble new_scale,
1214 gdouble current_scale,
1215 gboolean *vertically,
1216 gboolean *horizontally)
1217 {
1218 gboolean vertically_dummy;
1219 gboolean horizontally_dummy;
1220
1221 if (! vertically) vertically = &vertically_dummy;
1222 if (! horizontally) horizontally = &horizontally_dummy;
1223
1224 /* The image can only start to fit if we zoom out */
1225 if (new_scale > current_scale ||
1226 gimp_display_shell_get_infinite_canvas (shell))
1227 {
1228 *vertically = FALSE;
1229 *horizontally = FALSE;
1230 }
1231 else
1232 {
1233 gint current_scale_width;
1234 gint current_scale_height;
1235 gint new_scale_width;
1236 gint new_scale_height;
1237
1238 gimp_display_shell_scale_get_image_size_for_scale (shell,
1239 current_scale,
1240 ¤t_scale_width,
1241 ¤t_scale_height);
1242
1243 gimp_display_shell_scale_get_image_size_for_scale (shell,
1244 new_scale,
1245 &new_scale_width,
1246 &new_scale_height);
1247
1248 *vertically = (current_scale_width > shell->disp_width &&
1249 new_scale_width <= shell->disp_width);
1250 *horizontally = (current_scale_height > shell->disp_height &&
1251 new_scale_height <= shell->disp_height);
1252 }
1253
1254 return *vertically && *horizontally;
1255 }
1256
1257 static gboolean
gimp_display_shell_scale_image_stops_to_fit(GimpDisplayShell * shell,gdouble new_scale,gdouble current_scale,gboolean * vertically,gboolean * horizontally)1258 gimp_display_shell_scale_image_stops_to_fit (GimpDisplayShell *shell,
1259 gdouble new_scale,
1260 gdouble current_scale,
1261 gboolean *vertically,
1262 gboolean *horizontally)
1263 {
1264 return gimp_display_shell_scale_image_starts_to_fit (shell,
1265 current_scale,
1266 new_scale,
1267 vertically,
1268 horizontally);
1269 }
1270
1271 /**
1272 * gimp_display_shell_scale_viewport_coord_almost_centered:
1273 * @shell:
1274 * @x:
1275 * @y:
1276 * @horizontally:
1277 * @vertically:
1278 *
1279 **/
1280 static gboolean
gimp_display_shell_scale_viewport_coord_almost_centered(GimpDisplayShell * shell,gint x,gint y,gboolean * horizontally,gboolean * vertically)1281 gimp_display_shell_scale_viewport_coord_almost_centered (GimpDisplayShell *shell,
1282 gint x,
1283 gint y,
1284 gboolean *horizontally,
1285 gboolean *vertically)
1286 {
1287 gboolean local_horizontally = FALSE;
1288 gboolean local_vertically = FALSE;
1289 gint center_x = shell->disp_width / 2;
1290 gint center_y = shell->disp_height / 2;
1291
1292 if (! gimp_display_shell_get_infinite_canvas (shell))
1293 {
1294 local_horizontally = (x > center_x - ALMOST_CENTERED_THRESHOLD &&
1295 x < center_x + ALMOST_CENTERED_THRESHOLD);
1296
1297 local_vertically = (y > center_y - ALMOST_CENTERED_THRESHOLD &&
1298 y < center_y + ALMOST_CENTERED_THRESHOLD);
1299 }
1300
1301 if (horizontally) *horizontally = local_horizontally;
1302 if (vertically) *vertically = local_vertically;
1303
1304 return local_horizontally && local_vertically;
1305 }
1306
1307 static void
gimp_display_shell_scale_get_image_center_viewport(GimpDisplayShell * shell,gint * image_center_x,gint * image_center_y)1308 gimp_display_shell_scale_get_image_center_viewport (GimpDisplayShell *shell,
1309 gint *image_center_x,
1310 gint *image_center_y)
1311 {
1312 gint sw, sh;
1313
1314 gimp_display_shell_scale_get_image_size (shell, &sw, &sh);
1315
1316 if (image_center_x) *image_center_x = -shell->offset_x + sw / 2;
1317 if (image_center_y) *image_center_y = -shell->offset_y + sh / 2;
1318 }
1319
1320 /**
1321 * gimp_display_shell_scale_get_zoom_focus:
1322 * @shell:
1323 * @new_scale:
1324 * @x:
1325 * @y:
1326 *
1327 * Calculates the viewport coordinate to focus on when zooming
1328 * independently for each axis.
1329 **/
1330 static void
gimp_display_shell_scale_get_zoom_focus(GimpDisplayShell * shell,gdouble new_scale,gdouble current_scale,gdouble * x,gdouble * y,GimpZoomFocus zoom_focus)1331 gimp_display_shell_scale_get_zoom_focus (GimpDisplayShell *shell,
1332 gdouble new_scale,
1333 gdouble current_scale,
1334 gdouble *x,
1335 gdouble *y,
1336 GimpZoomFocus zoom_focus)
1337 {
1338 GtkWidget *window = GTK_WIDGET (gimp_display_shell_get_window (shell));
1339 GdkEvent *event;
1340 gint image_center_x;
1341 gint image_center_y;
1342 gint other_x;
1343 gint other_y;
1344
1345 /* Calculate stops-to-fit focus point */
1346 gimp_display_shell_scale_get_image_center_viewport (shell,
1347 &image_center_x,
1348 &image_center_y);
1349
1350 /* Calculate other focus point, default is the canvas center */
1351 other_x = shell->disp_width / 2;
1352 other_y = shell->disp_height / 2;
1353
1354 /* Center on the mouse position instead of the display center if
1355 * one of the following conditions are fulfilled and pointer is
1356 * within the canvas:
1357 *
1358 * (1) there's no current event (the action was triggered by an
1359 * input controller)
1360 * (2) the event originates from the canvas (a scroll event)
1361 * (3) the event originates from the window (a key press event)
1362 *
1363 * Basically the only situation where we don't want to center on
1364 * mouse position is if the action is being called from a menu.
1365 */
1366 event = gtk_get_current_event ();
1367
1368 if (! event ||
1369 gtk_get_event_widget (event) == shell->canvas ||
1370 gtk_get_event_widget (event) == window)
1371 {
1372 GdkPoint *point = g_queue_pop_head (shell->zoom_focus_pointer_queue);
1373 gint canvas_pointer_x;
1374 gint canvas_pointer_y;
1375
1376 if (point)
1377 {
1378 canvas_pointer_x = point->x;
1379 canvas_pointer_y = point->y;
1380
1381 g_slice_free (GdkPoint, point);
1382 }
1383 else
1384 {
1385 gtk_widget_get_pointer (shell->canvas,
1386 &canvas_pointer_x,
1387 &canvas_pointer_y);
1388 }
1389
1390 if (canvas_pointer_x >= 0 &&
1391 canvas_pointer_y >= 0 &&
1392 canvas_pointer_x < shell->disp_width &&
1393 canvas_pointer_y < shell->disp_height)
1394 {
1395 other_x = canvas_pointer_x;
1396 other_y = canvas_pointer_y;
1397 }
1398 }
1399
1400 if (zoom_focus == GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS)
1401 {
1402 if (gimp_display_shell_scale_viewport_coord_almost_centered (shell,
1403 image_center_x,
1404 image_center_y,
1405 NULL,
1406 NULL))
1407 {
1408 zoom_focus = GIMP_ZOOM_FOCUS_IMAGE_CENTER;
1409 }
1410 else
1411 {
1412 zoom_focus = GIMP_ZOOM_FOCUS_BEST_GUESS;
1413 }
1414 }
1415
1416 switch (zoom_focus)
1417 {
1418 case GIMP_ZOOM_FOCUS_POINTER:
1419 *x = other_x;
1420 *y = other_y;
1421 break;
1422
1423 case GIMP_ZOOM_FOCUS_IMAGE_CENTER:
1424 *x = image_center_x;
1425 *y = image_center_y;
1426 break;
1427
1428 case GIMP_ZOOM_FOCUS_BEST_GUESS:
1429 default:
1430 {
1431 gboolean within_horizontally, within_vertically;
1432 gboolean stops_horizontally, stops_vertically;
1433
1434 gimp_display_shell_scale_image_is_within_viewport (shell,
1435 &within_horizontally,
1436 &within_vertically);
1437
1438 gimp_display_shell_scale_image_stops_to_fit (shell,
1439 new_scale,
1440 current_scale,
1441 &stops_horizontally,
1442 &stops_vertically);
1443
1444 *x = within_horizontally && ! stops_horizontally ? image_center_x : other_x;
1445 *y = within_vertically && ! stops_vertically ? image_center_y : other_y;
1446 }
1447 break;
1448 }
1449 }
1450