[gimp/gimp-2-10] app, menus: add "show all" mode to GimpDisplayShell; "View -> Show All" toggle



commit 162665e42b900d1caf9365abc589615728b687c6
Author: Ell <ell_se yahoo com>
Date:   Wed Sep 4 15:50:29 2019 +0300

    app, menus: add "show all" mode to GimpDisplayShell; "View -> Show All" toggle
    
    Add a "show all" mode to GimpDisplayShell, controlled through a
    corresponding "View -> Show All" menu item.  When enabled, the
    entire image content is displayed, instead of cropping the image
    to the canvas size.  More generally, the display behaves as if the
    canvas were infinite.  The following commits improve the overall
    behavior in this mode.
    
    Add a prefernces option to control the default "show all" state.

 app/actions/view-actions.c               |  13 +++-
 app/actions/view-commands.c              |  30 +++++++++
 app/actions/view-commands.h              |   4 ++
 app/config/gimpdisplayconfig.c           |  14 ++++
 app/config/gimpdisplayconfig.h           |   1 +
 app/config/gimprc-blurbs.h               |   3 +
 app/dialogs/preferences-dialog.c         |   6 +-
 app/display/gimpdisplay-handlers.c       |  40 ++++++++---
 app/display/gimpdisplay.c                |  76 ++++++++++++++++-----
 app/display/gimpdisplay.h                |   3 +
 app/display/gimpdisplayshell-callbacks.c |  66 +++++++++++++-----
 app/display/gimpdisplayshell-handlers.c  |  14 ++++
 app/display/gimpdisplayshell-render.c    |  17 +++--
 app/display/gimpdisplayshell-scale.c     | 112 +++++++++++++++++++++++++++++++
 app/display/gimpdisplayshell-scale.h     |  12 ++++
 app/display/gimpdisplayshell.c           | 103 +++++++++++++++++++++++++++-
 app/display/gimpdisplayshell.h           |   9 +++
 app/widgets/gimphelp-ids.h               |   1 +
 menus/image-menu.xml.in                  |   1 +
 19 files changed, 476 insertions(+), 49 deletions(-)
