diff --git a/app/display/Makefile.am b/app/display/Makefile.am index de9fc49885..3539dc289f 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 093eaf3192..9e1a2603bd 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 880b9bda84..3e0b746ea1 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 3209ba4c46..5861c0a156 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 075b2ec7f4..0e0e032c32 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 9cb1261f15..0e3ef2d7c4 100644 --- a/app/display/gimpdisplayshell-items.c +++ b/app/display/gimpdisplayshell-items.c @@ -22,6 +22,8 @@ #include +#include + #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 0000000000..d04ce16e13 --- /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 . + */ + +#include "config.h" + +#include +#include + +#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 0000000000..0c71babc47 --- /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 . + */ + +#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 f7c776da69..4fb8bc3e4f 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 b6e18851d3..cc48fe8430 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 733c97239b..089cc4076a 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 ffe1fd5821..11aafdf3ec 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 0f8b747a0b..ead1cdbb83 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 c25c290a53..3b4f227e15 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 08ef099fe4..640cfdfd2c 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;