From d08bb7d4373b09cd3d4e6b0077f92205e614e6ad Mon Sep 17 00:00:00 2001
From: Manuel Kaufmann <humitos@gmail.com>
Date: Tue, 23 Oct 2012 10:34:49 -0300
Subject: [PATCH ImageViewer] Rotate, Zoom and center the image
Improves the rotation by centering the image on the screen when it is
smaller than the screen size.
Optimal zoom is calculated for images than are higher in hight when
they are rotated as well.
Fixed rotation (anti)clockwise. X, Y axis + angles in gradians are
handled different by cairo.
Signed-off-by: Manuel Kaufmann <humitos@gmail.com>
---
ImageView.py | 113 ++++++++++++++++++++++++++++++++++---------------
ImageViewerActivity.py | 4 +-
2 files changed, 82 insertions(+), 35 deletions(-)
diff --git a/ImageView.py b/ImageView.py
index 30b19ac..cbc5516 100644
a
|
b
|
import logging |
27 | 27 | import cairo |
28 | 28 | import random |
29 | 29 | import time |
| 30 | import math |
30 | 31 | |
31 | 32 | ZOOM_IN_OUT = 0.1 |
32 | 33 | |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
71 | 72 | self._fast = True |
72 | 73 | self._redraw_id = None |
73 | 74 | self._is_touching = False |
| 75 | self._switched = False |
74 | 76 | |
75 | 77 | def do_get_property(self, pspec): |
76 | 78 | if pspec.name == 'zoom': |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
123 | 125 | h = int(self.surface.get_height() * self.zoom) |
124 | 126 | logging.error('W: %s, H: %s', w, h) |
125 | 127 | |
126 | | ctx.save() |
127 | 128 | if self._fast: |
128 | 129 | ctx.set_antialias(cairo.ANTIALIAS_NONE) |
129 | | if self.angle != 0: |
130 | | logging.error('Rotating: %s', self.angle) |
131 | | ctx.translate(0.5 * w, 0.5 * h) |
132 | | ctx.rotate(self.angle) |
133 | | ctx.translate(-0.5 * w, -0.5 * h) |
134 | 130 | |
135 | 131 | scrolled_window = self.get_parent() |
136 | 132 | rect = scrolled_window.get_allocation() |
137 | 133 | x = y = 0 |
138 | | if rect.width >= w: |
139 | | x = int((rect.width - w) / 2) |
140 | | elif self._is_touching: |
141 | | hadj = int((w - rect.width) / 2) |
142 | | hadjustment = scrolled_window.get_hadjustment() |
143 | | hadjustment.set_value(hadj) |
144 | | |
145 | | if rect.height >= h: |
146 | | y = int((rect.height - h) / 2) |
147 | | elif self._is_touching: |
148 | | vadj = int((h - rect.height) / 2) |
149 | | vadjustment = scrolled_window.get_vadjustment() |
150 | | vadjustment.set_value(vadj) |
| 134 | if self.angle != 0: |
| 135 | logging.error('Rotating: %s', self.angle) |
| 136 | ctx.rotate(self.angle) |
| 137 | |
| 138 | if self.angle == math.pi: |
| 139 | ctx.translate(-w, -h) |
| 140 | |
| 141 | if rect.width > w: |
| 142 | x = -(rect.width - w) / 2 |
| 143 | if rect.height > h: |
| 144 | y = -(rect.height - h) / 2 |
| 145 | |
| 146 | elif self.angle == math.pi / 2: |
| 147 | ctx.translate(0, -h) |
| 148 | |
| 149 | # center the image |
| 150 | if rect.height > w: |
| 151 | x = (rect.height - w) / 2 |
| 152 | if rect.width > h: |
| 153 | y = -(rect.width - h) / 2 |
| 154 | |
| 155 | elif self.angle == math.pi * 3 / 2: |
| 156 | ctx.translate(-w, 0) |
| 157 | |
| 158 | if rect.height > w: |
| 159 | x = -(rect.height - w) / 2 |
| 160 | if rect.width > h: |
| 161 | y = (rect.width - h) / 2 |
| 162 | |
| 163 | else: |
| 164 | if rect.width > w: |
| 165 | x = int((rect.width - w) / 2) |
| 166 | if rect.height > h: |
| 167 | y = int((rect.height - h) / 2) |
| 168 | ctx.translate(x, y) |
| 169 | |
| 170 | if self._is_touching: |
| 171 | if self._switched: |
| 172 | w, h = h, w |
| 173 | |
| 174 | if rect.height < h: |
| 175 | vadj = int((h - rect.height) / 2) |
| 176 | vadjustment = scrolled_window.get_vadjustment() |
| 177 | vadjustment.set_value(vadj) |
| 178 | |
| 179 | if rect.width < w: |
| 180 | hadj = int((w - rect.width) / 2) |
| 181 | hadjustment = scrolled_window.get_hadjustment() |
| 182 | hadjustment.set_value(hadj) |
151 | 183 | |
152 | 184 | if self.zoom != 1: |
153 | 185 | logging.error('Scaling: %s', self.zoom) |
154 | | ctx.translate(x, y) |
155 | 186 | ctx.scale(self.zoom, self.zoom) |
156 | 187 | |
157 | 188 | ctx.set_source_surface(self.surface, 0, 0) |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
171 | 202 | def _redraw_high_quality(self): |
172 | 203 | self._fast = False |
173 | 204 | self._redraw_id = None |
174 | | self.queue_draw() |
| 205 | self._redraw() |
175 | 206 | return False |
176 | 207 | |
| 208 | def _redraw(self): |
| 209 | # README: this is a hack to not raise the 'draw' event (again) |
| 210 | # when we request more space to show the scroll bars |
| 211 | w = int(self.surface.get_width() * self.zoom) |
| 212 | h = int(self.surface.get_height() * self.zoom) |
| 213 | |
| 214 | self._switched = False |
| 215 | if (self.angle / (math.pi / 2)) % 2 == 1: |
| 216 | # change image dimensions if it's rotated |
| 217 | w, h = h, w |
| 218 | self._switched = True |
| 219 | |
| 220 | self.set_size_request(w, h) |
| 221 | |
177 | 222 | def set_zoom(self, zoom): |
178 | 223 | self._optimal_zoom_flag = False |
179 | 224 | self._set_zoom(zoom) |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
191 | 236 | def set_angle(self, angle): |
192 | 237 | self._optimal_zoom_flag = True |
193 | 238 | |
194 | | self.angle = angle |
195 | | self.queue_draw() |
| 239 | self.angle = angle % (2 * math.pi) |
| 240 | self._redraw() |
196 | 241 | self.emit('angle-changed') |
197 | 242 | |
198 | 243 | def zoom_in(self): |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
238 | 283 | width = rect.width |
239 | 284 | height = rect.height |
240 | 285 | |
241 | | if width < self.surface.get_width() or \ |
242 | | height < self.surface.get_height(): |
| 286 | surface_width = self.surface.get_width() |
| 287 | surface_height = self.surface.get_height() |
| 288 | |
| 289 | if self._switched: |
| 290 | surface_width, surface_height = \ |
| 291 | surface_height, surface_width |
| 292 | |
| 293 | if width < surface_width or \ |
| 294 | height < surface_height: |
243 | 295 | # Image is larger than allocated size |
244 | | zoom = min(width / self.surface.get_width(), |
245 | | height / self.surface.get_height()) |
| 296 | zoom = min(width / surface_width, |
| 297 | height / surface_height) |
246 | 298 | else: |
247 | 299 | zoom = 1 |
248 | 300 | |
… |
… |
class ImageViewer(Gtk.DrawingArea): |
251 | 303 | |
252 | 304 | def _set_zoom(self, zoom): |
253 | 305 | self.zoom = zoom |
254 | | # # README: this is a hack to not raise the 'draw' event (again) |
255 | | # # when we request more space to show the scroll bars |
256 | | self._fast = True |
257 | | w = int(self.surface.get_width() * self.zoom) |
258 | | h = int(self.surface.get_height() * self.zoom) |
259 | | self.set_size_request(w, h) |
| 306 | self._redraw() |
260 | 307 | self.emit('zoom-changed') |
261 | 308 | |
262 | 309 | |
diff --git a/ImageViewerActivity.py b/ImageViewerActivity.py
index f17d4cf..3b36103 100644
a
|
b
|
class ImageViewerActivity(activity.Activity): |
310 | 310 | self.view.set_zoom(1) |
311 | 311 | |
312 | 312 | def __rotate_anticlockwise_cb(self, button): |
313 | | angle = self.view.angle + math.pi / 2 |
| 313 | angle = self.view.angle - math.pi / 2 |
314 | 314 | self.view.set_angle(angle) |
315 | 315 | |
316 | 316 | def __rotate_clockwise_cb(self, button): |
317 | | angle = self.view.angle - math.pi / 2 |
| 317 | angle = self.view.angle + math.pi / 2 |
318 | 318 | self.view.set_angle(angle) |
319 | 319 | |
320 | 320 | def __fullscreen_cb(self, button): |