---
diff --git a/app/actions/view-actions.c b/app/actions/view-actions.c
index 149d699e44..9cd102f874 100644
--- a/app/actions/view-actions.c
+++ b/app/actions/view-actions.c
@@ -173,6 +173,14 @@ static const GimpActionEntry view_actions[] =
 
 static const GimpToggleActionEntry view_toggle_actions[] =
 {
+
+  { "view-show-all", NULL,
+    NC_("view-action", "Show _All"), NULL,
+    NC_("view-action", "Show full image content"),
+    view_show_all_cmd_callback,
+    FALSE,
+    GIMP_HELP_VIEW_SHOW_ALL },
+
   { "view-dot-for-dot", NULL,
     NC_("view-action", "_Dot for Dot"), NULL,
     NC_("view-action", "A pixel on the screen represents an image pixel"),
@@ -252,7 +260,7 @@ static const GimpToggleActionEntry view_toggle_actions[] =
     GIMP_HELP_VIEW_SHOW_SAMPLE_POINTS },
 
   { "view-snap-to-guides", NULL,
-    NC_("view-action", "Sn_ap to Guides"), NULL,
+    NC_("view-action", "Snap to Gu_ides"), NULL,
     NC_("view-action", "Tool operations snap to guides"),
     view_snap_to_guides_cmd_callback,
     TRUE,
@@ -900,6 +908,9 @@ view_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("view-new",   image);
   SET_SENSITIVE ("view-close", image);
 
+  SET_SENSITIVE ("view-show-all", image);
+  SET_ACTIVE    ("view-show-all", display && shell->show_all);
+
   SET_SENSITIVE ("view-dot-for-dot", image);
   SET_ACTIVE    ("view-dot-for-dot", display && shell->dot_for_dot);
 
diff --git a/app/actions/view-commands.c b/app/actions/view-commands.c
index 0b4c4ffba7..1f8749b4d2 100644
--- a/app/actions/view-commands.c
+++ b/app/actions/view-commands.c
@@ -310,6 +310,36 @@ view_zoom_other_cmd_callback (GimpAction *action,
     }
 }
 
+void
+view_show_all_cmd_callback (GimpAction *action,
+                            GVariant   *value,
+                            gpointer    data)
+{
+  GimpDisplay      *display;
+  GimpDisplayShell *shell;
+  gboolean          active;
+  return_if_no_display (display, data);
+
+  shell = gimp_display_get_shell (display);
+
+  active = g_variant_get_boolean (value);
+
+  if (active != shell->show_all)
+    {
+      GimpImageWindow *window = gimp_display_shell_get_window (shell);
+
+      gimp_display_shell_set_show_all (shell, active);
+
+      if (window)
+        SET_ACTIVE (gimp_image_window_get_ui_manager (window),
+                    "view-show-all", shell->show_all);
+
+      if (IS_ACTIVE_DISPLAY (display))
+        SET_ACTIVE (shell->popup_manager, "view-show-all",
+                    shell->show_all);
+    }
+}
+
 void
 view_dot_for_dot_cmd_callback (GimpAction *action,
                                GVariant   *value,
diff --git a/app/actions/view-commands.h b/app/actions/view-commands.h
index 72afae9b2c..c28a24ef2e 100644
--- a/app/actions/view-commands.h
+++ b/app/actions/view-commands.h
@@ -53,6 +53,10 @@ void   view_zoom_explicit_cmd_callback              (GimpAction *action,
 void   view_zoom_other_cmd_callback                 (GimpAction *action,
                                                      gpointer    data);
 
+void   view_show_all_cmd_callback                   (GimpAction *action,
+                                                     GVariant   *value,
+                                                     gpointer    data);
+
 void   view_dot_for_dot_cmd_callback                (GimpAction *action,
                                                      GVariant   *value,
                                                      gpointer    data);
diff --git a/app/config/gimpdisplayconfig.c b/app/config/gimpdisplayconfig.c
index 1e285ca518..3c55522020 100644
--- a/app/config/gimpdisplayconfig.c
+++ b/app/config/gimpdisplayconfig.c
@@ -51,6 +51,7 @@ enum
   PROP_MARCHING_ANTS_SPEED,
   PROP_RESIZE_WINDOWS_ON_ZOOM,
   PROP_RESIZE_WINDOWS_ON_RESIZE,
+  PROP_DEFAULT_SHOW_ALL,
   PROP_DEFAULT_DOT_FOR_DOT,
   PROP_INITIAL_ZOOM_TO_FIT,
   PROP_CURSOR_MODE,
@@ -158,6 +159,13 @@ gimp_display_config_class_init (GimpDisplayConfigClass *klass)
                             FALSE,
                             GIMP_PARAM_STATIC_STRINGS);
 
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SHOW_ALL,
+                            "default-show-all",
+                            "Default show-all",
+                            DEFAULT_SHOW_ALL_BLURB,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_DOT_FOR_DOT,
                             "default-dot-for-dot",
                             "Default dot-for-dot",
@@ -401,6 +409,9 @@ gimp_display_config_set_property (GObject      *object,
     case PROP_RESIZE_WINDOWS_ON_RESIZE:
       display_config->resize_windows_on_resize = g_value_get_boolean (value);
       break;
+    case PROP_DEFAULT_SHOW_ALL:
+      display_config->default_show_all = g_value_get_boolean (value);
+      break;
     case PROP_DEFAULT_DOT_FOR_DOT:
       display_config->default_dot_for_dot = g_value_get_boolean (value);
       break;
@@ -507,6 +518,9 @@ gimp_display_config_get_property (GObject    *object,
     case PROP_RESIZE_WINDOWS_ON_RESIZE:
       g_value_set_boolean (value, display_config->resize_windows_on_resize);
       break;
+    case PROP_DEFAULT_SHOW_ALL:
+      g_value_set_boolean (value, display_config->default_show_all);
+      break;
     case PROP_DEFAULT_DOT_FOR_DOT:
       g_value_set_boolean (value, display_config->default_dot_for_dot);
       break;
diff --git a/app/config/gimpdisplayconfig.h b/app/config/gimpdisplayconfig.h
index 2c6c05b0dd..3fe2050047 100644
--- a/app/config/gimpdisplayconfig.h
+++ b/app/config/gimpdisplayconfig.h
@@ -47,6 +47,7 @@ struct _GimpDisplayConfig
   gint                marching_ants_speed;
   gboolean            resize_windows_on_zoom;
   gboolean            resize_windows_on_resize;
+  gboolean            default_show_all;
   gboolean            default_dot_for_dot;
   gboolean            initial_zoom_to_fit;
   GimpCursorMode      cursor_mode;
diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h
index 1858f81dff..5b1178237f 100644
--- a/app/config/gimprc-blurbs.h
+++ b/app/config/gimprc-blurbs.h
@@ -67,6 +67,9 @@ _("Context-dependent mouse pointers are helpful.  They are enabled by " \
 "Specify a default tool preset.  The tool preset is searched for in the " \
 "specified tool prests path."
 
+#define DEFAULT_SHOW_ALL_BLURB \
+_("Show full image content by default.")
+
 #define DEFAULT_DOT_FOR_DOT_BLURB \
 _("When enabled, this will ensure that each pixel of an image gets " \
   "mapped to a pixel on the screen.")
diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c
index b50d6c24d8..7f34d81fde 100644
--- a/app/dialogs/preferences-dialog.c
+++ b/app/dialogs/preferences-dialog.c
@@ -2807,6 +2807,10 @@ prefs_dialog_new (Gimp       *gimp,
   /*  General  */
   vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE);
 
+  prefs_check_button_add (object, "default-show-all",
+                          _("Use \"Show _all\" by default"),
+                          GTK_BOX (vbox2));
+
   prefs_check_button_add (object, "default-dot-for-dot",
                           _("Use \"_Dot for dot\" by default"),
                           GTK_BOX (vbox2));
@@ -2814,7 +2818,7 @@ prefs_dialog_new (Gimp       *gimp,
   table = prefs_table_new (1, GTK_CONTAINER (vbox2));
 
   prefs_spin_button_add (object, "marching-ants-speed", 1.0, 10.0, 0,
-                         _("Marching _ants speed:"),
+                         _("Marching ants s_peed:"),
                          GTK_TABLE (table), 0, size_group);
 
   /*  Zoom & Resize Behavior  */
diff --git a/app/display/gimpdisplay-handlers.c b/app/display/gimpdisplay-handlers.c
index a255d8c667..94cb863ad7 100644
--- a/app/display/gimpdisplay-handlers.c
+++ b/app/display/gimpdisplay-handlers.c
@@ -30,16 +30,21 @@
 
 /*  local function prototypes  */
 
-static void   gimp_display_update_handler (GimpProjection *projection,
-                                           gboolean        now,
-                                           gint            x,
-                                           gint            y,
-                                           gint            w,
-                                           gint            h,
-                                           GimpDisplay    *display);
-static void   gimp_display_flush_handler  (GimpImage      *image,
-                                           gboolean        invalidate_preview,
-                                           GimpDisplay    *display);
+static void   gimp_display_update_handler         (GimpProjection *projection,
+                                                   gboolean        now,
+                                                   gint            x,
+                                                   gint            y,
+                                                   gint            w,
+                                                   gint            h,
+                                                   GimpDisplay    *display);
+
+static void   gimp_display_bounds_changed_handler (GimpImage      *image,
+                                                   gint            old_x,
+                                                   gint            old_y,
+                                                   GimpDisplay    *display);
+static void   gimp_display_flush_handler          (GimpImage      *image,
+                                                   gboolean        invalidate_preview,
+                                                   GimpDisplay    *display);
 
 
 /*  public functions  */
@@ -59,6 +64,9 @@ gimp_display_connect (GimpDisplay *display)
                     G_CALLBACK (gimp_display_update_handler),
                     display);
 
+  g_signal_connect (image, "bounds-changed",
+                    G_CALLBACK (gimp_display_bounds_changed_handler),
+                    display);
   g_signal_connect (image, "flush",
                     G_CALLBACK (gimp_display_flush_handler),
                     display);
@@ -78,6 +86,9 @@ gimp_display_disconnect (GimpDisplay *display)
   g_signal_handlers_disconnect_by_func (image,
                                         gimp_display_flush_handler,
                                         display);
+  g_signal_handlers_disconnect_by_func (image,
+                                        gimp_display_bounds_changed_handler,
+                                        display);
 
   g_signal_handlers_disconnect_by_func (gimp_image_get_projection (image),
                                         gimp_display_update_handler,
@@ -99,6 +110,15 @@ gimp_display_update_handler (GimpProjection *projection,
   gimp_display_update_area (display, now, x, y, w, h);
 }
 
+static void
+gimp_display_bounds_changed_handler (GimpImage   *image,
+                                     gint         old_x,
+                                     gint         old_y,
+                                     GimpDisplay *display)
+{
+  gimp_display_update_bounding_box (display);
+}
+
 static void
 gimp_display_flush_handler (GimpImage   *image,
                             gboolean     invalidate_preview,
diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c
index 04439043a0..6eb9164d53 100644
--- a/app/display/gimpdisplay.c
+++ b/app/display/gimpdisplay.c
@@ -76,6 +76,8 @@ struct _GimpDisplayPrivate
   gint            instance;     /*  the instance # of this display as
                                  *  taken from the image at creation    */
 
+  GeglRectangle   bounding_box;
+
   GtkWidget      *shell;
   cairo_region_t *update_region;
 
@@ -450,6 +452,8 @@ gimp_display_new (Gimp              *gimp,
 
   shell = gimp_display_get_shell (display);
 
+  gimp_display_update_bounding_box (display);
+
   gimp_image_window_add_shell (window, shell);
   gimp_display_shell_present (shell);
 
@@ -685,6 +689,8 @@ gimp_display_set_image (GimpDisplay *display,
   if (old_image)
     g_object_unref (old_image);
 
+  gimp_display_update_bounding_box (display);
+
   if (shell)
     {
       if (image)
@@ -767,6 +773,48 @@ gimp_display_fill (GimpDisplay *display,
                            image, unit, scale);
 }
 
+void
+gimp_display_update_bounding_box (GimpDisplay *display)
+{
+  GimpDisplayPrivate *private;
+  GimpDisplayShell   *shell;
+  GeglRectangle       bounding_box = {};
+
+  g_return_if_fail (GIMP_IS_DISPLAY (display));
+
+  private = GIMP_DISPLAY_GET_PRIVATE (display);
+  shell   = gimp_display_get_shell (display);
+
+  if (shell)
+    {
+      bounding_box = gimp_display_shell_get_bounding_box (shell);
+
+      if (! gegl_rectangle_equal (&bounding_box, &private->bounding_box))
+        {
+          GeglRectangle diff_rects[4];
+          gint          n_diff_rects;
+          gint          i;
+
+          n_diff_rects = gegl_rectangle_subtract (diff_rects,
+                                                  &private->bounding_box,
+                                                  &bounding_box);
+
+          for (i = 0; i < n_diff_rects; i++)
+            {
+              gimp_display_paint_area (display,
+                                       diff_rects[i].x,     diff_rects[i].y,
+                                       diff_rects[i].width, diff_rects[i].height);
+            }
+
+          private->bounding_box = bounding_box;
+        }
+    }
+  else
+    {
+      private->bounding_box = bounding_box;
+    }
+}
+
 void
 gimp_display_update_area (GimpDisplay *display,
                           gboolean     now,
@@ -877,27 +925,25 @@ gimp_display_paint_area (GimpDisplay *display,
                          gint         w,
                          gint         h)
 {
-  GimpDisplayPrivate *private      = GIMP_DISPLAY_GET_PRIVATE (display);
-  GimpDisplayShell   *shell        = gimp_display_get_shell (display);
-  gint                image_width  = gimp_image_get_width  (private->image);
-  gint                image_height = gimp_image_get_height (private->image);
+  GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
+  GimpDisplayShell   *shell   = gimp_display_get_shell (display);
+  GeglRectangle       rect;
   gint                x1, y1, x2, y2;
   gdouble             x1_f, y1_f, x2_f, y2_f;
 
-  /*  Bounds check  */
-  x1 = CLAMP (x,     0, image_width);
-  y1 = CLAMP (y,     0, image_height);
-  x2 = CLAMP (x + w, 0, image_width);
-  y2 = CLAMP (y + h, 0, image_height);
-
-  x = x1;
-  y = y1;
-  w = (x2 - x1);
-  h = (y2 - y1);
+  if (! gegl_rectangle_intersect (&rect,
+                                  &private->bounding_box,
+                                  GEGL_RECTANGLE (x, y, w, h)))
+    {
+      return;
+    }
 
   /*  display the area  */
   gimp_display_shell_transform_bounds (shell,
-                                       x, y, x + w, y + h,
+                                       rect.x,
+                                       rect.y,
+                                       rect.x + rect.width,
+                                       rect.y + rect.height,
                                        &x1_f, &y1_f, &x2_f, &y2_f);
 
   /*  make sure to expose a superset of the transformed sub-pixel expose
diff --git a/app/display/gimpdisplay.h b/app/display/gimpdisplay.h
index 05b2dc6b20..7e676b0afd 100644
--- a/app/display/gimpdisplay.h
+++ b/app/display/gimpdisplay.h
@@ -82,6 +82,9 @@ void               gimp_display_fill            (GimpDisplay       *display,
                                                  GimpUnit           unit,
                                                  gdouble            scale);
 
+void               gimp_display_update_bounding_box
+                                                (GimpDisplay       *display);
+
 void               gimp_display_update_area     (GimpDisplay       *display,
                                                  gboolean           now,
                                                  gint               x,
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index fb4d69f1d1..bac6db8753 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -43,6 +43,7 @@
 #include "gimpdisplayshell-scrollbars.h"
 #include "gimpdisplayshell-selection.h"
 #include "gimpdisplayshell-title.h"
+#include "gimpdisplayshell-transform.h"
 #include "gimpdisplayxfer.h"
 #include "gimpimagewindow.h"
 #include "gimpnavigationeditor.h"
@@ -454,14 +455,18 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
                                       cairo_t          *cr)
 {
   cairo_rectangle_list_t *clip_rectangles;
-  cairo_rectangle_int_t   image_rect;
+  GeglRectangle           image_rect;
+  GeglRectangle           rotated_image_rect;
   cairo_matrix_t          matrix;
+  gdouble                 x1, y1;
+  gdouble                 x2, y2;
 
-  image_rect.x = - shell->offset_x;
-  image_rect.y = - shell->offset_y;
-  gimp_display_shell_scale_get_image_size (shell,
-                                           &image_rect.width,
-                                           &image_rect.height);
+  gimp_display_shell_scale_get_image_unrotated_bounding_box (
+    shell,
+    &image_rect.x,
+    &image_rect.y,
+    &image_rect.width,
+    &image_rect.height);
 
 
   /*  the background has already been cleared by GdkWindow
@@ -479,6 +484,13 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
   if (shell->rotate_transform)
     cairo_transform (cr, shell->rotate_transform);
 
+  if (shell->show_all)
+    {
+      cairo_save (cr);
+      gimp_display_shell_draw_checkerboard (shell, cr);
+      cairo_restore (cr);
+    }
+
   cairo_rectangle (cr,
                    image_rect.x,
                    image_rect.y,
@@ -486,13 +498,28 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
                    image_rect.height);
   cairo_clip (cr);
 
+  gimp_display_shell_rotate_bounds (shell,
+                                    image_rect.x,
+                                    image_rect.y,
+                                    image_rect.x + image_rect.width,
+                                    image_rect.y + image_rect.height,
+                                    &x1, &y1, &x2, &y2);
+
+  rotated_image_rect.x      = floor (x1);
+  rotated_image_rect.y      = floor (y1);
+  rotated_image_rect.width  = ceil  (x2) - rotated_image_rect.x;
+  rotated_image_rect.height = ceil  (y2) - rotated_image_rect.y;
+
   if (gdk_cairo_get_clip_rectangle (cr, NULL))
     {
       gint i;
 
-      cairo_save (cr);
-      gimp_display_shell_draw_checkerboard (shell, cr);
-      cairo_restore (cr);
+      if (! shell->show_all)
+        {
+          cairo_save (cr);
+          gimp_display_shell_draw_checkerboard (shell, cr);
+          cairo_restore (cr);
+        }
 
       if (shell->show_image)
         {
@@ -500,13 +527,20 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
 
           for (i = 0; i < clip_rectangles->num_rectangles; i++)
             {
-              cairo_rectangle_t rect = clip_rectangles->rectangles[i];
-
-              gimp_display_shell_draw_image (shell, cr,
-                                             floor (rect.x),
-                                             floor (rect.y),
-                                             ceil (rect.width),
-                                             ceil (rect.height));
+              cairo_rectangle_t clip_rect = clip_rectangles->rectangles[i];
+              GeglRectangle     rect;
+
+              rect.x      = floor (clip_rect.x);
+              rect.y      = floor (clip_rect.y);
+              rect.width  = ceil  (clip_rect.x + clip_rect.width)  - rect.x;
+              rect.height = ceil  (clip_rect.y + clip_rect.height) - rect.y;
+
+              if (gegl_rectangle_intersect (&rect, &rect, &rotated_image_rect))
+                {
+                  gimp_display_shell_draw_image (shell, cr,
+                                                 rect.x,     rect.y,
+                                                 rect.width, rect.height);
+                }
             }
         }
     }
diff --git a/app/display/gimpdisplayshell-handlers.c b/app/display/gimpdisplayshell-handlers.c
index 85539a5798..d35da74f81 100644
--- a/app/display/gimpdisplayshell-handlers.c
+++ b/app/display/gimpdisplayshell-handlers.c
@@ -386,6 +386,13 @@ gimp_display_shell_connect (GimpDisplayShell *shell)
 
   gimp_canvas_layer_boundary_set_layer (GIMP_CANVAS_LAYER_BOUNDARY (shell->layer_boundary),
                                         gimp_image_get_active_layer (image));
+
+  if (shell->show_all)
+    {
+      gimp_image_inc_show_all_count (image);
+
+      gimp_image_flush (image);
+    }
 }
 
 void
@@ -549,6 +556,13 @@ gimp_display_shell_disconnect (GimpDisplayShell *shell)
   g_signal_handlers_disconnect_by_func (image,
                                         gimp_display_shell_clean_dirty_handler,
                                         shell);
+
+  if (shell->show_all)
+    {
+      gimp_image_dec_show_all_count (image);
+
+      gimp_image_flush (image);
+    }
 }
 
 
diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c
index 0470d7b73f..a1525bf70f 100644
--- a/app/display/gimpdisplayshell-render.c
+++ b/app/display/gimpdisplayshell-render.c
@@ -59,6 +59,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
 #ifdef USE_NODE_BLIT
   GeglNode        *node;
 #endif
+  GeglAbyssPolicy  abyss_policy;
   cairo_surface_t *xfer;
   gint             xfer_src_x;
   gint             xfer_src_y;
@@ -74,13 +75,19 @@ gimp_display_shell_render (GimpDisplayShell *shell,
   g_return_if_fail (scale > 0.0);
 
   image  = gimp_display_get_image (shell->display);
-  buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));
+  buffer = gimp_pickable_get_buffer (
+    gimp_display_shell_get_pickable (shell));
 #ifdef USE_NODE_BLIT
   node   = gimp_projectable_get_graph (GIMP_PROJECTABLE (image));
 
   gimp_projectable_begin_render (GIMP_PROJECTABLE (image));
 #endif
 
+  if (shell->show_all)
+    abyss_policy = GEGL_ABYSS_NONE;
+  else
+    abyss_policy = GEGL_ABYSS_CLAMP;
+
   xfer = gimp_display_xfer_get_surface (shell->xfer, w, h,
                                         &xfer_src_x, &xfer_src_y);
 
@@ -134,7 +141,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                            GEGL_RECTANGLE (x, y, w, h), scale,
                            gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
                            shell->profile_data, shell->profile_stride,
-                           GEGL_ABYSS_CLAMP);
+                           abyss_policy);
 #else
           gegl_node_blit (node,
                           scale, GEGL_RECTANGLE (x, y, w, h),
@@ -152,7 +159,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                            GEGL_RECTANGLE (x, y, w, h), scale,
                            shell->filter_format,
                            shell->filter_data, shell->filter_stride,
-                           GEGL_ABYSS_CLAMP);
+                           abyss_policy);
 #else
           gegl_node_blit (node,
                           scale, GEGL_RECTANGLE (x, y, w, h),
@@ -255,7 +262,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                            GEGL_RECTANGLE (0, 0, w, h), 1.0,
                            babl_format ("cairo-ARGB32"),
                            cairo_data, cairo_stride,
-                           GEGL_ABYSS_CLAMP);
+                           GEGL_ABYSS_NONE);
         }
     }
   else
@@ -268,7 +275,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                        GEGL_RECTANGLE (x, y, w, h), scale,
                        babl_format ("cairo-ARGB32"),
                        cairo_data, cairo_stride,
-                       GEGL_ABYSS_CLAMP);
+                       abyss_policy);
 #else
       gegl_node_blit (node,
                       scale, GEGL_RECTANGLE (x, y, w, h),
diff --git a/app/display/gimpdisplayshell-scale.c b/app/display/gimpdisplayshell-scale.c
index cb86b2864b..c9dff0968b 100644
--- a/app/display/gimpdisplayshell-scale.c
+++ b/app/display/gimpdisplayshell-scale.c
@@ -274,6 +274,118 @@ gimp_display_shell_scale_get_image_bounds (GimpDisplayShell *shell,
   if (h) *h = y2 - y1;
 }
 
+/**
+ * gimp_display_shell_scale_get_image_bounding_box:
+ * @shell:
+ * @x:
+ * @y:
+ * @w:
+ * @h:
+ *
+ * Gets the screen-space boudning box of the image content, after it has
+ * been transformed (i.e., scaled, rotated, and scrolled).
+ **/
+void
+gimp_display_shell_scale_get_image_bounding_box (GimpDisplayShell *shell,
+                                                 gint             *x,
+                                                 gint             *y,
+                                                 gint             *w,
+                                                 gint             *h)
+{
+  GeglRectangle bounding_box;
+  gdouble       x1, y1;
+  gdouble       x2, y2;
+
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  bounding_box = gimp_display_shell_get_bounding_box (shell);
+
+  gimp_display_shell_transform_bounds (shell,
+                                       bounding_box.x,
+                                       bounding_box.y,
+                                       bounding_box.x + bounding_box.width,
+                                       bounding_box.y + bounding_box.height,
+                                       &x1, &y1,
+                                       &x2, &y2);
+
+  if (! shell->show_all)
+    {
+      x1 = ceil  (x1);
+      y1 = ceil  (y1);
+      x2 = floor (x2);
+      y2 = floor (y2);
+    }
+  else
+    {
+      x1 = floor (x1);
+      y1 = floor (y1);
+      x2 = ceil  (x2);
+      y2 = ceil  (y2);
+    }
+
+  if (x) *x = x1 + shell->offset_x;
+  if (y) *y = y1 + shell->offset_y;
+  if (w) *w = x2 - x1;
+  if (h) *h = y2 - y1;
+}
+
+/**
+ * gimp_display_shell_scale_get_image_unrotated_bounding_box:
+ * @shell:
+ * @x:
+ * @y:
+ * @w:
+ * @h:
+ *
+ * Gets the screen-space boudning box of the image content, after it has
+ * been scaled and scrolled, but before it has been rotated.
+ **/
+void
+gimp_display_shell_scale_get_image_unrotated_bounding_box (GimpDisplayShell *shell,
+                                                           gint             *x,
+                                                           gint             *y,
+                                                           gint             *w,
+                                                           gint             *h)
+{
+  GeglRectangle bounding_box;
+  gdouble       x1, y1;
+  gdouble       x2, y2;
+
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  bounding_box = gimp_display_shell_get_bounding_box (shell);
+
+  x1 = bounding_box.x * shell->scale_x -
+       shell->offset_x;
+  y1 = bounding_box.y * shell->scale_y -
+       shell->offset_y;
+
+  x2 = (bounding_box.x + bounding_box.width)  * shell->scale_x -
+       shell->offset_x;
+  y2 = (bounding_box.y + bounding_box.height) * shell->scale_y -
+       shell->offset_y;
+
+  if (! shell->show_all)
+    {
+      x1 = ceil  (x1);
+      y1 = ceil  (y1);
+      x2 = floor (x2);
+      y2 = floor (y2);
+    }
+  else
+    {
+      x1 = floor (x1);
+      y1 = floor (y1);
+      x2 = ceil  (x2);
+      y2 = ceil  (y2);
+    }
+
+  if (x) *x = x1;
+  if (y) *y = y1;
+  if (w) *w = x2 - x1;
+  if (h) *h = y2 - y1;
+}
+
 /**
  * gimp_display_shell_scale_image_is_within_viewport:
  * @shell:
diff --git a/app/display/gimpdisplayshell-scale.h b/app/display/gimpdisplayshell-scale.h
index 8489a1545a..7f38eb0264 100644
--- a/app/display/gimpdisplayshell-scale.h
+++ b/app/display/gimpdisplayshell-scale.h
@@ -34,6 +34,18 @@ void     gimp_display_shell_scale_get_image_bounds   (GimpDisplayShell *shell,
                                                       gint             *y,
                                                       gint             *w,
                                                       gint             *h);
+void     gimp_display_shell_scale_get_image_bounding_box
+                                                     (GimpDisplayShell *shell,
+                                                      gint             *x,
+                                                      gint             *y,
+                                                      gint             *w,
+                                                      gint             *h);
+void     gimp_display_shell_scale_get_image_unrotated_bounding_box
+                                                     (GimpDisplayShell *shell,
+                                                      gint             *x,
+                                                      gint             *y,
+                                                      gint             *w,
+                                                      gint             *h);
 gboolean gimp_display_shell_scale_image_is_within_viewport
                                                      (GimpDisplayShell *shell,
                                                       gboolean         *horizontally,
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index aee598a969..b24d2da60d 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -43,6 +43,8 @@
 #include "core/gimpimage-grid.h"
 #include "core/gimpimage-guides.h"
 #include "core/gimpimage-snap.h"
+#include "core/gimppickable.h"
+#include "core/gimpprojectable.h"
 #include "core/gimpprojection.h"
 #include "core/gimpmarshal.h"
 #include "core/gimptemplate.h"
@@ -98,7 +100,8 @@ enum
   PROP_UNIT,
   PROP_TITLE,
   PROP_STATUS,
-  PROP_ICON
+  PROP_ICON,
+  PROP_SHOW_ALL
 };
 
 enum
@@ -303,6 +306,12 @@ gimp_display_shell_class_init (GimpDisplayShellClass *klass)
                                                         GDK_TYPE_PIXBUF,
                                                         GIMP_PARAM_READWRITE));
 
+  g_object_class_install_property (object_class, PROP_SHOW_ALL,
+                                   g_param_spec_boolean ("show-all",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE));
+
   gtk_rc_parse_string (display_rc_style);
 }
 
@@ -328,6 +337,8 @@ gimp_display_shell_init (GimpDisplayShell *shell)
 
   shell->show_image  = TRUE;
 
+  shell->show_all    = FALSE;
+
   gimp_display_shell_items_init (shell);
 
   shell->icon_size       = 128;
@@ -790,6 +801,8 @@ gimp_display_shell_constructed (GObject *object)
 
   /* make sure the information is up-to-date */
   gimp_display_shell_scale_update (shell);
+
+  gimp_display_shell_set_show_all (shell, config->default_show_all);
 }
 
 static void
@@ -911,6 +924,9 @@ gimp_display_shell_set_property (GObject      *object,
         g_object_unref (shell->icon);
       shell->icon = g_value_dup_object (value);
       break;
+    case PROP_SHOW_ALL:
+      gimp_display_shell_set_show_all (shell, g_value_get_boolean (value));
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -952,6 +968,9 @@ gimp_display_shell_get_property (GObject    *object,
     case PROP_ICON:
       g_value_set_object (value, shell->icon);
       break;
+    case PROP_SHOW_ALL:
+      g_value_set_boolean (value, shell->show_all);
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1500,6 +1519,8 @@ gimp_display_shell_fill (GimpDisplayShell *shell,
   config = shell->display->config;
   window = gimp_display_shell_get_window (shell);
 
+  shell->show_image  = TRUE;
+
   shell->dot_for_dot = config->default_dot_for_dot;
 
   gimp_display_shell_set_unit (shell, unit);
@@ -1534,6 +1555,8 @@ gimp_display_shell_fill (GimpDisplayShell *shell,
     g_idle_add_full (GIMP_PRIORITY_DISPLAY_SHELL_FILL_IDLE,
                      (GSourceFunc) gimp_display_shell_fill_idle, shell,
                      NULL);
+
+  gimp_display_shell_set_show_all (shell, config->default_show_all);
 }
 
 void
@@ -1800,6 +1823,84 @@ gimp_display_shell_set_show_image (GimpDisplayShell *shell,
     }
 }
 
+void
+gimp_display_shell_set_show_all (GimpDisplayShell *shell,
+                                 gboolean          show_all)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  if (show_all != shell->show_all)
+    {
+      GimpImage *image = gimp_display_get_image (shell->display);
+
+      shell->show_all = show_all;
+
+      if (image)
+        {
+          if (show_all)
+            gimp_image_inc_show_all_count (image);
+          else
+            gimp_image_dec_show_all_count (image);
+
+          gimp_image_flush (image);
+        }
+
+      gimp_display_update_bounding_box (shell->display);
+
+      gimp_display_shell_expose_full (shell);
+
+      g_object_notify (G_OBJECT (shell), "show-all");
+    }
+}
+
+
+GimpPickable *
+gimp_display_shell_get_pickable (GimpDisplayShell *shell)
+{
+  GimpImage *image;
+
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+  image = gimp_display_get_image (shell->display);
+
+  if (image)
+    {
+      if (! shell->show_all)
+        return GIMP_PICKABLE (image);
+      else
+        return GIMP_PICKABLE (gimp_image_get_projection (image));
+    }
+
+  return NULL;
+}
+
+GeglRectangle
+gimp_display_shell_get_bounding_box (GimpDisplayShell *shell)
+{
+  GeglRectangle  bounding_box = {};
+  GimpImage     *image;
+
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), bounding_box);
+
+  image = gimp_display_get_image (shell->display);
+
+  if (image)
+    {
+      if (! shell->show_all)
+        {
+          bounding_box.width  = gimp_image_get_width  (image);
+          bounding_box.height = gimp_image_get_height (image);
+        }
+      else
+        {
+          bounding_box = gimp_projectable_get_bounding_box (
+            GIMP_PROJECTABLE (image));
+        }
+    }
+
+  return bounding_box;
+}
+
 void
 gimp_display_shell_flush (GimpDisplayShell *shell,
                           gboolean          now)
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index e938bb5035..d331a762df 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -93,6 +93,8 @@ struct _GimpDisplayShell
 
   gboolean           show_image;       /*  whether to show the image          */
 
+  gboolean           show_all;         /*  show the entire image              */
+
   Selection         *selection;        /*  Selection (marching ants)          */
 
   GList             *children;
@@ -303,6 +305,13 @@ void              gimp_display_shell_set_show_image
                                                    (GimpDisplayShell   *shell,
                                                     gboolean            show_image);
 
+void              gimp_display_shell_set_show_all  (GimpDisplayShell   *shell,
+                                                    gboolean            show_all);
+
+GimpPickable    * gimp_display_shell_get_pickable  (GimpDisplayShell   *shell);
+GeglRectangle     gimp_display_shell_get_bounding_box
+                                                   (GimpDisplayShell   *shell);
+
 void              gimp_display_shell_flush         (GimpDisplayShell   *shell,
                                                     gboolean            now);
 
diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h
index c873895db9..ccf29203ee 100644
--- a/app/widgets/gimphelp-ids.h
+++ b/app/widgets/gimphelp-ids.h
@@ -83,6 +83,7 @@
 #define GIMP_HELP_SELECTION_TO_PATH               "gimp-selection-to-path"
 
 #define GIMP_HELP_VIEW_NEW                        "gimp-view-new"
+#define GIMP_HELP_VIEW_SHOW_ALL                   "gimp-view-show-all"
 #define GIMP_HELP_VIEW_DOT_FOR_DOT                "gimp-view-dot-for-dot"
 #define GIMP_HELP_VIEW_SCROLL_CENTER              "gimp-view-scroll-center"
 #define GIMP_HELP_VIEW_ZOOM_REVERT                "gimp-view-zoom-revert"
diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in
index 22d353e0f7..cfa9cfd4d4 100644
--- a/menus/image-menu.xml.in
+++ b/menus/image-menu.xml.in
@@ -258,6 +258,7 @@
 
     <menu action="view-menu" name="View">
       <menuitem action="view-new" />
+      <menuitem action="view-show-all" />
       <menuitem action="view-dot-for-dot" />
       <menu action="view-zoom-menu" name="Zoom">
         <menuitem action="view-zoom-revert" />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]