[gimp] Bug 55367 - Rotated view of the canvas (view is rotated, not image contents)



commit b3a9a6a3e39567cd801b92bca5dfae5a37eb4193
Author: Michael Natterer <mitch gimp org>
Date:   Fri Apr 19 16:22:19 2013 +0200

    Bug 55367 - Rotated view of the canvas (view is rotated, not image contents)
    
    First version of display rotation, inspired by gimp-painter.
    The rotation always happens around the image's center.
    
    The only "UI" for rotating is currently shift+middle-drag and
    shift+space-drag. Control constrains the angle to 15 degrees
    and is currently the only way to go back to "no rotation".

 app/display/Makefile.am                    |   2 +
 app/display/gimpcanvasitem.c               |  11 +-
 app/display/gimpdisplay.c                  |   5 +-
 app/display/gimpdisplayshell-callbacks.c   |  21 ++-
 app/display/gimpdisplayshell-draw.c        |  45 ++++++-
 app/display/gimpdisplayshell-items.c       |  38 +++++-
 app/display/gimpdisplayshell-rotate.c      | 201 +++++++++++++++++++++++++++++
 app/display/gimpdisplayshell-rotate.h      |  51 ++++++++
 app/display/gimpdisplayshell-scroll.c      |   3 +
 app/display/gimpdisplayshell-selection.c   |   6 +
 app/display/gimpdisplayshell-tool-events.c |  51 ++++++--
 app/display/gimpdisplayshell-transform.c   | 125 +++++++++++++++++-
 app/display/gimpdisplayshell-transform.h   |  19 +++
 app/display/gimpdisplayshell.c             |  20 ++-
 app/display/gimpdisplayshell.h             |   6 +
 15 files changed, 568 insertions(+), 36 deletions(-)
---
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index de9fc49..3539dc2 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -113,6 +113,8 @@ libappdisplay_a_sources = \
        gimpdisplayshell-progress.h             \
        gimpdisplayshell-render.c               \
        gimpdisplayshell-render.h               \
+       gimpdisplayshell-rotate.c               \
+       gimpdisplayshell-rotate.h               \
        gimpdisplayshell-scale.c                \
        gimpdisplayshell-scale.h                \
        gimpdisplayshell-scale-dialog.c         \
diff --git a/app/display/gimpcanvasitem.c b/app/display/gimpcanvasitem.c
index 093eaf3..9e1a260 100644
--- a/app/display/gimpcanvasitem.c
+++ b/app/display/gimpcanvasitem.c
@@ -629,12 +629,18 @@ gimp_canvas_item_transform_xy (GimpCanvasItem *item,
                                gint           *ty)
 {
   GimpCanvasItemPrivate *private;
+  gint64                 nx;
+  gint64                 ny;
 
   g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
 
   private = GET_PRIVATE (item);
 
-  gimp_display_shell_transform_xy (private->shell, x, y, tx, ty);
+  nx = x * private->shell->scale_x - private->shell->offset_x;
+  ny = y * private->shell->scale_y - private->shell->offset_y;
+
+  *tx = CLAMP (nx, G_MININT, G_MAXINT);
+  *ty = CLAMP (ny, G_MININT, G_MAXINT);
 }
 
 void
@@ -650,7 +656,8 @@ gimp_canvas_item_transform_xy_f (GimpCanvasItem *item,
 
   private = GET_PRIVATE (item);
 
-  gimp_display_shell_transform_xy_f (private->shell, x, y, tx, ty);
+  *tx = SCALEX (private->shell, x) - private->shell->offset_x;
+  *ty = SCALEY (private->shell, y) - private->shell->offset_y;
 }
 
 
diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c
index 880b9bd..3e0b746 100644
--- a/app/display/gimpdisplay.c
+++ b/app/display/gimpdisplay.c
@@ -886,8 +886,9 @@ gimp_display_paint_area (GimpDisplay *display,
   h = (y2 - y1);
 
   /*  display the area  */
-  gimp_display_shell_transform_xy_f (shell, x,     y,     &x1_f, &y1_f);
-  gimp_display_shell_transform_xy_f (shell, x + w, y + h, &x2_f, &y2_f);
+  gimp_display_shell_transform_bounds (shell,
+                                       x, y, x + w, y + h,
+                                       &x1_f, &y1_f, &x2_f, &y2_f);
 
   /*  make sure to expose a superset of the transformed sub-pixel expose
    *  area, not a subset. bug #126942. --mitch
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index 3209ba4..5861c0a 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -454,7 +454,8 @@ static void
 gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
                                       cairo_t          *cr)
 {
-  cairo_rectangle_int_t image_rect;
+  cairo_rectangle_list_t *clip_rectangles;
+  cairo_rectangle_int_t   image_rect;
 
   image_rect.x = - shell->offset_x;
   image_rect.y = - shell->offset_y;
@@ -469,6 +470,9 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
 
   cairo_save (cr);
 
+  if (shell->rotate_transform)
+    cairo_transform (cr, shell->rotate_transform);
+
   cairo_rectangle (cr,
                    image_rect.x,
                    image_rect.y,
@@ -489,6 +493,10 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
    */
 
   cairo_save (cr);
+  clip_rectangles = cairo_copy_clip_rectangle_list (cr);
+
+  if (shell->rotate_transform)
+    cairo_transform (cr, shell->rotate_transform);
 
   cairo_rectangle (cr,
                    image_rect.x,
@@ -499,15 +507,12 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
 
   if (gdk_cairo_get_clip_rectangle (cr, NULL))
     {
-      cairo_rectangle_list_t *clip_rectangles;
-      gint                    i;
+      gint i;
 
       cairo_save (cr);
       gimp_display_shell_draw_checkerboard (shell, cr);
       cairo_restore (cr);
 
-      clip_rectangles = cairo_copy_clip_rectangle_list (cr);
-
       for (i = 0; i < clip_rectangles->num_rectangles; i++)
         {
           cairo_rectangle_t rect = clip_rectangles->rectangles[i];
@@ -518,10 +523,9 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
                                          ceil (rect.width),
                                          ceil (rect.height));
         }
-
-      cairo_rectangle_list_destroy (clip_rectangles);
     }
 
+  cairo_rectangle_list_destroy (clip_rectangles);
   cairo_restore (cr);
 
 
@@ -529,6 +533,9 @@ gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell,
    */
 
   /* draw canvas items */
+  if (shell->rotate_transform)
+    cairo_transform (cr, shell->rotate_transform);
+
   gimp_canvas_item_draw (shell->canvas_item, cr);
 
   /* restart (and recalculate) the selection boundaries */
diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c
index 075b2ec..0e0e032 100644
--- a/app/display/gimpdisplayshell-draw.c
+++ b/app/display/gimpdisplayshell-draw.c
@@ -28,6 +28,7 @@
 #include "display-types.h"
 
 #include "core/gimp-cairo.h"
+#include "core/gimp-utils.h"
 
 #include "gimpcanvas.h"
 #include "gimpcanvas-style.h"
@@ -36,6 +37,8 @@
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-draw.h"
 #include "gimpdisplayshell-render.h"
+#include "gimpdisplayshell-rotate.h"
+#include "gimpdisplayshell-scale.h"
 #include "gimpdisplayxfer.h"
 
 
@@ -132,22 +135,54 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
                                gint              w,
                                gint              h)
 {
-  gint x2, y2;
+  gint x1, y1, x2, y2;
   gint i, j;
 
   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
   g_return_if_fail (gimp_display_get_image (shell->display));
   g_return_if_fail (cr != NULL);
 
-  x2 = x + w;
-  y2 = y + h;
+  if (shell->rotate_untransform)
+    {
+      gdouble tx1, ty1;
+      gdouble tx2, ty2;
+      gint    image_width;
+      gint    image_height;
+
+      gimp_display_shell_rotate_untransform_bounds (shell,
+                                                    x, y, x + w, y + h,
+                                                    &tx1, &ty1, &tx2, &ty2);
+
+      x1 = floor (tx1 - 0.5);
+      y1 = floor (ty1 - 0.5);
+      x2 = ceil (tx2 + 0.5);
+      y2 = ceil (ty2 + 0.5);
+
+      gimp_display_shell_scale_get_image_size (shell,
+                                               &image_width, &image_height);
+
+      x1 = CLAMP (x1, -shell->offset_x, -shell->offset_x + image_width);
+      y1 = CLAMP (y1, -shell->offset_y, -shell->offset_y + image_height);
+      x2 = CLAMP (x2, -shell->offset_x, -shell->offset_x + image_width);
+      y2 = CLAMP (y2, -shell->offset_y, -shell->offset_y + image_height);
+
+      if (!(x2 > x1) || !(y2 > y1))
+        return;
+    }
+  else
+    {
+      x1 = x;
+      y1 = y;
+      x2 = x + w;
+      y2 = y + h;
+    }
 
   /*  display the image in RENDER_BUF_WIDTH x RENDER_BUF_HEIGHT
    *  sized chunks
    */
-  for (i = y; i < y2; i += GIMP_DISPLAY_RENDER_BUF_HEIGHT)
+  for (i = y1; i < y2; i += GIMP_DISPLAY_RENDER_BUF_HEIGHT)
     {
-      for (j = x; j < x2; j += GIMP_DISPLAY_RENDER_BUF_WIDTH)
+      for (j = x1; j < x2; j += GIMP_DISPLAY_RENDER_BUF_WIDTH)
         {
           gint dx, dy;
 
diff --git a/app/display/gimpdisplayshell-items.c b/app/display/gimpdisplayshell-items.c
index 9cb1261..0e3ef2d 100644
--- a/app/display/gimpdisplayshell-items.c
+++ b/app/display/gimpdisplayshell-items.c
@@ -22,6 +22,8 @@
 
 #include <gtk/gtk.h>
 
+#include <libgimpmath/gimpmath.h>
+
 #include "display-types.h"
 
 #include "gimpcanvascursor.h"
@@ -32,6 +34,7 @@
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-expose.h"
 #include "gimpdisplayshell-items.h"
+#include "gimpdisplayshell-rotate.h"
 
 
 /*  local function prototypes  */
@@ -190,5 +193,38 @@ gimp_display_shell_item_update (GimpCanvasItem   *item,
                                 cairo_region_t   *region,
                                 GimpDisplayShell *shell)
 {
-  gimp_display_shell_expose_region (shell, region);
+  if (shell->rotate_transform)
+    {
+      gint n_rects;
+      gint i;
+
+      n_rects = cairo_region_num_rectangles (region);
+
+      for (i = 0; i < n_rects; i++)
+        {
+          cairo_rectangle_int_t rect;
+          gdouble               tx1, ty1;
+          gdouble               tx2, ty2;
+          gint                  x1, y1, x2, y2;
+
+          cairo_region_get_rectangle (region, i, &rect);
+
+          gimp_display_shell_rotate_transform_bounds (shell,
+                                                      rect.x, rect.y,
+                                                      rect.x + rect.width,
+                                                      rect.y + rect.height,
+                                                      &tx1, &ty1, &tx2, &ty2);
+
+          x1 = floor (tx1 - 0.5);
+          y1 = floor (ty1 - 0.5);
+          x2 = ceil (tx2 + 0.5);
+          y2 = ceil (ty2 + 0.5);
+
+          gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1);
+        }
+    }
+  else
+    {
+      gimp_display_shell_expose_region (shell, region);
+    }
 }
diff --git a/app/display/gimpdisplayshell-rotate.c b/app/display/gimpdisplayshell-rotate.c
new file mode 100644
index 0000000..d04ce16
--- /dev/null
+++ b/app/display/gimpdisplayshell-rotate.c
@@ -0,0 +1,201 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "display-types.h"
+
+#include "core/gimp-utils.h"
+
+#include "gimpdisplay.h"
+#include "gimpdisplayshell.h"
+#include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-rotate.h"
+#include "gimpdisplayshell-scale.h"
+
+
+/*  public functions  */
+
+void
+gimp_display_shell_rotate_update_transform (GimpDisplayShell *shell)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  g_free (shell->rotate_transform);
+  g_free (shell->rotate_untransform);
+
+  if (shell->rotate_angle != 0.0 && gimp_display_get_image (shell->display))
+    {
+      gint    image_width, image_height;
+      gdouble cx, cy;
+
+      shell->rotate_transform   = g_new (cairo_matrix_t, 1);
+      shell->rotate_untransform = g_new (cairo_matrix_t, 1);
+
+      gimp_display_shell_scale_get_image_size (shell,
+                                               &image_width, &image_height);
+
+      cx = -shell->offset_x + image_width  / 2;
+      cy = -shell->offset_y + image_height / 2;
+
+      cairo_matrix_init_translate (shell->rotate_transform, cx, cy);
+      cairo_matrix_rotate (shell->rotate_transform,
+                           shell->rotate_angle / 180.0 * G_PI);
+      cairo_matrix_translate (shell->rotate_transform, -cx, -cy);
+
+      *shell->rotate_untransform = *shell->rotate_transform;
+      cairo_matrix_invert (shell->rotate_untransform);
+    }
+  else
+    {
+      shell->rotate_transform   = NULL;
+      shell->rotate_untransform = NULL;
+    }
+}
+
+void
+gimp_display_shell_rotate_transform_bounds (GimpDisplayShell *shell,
+                                            gdouble           x1,
+                                            gdouble           y1,
+                                            gdouble           x2,
+                                            gdouble           y2,
+                                            gdouble          *nx1,
+                                            gdouble          *ny1,
+                                            gdouble          *nx2,
+                                            gdouble          *ny2)
+{
+  gdouble tx1, ty1;
+  gdouble tx2, ty2;
+  gdouble tx3, ty3;
+  gdouble tx4, ty4;
+
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  tx1 = x1;
+  ty1 = y1;
+  tx2 = x1;
+  ty2 = y2;
+  tx3 = x2;
+  ty3 = y1;
+  tx4 = x2;
+  ty4 = y2;
+
+  cairo_matrix_transform_point (shell->rotate_transform, &tx1, &ty1);
+  cairo_matrix_transform_point (shell->rotate_transform, &tx2, &ty2);
+  cairo_matrix_transform_point (shell->rotate_transform, &tx3, &ty3);
+  cairo_matrix_transform_point (shell->rotate_transform, &tx4, &ty4);
+
+  *nx1 = MIN4 (tx1, tx2, tx3, tx4);
+  *ny1 = MIN4 (ty1, ty2, ty3, ty4);
+  *nx2 = MAX4 (tx1, tx2, tx3, tx4);
+  *ny2 = MAX4 (ty1, ty2, ty3, ty4);
+}
+
+void
+gimp_display_shell_rotate_untransform_bounds (GimpDisplayShell *shell,
+                                              gdouble           x1,
+                                              gdouble           y1,
+                                              gdouble           x2,
+                                              gdouble           y2,
+                                              gdouble          *nx1,
+                                              gdouble          *ny1,
+                                              gdouble          *nx2,
+                                              gdouble          *ny2)
+{
+  gdouble tx1, ty1;
+  gdouble tx2, ty2;
+  gdouble tx3, ty3;
+  gdouble tx4, ty4;
+
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  tx1 = x1;
+  ty1 = y1;
+  tx2 = x1;
+  ty2 = y2;
+  tx3 = x2;
+  ty3 = y1;
+  tx4 = x2;
+  ty4 = y2;
+
+  cairo_matrix_transform_point (shell->rotate_untransform, &tx1, &ty1);
+  cairo_matrix_transform_point (shell->rotate_untransform, &tx2, &ty2);
+  cairo_matrix_transform_point (shell->rotate_untransform, &tx3, &ty3);
+  cairo_matrix_transform_point (shell->rotate_untransform, &tx4, &ty4);
+
+  *nx1 = MIN4 (tx1, tx2, tx3, tx4);
+  *ny1 = MIN4 (ty1, ty2, ty3, ty4);
+  *nx2 = MAX4 (tx1, tx2, tx3, tx4);
+  *ny2 = MAX4 (ty1, ty2, ty3, ty4);
+}
+
+void
+gimp_display_shell_rotate_drag (GimpDisplayShell *shell,
+                                gdouble           last_x,
+                                gdouble           last_y,
+                                gdouble           cur_x,
+                                gdouble           cur_y,
+                                gboolean          constrain)
+{
+  gint    image_width, image_height;
+  gdouble px, py;
+  gdouble x1, y1, x2, y2;
+  gdouble angle1, angle2, angle;
+
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  gimp_display_shell_scale_get_image_size (shell,
+                                           &image_width, &image_height);
+
+  px = -shell->offset_x + image_width  / 2;
+  py = -shell->offset_y + image_height / 2;
+
+  x1 = cur_x  - px;
+  x2 = last_x - px;
+  y1 = py - cur_y;
+  y2 = py - last_y;
+
+  /*  find the first angle  */
+  angle1 = atan2 (y1, x1);
+
+  /*  find the angle  */
+  angle2 = atan2 (y2, x2);
+
+  angle = angle2 - angle1;
+
+  if (angle > G_PI || angle < -G_PI)
+    angle = angle2 - ((angle1 < 0) ? 2.0 * G_PI + angle1 : angle1 - 2.0 * G_PI);
+
+  shell->rotate_drag_angle += (angle * 180.0 / G_PI);
+
+  if (constrain)
+    {
+      shell->rotate_angle = (gint) (((gint) shell->rotate_drag_angle / 15) * 15);
+    }
+  else
+    {
+      shell->rotate_angle = shell->rotate_drag_angle;
+    }
+
+  gimp_display_shell_rotate_update_transform (shell);
+  gimp_display_shell_expose_full (shell);
+}
diff --git a/app/display/gimpdisplayshell-rotate.h b/app/display/gimpdisplayshell-rotate.h
new file mode 100644
index 0000000..0c71bab
--- /dev/null
+++ b/app/display/gimpdisplayshell-rotate.h
@@ -0,0 +1,51 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_DISPLAY_SHELL_ROTATE_H__
+#define __GIMP_DISPLAY_SHELL_ROTATE_H__
+
+
+void     gimp_display_shell_rotate_update_transform   (GimpDisplayShell *shell);
+
+void     gimp_display_shell_rotate_transform_bounds   (GimpDisplayShell *shell,
+                                                       gdouble           x1,
+                                                       gdouble           y1,
+                                                       gdouble           x2,
+                                                       gdouble           y2,
+                                                       gdouble          *nx1,
+                                                       gdouble          *ny1,
+                                                       gdouble          *nx2,
+                                                       gdouble          *ny2);
+void     gimp_display_shell_rotate_untransform_bounds (GimpDisplayShell *shell,
+                                                       gdouble           x1,
+                                                       gdouble           y1,
+                                                       gdouble           x2,
+                                                       gdouble           y2,
+                                                       gdouble          *nx1,
+                                                       gdouble          *ny1,
+                                                       gdouble          *nx2,
+                                                       gdouble          *ny2);
+
+void    gimp_display_shell_rotate_drag                (GimpDisplayShell *shell,
+                                                       gdouble           last_x,
+                                                       gdouble           last_y,
+                                                       gdouble           cur_x,
+                                                       gdouble           cur_y,
+                                                       gboolean          constrain);
+
+
+#endif  /*  __GIMP_DISPLAY_SHELL_ROTATE_H__  */
diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c
index f7c776d..4fb8bc3 100644
--- a/app/display/gimpdisplayshell-scroll.c
+++ b/app/display/gimpdisplayshell-scroll.c
@@ -36,6 +36,7 @@
 #include "gimpdisplay-foreach.h"
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 
@@ -119,6 +120,8 @@ gimp_display_shell_scroll (GimpDisplayShell *shell,
       shell->offset_x += x_offset;
       shell->offset_y += y_offset;
 
+      gimp_display_shell_rotate_update_transform (shell);
+
       gimp_overlay_box_scroll (GIMP_OVERLAY_BOX (shell->canvas),
                                -x_offset, -y_offset);
 
diff --git a/app/display/gimpdisplayshell-selection.c b/app/display/gimpdisplayshell-selection.c
index b6e1885..cc48fe8 100644
--- a/app/display/gimpdisplayshell-selection.c
+++ b/app/display/gimpdisplayshell-selection.c
@@ -292,6 +292,9 @@ selection_render_mask (Selection *selection)
   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
   cairo_set_line_width (cr, 1.0);
 
+  if (selection->shell->rotate_transform)
+    cairo_transform (cr, selection->shell->rotate_transform);
+
   gimp_cairo_add_segments (cr,
                            selection->segs_in,
                            selection->n_segs_in);
@@ -438,6 +441,9 @@ selection_start_timeout (Selection *selection)
 
           cr = gdk_cairo_create (gtk_widget_get_window (selection->shell->canvas));
 
+          if (selection->shell->rotate_transform)
+            cairo_transform (cr, selection->shell->rotate_transform);
+
           gimp_display_shell_draw_selection_out (selection->shell, cr,
                                                  selection->segs_out,
                                                  selection->n_segs_out);
diff --git a/app/display/gimpdisplayshell-tool-events.c b/app/display/gimpdisplayshell-tool-events.c
index 733c972..089cc40 100644
--- a/app/display/gimpdisplayshell-tool-events.c
+++ b/app/display/gimpdisplayshell-tool-events.c
@@ -55,6 +55,7 @@
 #include "gimpdisplayshell-cursor.h"
 #include "gimpdisplayshell-grab.h"
 #include "gimpdisplayshell-layer-select.h"
+#include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 #include "gimpdisplayshell-tool-events.h"
@@ -79,6 +80,7 @@ static void       gimp_display_shell_check_device_cursor      (GimpDisplayShell
 
 static void       gimp_display_shell_start_scrolling          (GimpDisplayShell  *shell,
                                                                const GdkEvent    *event,
+                                                               GdkModifierType    state,
                                                                gint               x,
                                                                gint               y);
 static void       gimp_display_shell_stop_scrolling           (GimpDisplayShell  *shell,
@@ -561,7 +563,7 @@ gimp_display_shell_canvas_tool_events (GtkWidget        *canvas,
           }
         else if (bevent->button == 2)
           {
-            gimp_display_shell_start_scrolling (shell, NULL,
+            gimp_display_shell_start_scrolling (shell, NULL, state,
                                                 bevent->x, bevent->y);
           }
 
@@ -841,9 +843,24 @@ gimp_display_shell_canvas_tool_events (GtkWidget        *canvas,
                             ? ((GdkEventMotion *) compressed_motion)->y
                             : mevent->y);
 
-            gimp_display_shell_scroll (shell,
-                                       shell->scroll_last_x - x,
-                                       shell->scroll_last_y - y);
+            if (shell->rotating)
+              {
+                gboolean constrain = (state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+
+                gimp_display_shell_rotate_drag (shell,
+                                                shell->scroll_last_x,
+                                                shell->scroll_last_y,
+                                                x,
+                                                y,
+                                                constrain);
+              }
+            else
+              {
+                gimp_display_shell_scroll (shell,
+                                           shell->scroll_last_x - x,
+                                           shell->scroll_last_y - y);
+
+              }
 
             shell->scroll_last_x = x;
             shell->scroll_last_y = y;
@@ -1406,6 +1423,7 @@ gimp_display_shell_check_device_cursor (GimpDisplayShell *shell)
 static void
 gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
                                     const GdkEvent   *event,
+                                    GdkModifierType   state,
                                     gint              x,
                                     gint              y)
 {
@@ -1413,11 +1431,16 @@ gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
 
   gimp_display_shell_pointer_grab (shell, event, GDK_POINTER_MOTION_MASK);
 
-  shell->scrolling     = TRUE;
-  shell->scroll_last_x = x;
-  shell->scroll_last_y = y;
+  shell->scrolling         = TRUE;
+  shell->scroll_last_x     = x;
+  shell->scroll_last_y     = y;
+  shell->rotating          = (state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+  shell->rotate_drag_angle = shell->rotate_angle;
 
-  gimp_display_shell_set_override_cursor (shell, GDK_FLEUR);
+  if (shell->rotating)
+    gimp_display_shell_set_override_cursor (shell, GDK_EXCHANGE);
+  else
+    gimp_display_shell_set_override_cursor (shell, GDK_FLEUR);
 }
 
 static void
@@ -1428,9 +1451,11 @@ gimp_display_shell_stop_scrolling (GimpDisplayShell *shell,
 
   gimp_display_shell_unset_override_cursor (shell);
 
-  shell->scrolling     = FALSE;
-  shell->scroll_last_x = 0;
-  shell->scroll_last_y = 0;
+  shell->scrolling         = FALSE;
+  shell->scroll_last_x     = 0;
+  shell->scroll_last_y     = 0;
+  shell->rotating          = FALSE;
+  shell->rotate_drag_angle = 0.0;
 
   gimp_display_shell_pointer_ungrab (shell, event);
 }
@@ -1457,6 +1482,7 @@ gimp_display_shell_space_pressed (GimpDisplayShell *shell,
         GimpDeviceManager *manager;
         GimpDeviceInfo    *current_device;
         GimpCoords         coords;
+        GdkModifierType    state = 0;
 
         manager = gimp_devices_get_manager (gimp);
         current_device = gimp_device_manager_get_current_device (manager);
@@ -1464,8 +1490,9 @@ gimp_display_shell_space_pressed (GimpDisplayShell *shell,
         gimp_device_info_get_device_coords (current_device,
                                             gtk_widget_get_window (shell->canvas),
                                             &coords);
+        gdk_event_get_state (event, &state);
 
-        gimp_display_shell_start_scrolling (shell, event,
+        gimp_display_shell_start_scrolling (shell, event, state,
                                             coords.x, coords.y);
       }
       break;
diff --git a/app/display/gimpdisplayshell-transform.c b/app/display/gimpdisplayshell-transform.c
index ffe1fd5..11aafdf 100644
--- a/app/display/gimpdisplayshell-transform.c
+++ b/app/display/gimpdisplayshell-transform.c
@@ -27,6 +27,7 @@
 #include "core/gimpboundary.h"
 #include "core/gimpdrawable.h"
 #include "core/gimpimage.h"
+#include "core/gimp-utils.h"
 
 #include "gimpdisplay.h"
 #include "gimpdisplayshell.h"
@@ -59,6 +60,11 @@ gimp_display_shell_transform_coords (const GimpDisplayShell *shell,
 
   display_coords->x -= shell->offset_x;
   display_coords->y -= shell->offset_y;
+
+  if (shell->rotate_transform)
+    cairo_matrix_transform_point (shell->rotate_transform,
+                                  &display_coords->x,
+                                  &display_coords->y);
 }
 
 /**
@@ -81,8 +87,13 @@ gimp_display_shell_untransform_coords (const GimpDisplayShell *shell,
 
   *image_coords = *display_coords;
 
-  image_coords->x = display_coords->x + shell->offset_x;
-  image_coords->y = display_coords->y + shell->offset_y;
+  if (shell->rotate_untransform)
+    cairo_matrix_transform_point (shell->rotate_untransform,
+                                  &image_coords->x,
+                                  &image_coords->y);
+
+  image_coords->x += shell->offset_x;
+  image_coords->y += shell->offset_y;
 
   image_coords->x /= shell->scale_x;
   image_coords->y /= shell->scale_y;
@@ -115,6 +126,17 @@ gimp_display_shell_transform_xy (const GimpDisplayShell *shell,
   tx = x * shell->scale_x - shell->offset_x;
   ty = y * shell->scale_y - shell->offset_y;
 
+  if (shell->rotate_transform)
+    {
+      gdouble fx = tx;
+      gdouble fy = ty;
+
+      cairo_matrix_transform_point (shell->rotate_transform, &fy, &fy);
+
+      tx = fx;
+      ty = fy;
+    }
+
   /* The projected coordinates might overflow a gint in the case of big
      images at high zoom levels, so we clamp them here to avoid problems.  */
   *nx = CLAMP (tx, G_MININT, G_MAXINT);
@@ -150,6 +172,17 @@ gimp_display_shell_untransform_xy (const GimpDisplayShell *shell,
   g_return_if_fail (nx != NULL);
   g_return_if_fail (ny != NULL);
 
+  if (shell->rotate_untransform)
+    {
+      gdouble fx = x;
+      gdouble fy = y;
+
+      cairo_matrix_transform_point (shell->rotate_untransform, &fy, &fy);
+
+      x = fx;
+      y = fy;
+    }
+
   if (round)
     {
       tx = SIGNED_ROUND (((gint64) x + shell->offset_x) / shell->scale_x);
@@ -189,6 +222,9 @@ gimp_display_shell_transform_xy_f  (const GimpDisplayShell *shell,
 
   *nx = SCALEX (shell, x) - shell->offset_x;
   *ny = SCALEY (shell, y) - shell->offset_y;
+
+  if (shell->rotate_transform)
+    cairo_matrix_transform_point (shell->rotate_transform, nx, ny);
 }
 
 /**
@@ -214,10 +250,95 @@ gimp_display_shell_untransform_xy_f (const GimpDisplayShell *shell,
   g_return_if_fail (nx != NULL);
   g_return_if_fail (ny != NULL);
 
+  if (shell->rotate_untransform)
+    cairo_matrix_transform_point (shell->rotate_untransform, &x, &y);
+
   *nx = (x + shell->offset_x) / shell->scale_x;
   *ny = (y + shell->offset_y) / shell->scale_y;
 }
 
+void
+gimp_display_shell_transform_bounds (const GimpDisplayShell *shell,
+                                     gdouble                 x1,
+                                     gdouble                 y1,
+                                     gdouble                 x2,
+                                     gdouble                 y2,
+                                     gdouble                *nx1,
+                                     gdouble                *ny1,
+                                     gdouble                *nx2,
+                                     gdouble                *ny2)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+  g_return_if_fail (nx1 != NULL);
+  g_return_if_fail (ny1 != NULL);
+  g_return_if_fail (nx2 != NULL);
+  g_return_if_fail (ny2 != NULL);
+
+  if (shell->rotate_transform)
+    {
+      gdouble tx1, ty1;
+      gdouble tx2, ty2;
+      gdouble tx3, ty3;
+      gdouble tx4, ty4;
+
+      gimp_display_shell_transform_xy_f (shell, x1, y1, &tx1, &ty1);
+      gimp_display_shell_transform_xy_f (shell, x1, y2, &tx2, &ty2);
+      gimp_display_shell_transform_xy_f (shell, x2, y1, &tx3, &ty3);
+      gimp_display_shell_transform_xy_f (shell, x2, y2, &tx4, &ty4);
+
+      *nx1 = MIN4 (tx1, tx2, tx3, tx4);
+      *ny1 = MIN4 (ty1, ty2, ty3, ty4);
+      *nx2 = MAX4 (tx1, tx2, tx3, tx4);
+      *ny2 = MAX4 (ty1, ty2, ty3, ty4);
+    }
+  else
+    {
+      gimp_display_shell_transform_xy_f (shell, x1, y1, nx1, ny1);
+      gimp_display_shell_transform_xy_f (shell, x2, y2, nx2, ny2);
+    }
+}
+
+void
+gimp_display_shell_untransform_bounds (const GimpDisplayShell *shell,
+                                       gdouble                 x1,
+                                       gdouble                 y1,
+                                       gdouble                 x2,
+                                       gdouble                 y2,
+                                       gdouble                *nx1,
+                                       gdouble                *ny1,
+                                       gdouble                *nx2,
+                                       gdouble                *ny2)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+  g_return_if_fail (nx1 != NULL);
+  g_return_if_fail (ny1 != NULL);
+  g_return_if_fail (nx2 != NULL);
+  g_return_if_fail (ny2 != NULL);
+
+  if (shell->rotate_untransform)
+    {
+      gdouble tx1, ty1;
+      gdouble tx2, ty2;
+      gdouble tx3, ty3;
+      gdouble tx4, ty4;
+
+      gimp_display_shell_untransform_xy_f (shell, x1, y1, &tx1, &ty1);
+      gimp_display_shell_untransform_xy_f (shell, x1, y2, &tx2, &ty2);
+      gimp_display_shell_untransform_xy_f (shell, x2, y1, &tx3, &ty3);
+      gimp_display_shell_untransform_xy_f (shell, x2, y2, &tx4, &ty4);
+
+      *nx1 = MIN4 (tx1, tx2, tx3, tx4);
+      *ny1 = MIN4 (ty1, ty2, ty3, ty4);
+      *nx2 = MAX4 (tx1, tx2, tx3, tx4);
+      *ny2 = MAX4 (ty1, ty2, ty3, ty4);
+    }
+  else
+    {
+      gimp_display_shell_untransform_xy_f (shell, x1, y1, nx1, ny1);
+      gimp_display_shell_untransform_xy_f (shell, x2, y2, nx2, ny2);
+    }
+}
+
 /**
  * gimp_display_shell_transform_segments:
  * @shell:       a #GimpDisplayShell
diff --git a/app/display/gimpdisplayshell-transform.h b/app/display/gimpdisplayshell-transform.h
index 0f8b747..ead1cdb 100644
--- a/app/display/gimpdisplayshell-transform.h
+++ b/app/display/gimpdisplayshell-transform.h
@@ -49,6 +49,25 @@ void  gimp_display_shell_untransform_xy_f     (const GimpDisplayShell *shell,
                                                gdouble                *nx,
                                                gdouble                *ny);
 
+void  gimp_display_shell_transform_bounds     (const GimpDisplayShell *shell,
+                                               gdouble                 x1,
+                                               gdouble                 y1,
+                                               gdouble                 x2,
+                                               gdouble                 y2,
+                                               gdouble                *nx1,
+                                               gdouble                *ny1,
+                                               gdouble                *nx2,
+                                               gdouble                *ny2);
+void  gimp_display_shell_untransform_bounds   (const GimpDisplayShell *shell,
+                                               gdouble                 x1,
+                                               gdouble                 y1,
+                                               gdouble                 x2,
+                                               gdouble                 y2,
+                                               gdouble                *nx1,
+                                               gdouble                *ny1,
+                                               gdouble                *nx2,
+                                               gdouble                *ny2);
+
 void  gimp_display_shell_transform_segments   (const GimpDisplayShell *shell,
                                                const GimpBoundSeg     *src_segs,
                                                GimpSegment            *dest_segs,
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index c25c290..3b4f227 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -36,6 +36,7 @@
 #include "config/gimpdisplayoptions.h"
 
 #include "core/gimp.h"
+#include "core/gimp-utils.h"
 #include "core/gimpchannel.h"
 #include "core/gimpcontext.h"
 #include "core/gimpimage.h"
@@ -67,6 +68,7 @@
 #include "gimpdisplayshell-items.h"
 #include "gimpdisplayshell-progress.h"
 #include "gimpdisplayshell-render.h"
+#include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 #include "gimpdisplayshell-selection.h"
@@ -1423,6 +1425,8 @@ gimp_display_shell_scaled (GimpDisplayShell *shell)
 
   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
 
+  gimp_display_shell_rotate_update_transform (shell);
+
   for (list = shell->children; list; list = g_list_next (list))
     {
       GtkWidget *child = list->data;
@@ -1444,6 +1448,8 @@ gimp_display_shell_scrolled (GimpDisplayShell *shell)
 
   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
 
+  gimp_display_shell_rotate_update_transform (shell);
+
   for (list = shell->children; list; list = g_list_next (list))
     {
       GtkWidget *child = list->data;
@@ -1585,6 +1591,8 @@ gimp_display_shell_mask_bounds (GimpDisplayShell *shell,
   GimpLayer *layer;
   gdouble    x1_f, y1_f;
   gdouble    x2_f, y2_f;
+  gdouble    x3_f, y3_f;
+  gdouble    x4_f, y4_f;
 
   g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
   g_return_val_if_fail (x1 != NULL, FALSE);
@@ -1625,13 +1633,15 @@ gimp_display_shell_mask_bounds (GimpDisplayShell *shell,
     }
 
   gimp_display_shell_transform_xy_f (shell, *x1, *y1, &x1_f, &y1_f);
-  gimp_display_shell_transform_xy_f (shell, *x2, *y2, &x2_f, &y2_f);
+  gimp_display_shell_transform_xy_f (shell, *x1, *y2, &x2_f, &y2_f);
+  gimp_display_shell_transform_xy_f (shell, *x2, *y1, &x3_f, &y3_f);
+  gimp_display_shell_transform_xy_f (shell, *x2, *y2, &x4_f, &y4_f);
 
   /*  Make sure the extents are within bounds  */
-  *x1 = CLAMP (floor (x1_f), 0, shell->disp_width);
-  *y1 = CLAMP (floor (y1_f), 0, shell->disp_height);
-  *x2 = CLAMP (ceil (x2_f),  0, shell->disp_width);
-  *y2 = CLAMP (ceil (y2_f),  0, shell->disp_height);
+  *x1 = CLAMP (floor (MIN4 (x1_f, x2_f, x3_f, x4_f)), 0, shell->disp_width);
+  *y1 = CLAMP (floor (MIN4 (y1_f, y2_f, y3_f, y4_f)), 0, shell->disp_height);
+  *x2 = CLAMP (ceil (MAX4 (x1_f, x2_f, x3_f, x4_f)),  0, shell->disp_width);
+  *y2 = CLAMP (ceil (MAX4 (y1_f, y2_f, y3_f, y4_f)),  0, shell->disp_height);
 
   return ((*x2 - *x1) > 0) && ((*y2 - *y1) > 0);
 }
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index 08ef099..640cfdf 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -70,6 +70,10 @@ struct _GimpDisplayShell
   gdouble            scale_x;          /*  horizontal scale factor            */
   gdouble            scale_y;          /*  vertical scale factor              */
 
+  gdouble            rotate_angle;
+  cairo_matrix_t    *rotate_transform;
+  cairo_matrix_t    *rotate_untransform;
+
   gdouble            monitor_xres;
   gdouble            monitor_yres;
   gboolean           dot_for_dot;      /*  ignore monitor resolution          */
@@ -176,6 +180,8 @@ struct _GimpDisplayShell
   gboolean           scrolling;
   gint               scroll_last_x;
   gint               scroll_last_y;
+  gboolean           rotating;
+  gdouble            rotate_drag_angle;
   gpointer           scroll_info;
 
   GimpDrawable      *mask;



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