OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * Viewport class controls the way the image is displayed (scale, offset etc). | 8 * Viewport class controls the way the image is displayed (scale, offset etc). |
9 * @constructor | 9 * @constructor |
10 */ | 10 */ |
11 function Viewport() { | 11 function Viewport() { |
12 /** | 12 /** |
13 * Size of the full resolution image. | 13 * Size of the full resolution image. |
14 * @type {Rect} | 14 * @type {Rect} |
15 * @private | 15 * @private |
16 */ | 16 */ |
17 this.imageBounds_ = new Rect(); | 17 this.imageBounds_ = new Rect(); |
18 | 18 |
19 /** | 19 /** |
20 * Size of the application window. | 20 * Size of the application window. |
21 * @type {Rect} | 21 * @type {Rect} |
22 * @private | 22 * @private |
23 */ | 23 */ |
24 this.screenBounds_ = new Rect(); | 24 this.screenBounds_ = new Rect(); |
25 | 25 |
26 /** | 26 /** |
| 27 * Bounds of the image element on screen without zoom and offset. |
| 28 * @type {Rect} |
| 29 * @private |
| 30 */ |
| 31 this.imageElementBoundsOnScreen_ = null; |
| 32 |
| 33 /** |
| 34 * Bounds of the image with zoom and offset. |
| 35 * @type {Rect} |
| 36 * @private |
| 37 */ |
| 38 this.imageBoundsOnScreen_ = null; |
| 39 |
| 40 /** |
| 41 * Image bounds that is clipped with the screen bounds. |
| 42 * @type {Rect} |
| 43 * @private |
| 44 */ |
| 45 this.imageBoundsOnScreenClipped_ = null; |
| 46 |
| 47 /** |
27 * Scale from the full resolution image to the screen displayed image. This is | 48 * Scale from the full resolution image to the screen displayed image. This is |
28 * not zoom operated by users. | 49 * not zoom operated by users. |
29 * @type {number} | 50 * @type {number} |
30 * @private | 51 * @private |
31 */ | 52 */ |
32 this.scale_ = 1; | 53 this.scale_ = 1; |
33 | 54 |
34 /** | 55 /** |
35 * Index of zoom ratio. 0 is "zoom ratio = 1". | 56 * Index of zoom ratio. 0 is "zoom ratio = 1". |
36 * @type {number} | 57 * @type {number} |
37 * @private | 58 * @private |
38 */ | 59 */ |
39 this.zoomIndex_ = 0; | 60 this.zoomIndex_ = 0; |
40 | 61 |
41 /** | 62 /** |
42 * Zoom ratio specified by user operations. | 63 * Zoom ratio specified by user operations. |
43 * @type {number} | 64 * @type {number} |
44 * @private | 65 * @private |
45 */ | 66 */ |
46 this.zoom_ = 1; | 67 this.zoom_ = 1; |
47 | 68 |
| 69 /** |
| 70 * Offset specified by user operations. |
| 71 * @type {number} |
| 72 */ |
48 this.offsetX_ = 0; | 73 this.offsetX_ = 0; |
| 74 |
| 75 /** |
| 76 * Offset specified by user operations. |
| 77 * @type {number} |
| 78 */ |
49 this.offsetY_ = 0; | 79 this.offsetY_ = 0; |
50 | 80 |
| 81 /** |
| 82 * Generation of the screen size image cache. |
| 83 * This is incremented every time when the size of image cache is changed. |
| 84 * @type {number} |
| 85 * @private |
| 86 */ |
51 this.generation_ = 0; | 87 this.generation_ = 0; |
52 | 88 |
53 this.update(); | 89 this.update(); |
| 90 Object.seal(this); |
54 } | 91 } |
55 | 92 |
56 /** | 93 /** |
57 * Zoom ratios. | 94 * Zoom ratios. |
58 * | 95 * |
59 * @type {Object.<string, number>} | 96 * @type {Object.<string, number>} |
60 * @const | 97 * @const |
61 */ | 98 */ |
62 Viewport.ZOOM_RATIOS = Object.freeze({ | 99 Viewport.ZOOM_RATIOS = Object.freeze({ |
63 '3': 3, | 100 '3': 3, |
64 '2': 2, | 101 '2': 2, |
65 '1': 1.5, | 102 '1': 1.5, |
66 '0': 1, | 103 '0': 1, |
67 '-1': 0.75, | 104 '-1': 0.75, |
68 '-2': 0.5, | 105 '-2': 0.5, |
69 '-3': 0.25 | 106 '-3': 0.25 |
70 }); | 107 }); |
71 | 108 |
72 /** | 109 /** |
73 * @param {number} width Image width. | 110 * @param {number} width Image width. |
74 * @param {number} height Image height. | 111 * @param {number} height Image height. |
75 */ | 112 */ |
76 Viewport.prototype.setImageSize = function(width, height) { | 113 Viewport.prototype.setImageSize = function(width, height) { |
77 this.imageBounds_ = new Rect(width, height); | 114 this.imageBounds_ = new Rect(width, height); |
| 115 this.update(); |
78 this.invalidateCaches(); | 116 this.invalidateCaches(); |
79 }; | 117 }; |
80 | 118 |
81 /** | 119 /** |
82 * @param {number} width Screen width. | 120 * @param {number} width Screen width. |
83 * @param {number} height Screen height. | 121 * @param {number} height Screen height. |
84 */ | 122 */ |
85 Viewport.prototype.setScreenSize = function(width, height) { | 123 Viewport.prototype.setScreenSize = function(width, height) { |
86 this.screenBounds_ = new Rect(width, height); | 124 this.screenBounds_ = new Rect(width, height); |
| 125 this.update(); |
87 this.invalidateCaches(); | 126 this.invalidateCaches(); |
88 }; | 127 }; |
89 | 128 |
90 /** | 129 /** |
91 * Sets the new zoom ratio. | 130 * Sets the new zoom ratio. |
92 * @param {number} zoomIndex New zoom index. | 131 * @param {number} zoomIndex New zoom index. |
93 */ | 132 */ |
94 Viewport.prototype.setZoomIndex = function(zoomIndex) { | 133 Viewport.prototype.setZoomIndex = function(zoomIndex) { |
95 // Ignore the invalid zoomIndex. | 134 // Ignore the invalid zoomIndex. |
96 if (!Viewport.ZOOM_RATIOS[zoomIndex.toString()]) | 135 if (!Viewport.ZOOM_RATIOS[zoomIndex.toString()]) |
97 return; | 136 return; |
98 | |
99 this.zoomIndex_ = zoomIndex; | 137 this.zoomIndex_ = zoomIndex; |
100 this.zoom_ = Viewport.ZOOM_RATIOS[zoomIndex]; | 138 this.zoom_ = Viewport.ZOOM_RATIOS[zoomIndex]; |
| 139 this.update(); |
101 }; | 140 }; |
102 | 141 |
103 /** | 142 /** |
104 * Returns the current zoom index. | 143 * Returns the current zoom index. |
105 * @return {number} Zoon index. | 144 * @return {number} Zoon index. |
106 */ | 145 */ |
107 Viewport.prototype.getZoomIndex = function() { | 146 Viewport.prototype.getZoomIndex = function() { |
108 return this.zoomIndex_; | 147 return this.zoomIndex_; |
109 }; | 148 }; |
110 | 149 |
111 /** | 150 /** |
112 * Set the size by an HTML element. | |
113 * | |
114 * @param {HTMLElement} frame The element acting as the "screen". | |
115 */ | |
116 Viewport.prototype.sizeByFrame = function(frame) { | |
117 this.setScreenSize(frame.clientWidth, frame.clientHeight); | |
118 }; | |
119 | |
120 /** | |
121 * Set the size and scale to fit an HTML element. | |
122 * | |
123 * @param {HTMLElement} frame The element acting as the "screen". | |
124 */ | |
125 Viewport.prototype.sizeByFrameAndFit = function(frame) { | |
126 var wasFitting = this.getScale() == this.getFittingScale(); | |
127 this.sizeByFrame(frame); | |
128 var minScale = this.getFittingScale(); | |
129 if (wasFitting || (this.getScale() < minScale)) { | |
130 this.setScale(minScale, true); | |
131 } | |
132 }; | |
133 | |
134 /** | |
135 * @return {number} Scale. | 151 * @return {number} Scale. |
136 */ | 152 */ |
137 Viewport.prototype.getScale = function() { return this.scale_; }; | 153 Viewport.prototype.getScale = function() { return this.scale_; }; |
138 | 154 |
139 /** | 155 /** |
140 * @param {number} scale The new scale. | 156 * @param {number} scale The new scale. |
141 * @param {boolean} notify True if the change should be reflected in the UI. | |
142 */ | 157 */ |
143 Viewport.prototype.setScale = function(scale, notify) { | 158 Viewport.prototype.setScale = function(scale) { |
144 if (this.scale_ == scale) return; | 159 if (this.scale_ == scale) |
| 160 return; |
145 this.scale_ = scale; | 161 this.scale_ = scale; |
| 162 this.update(); |
146 this.invalidateCaches(); | 163 this.invalidateCaches(); |
147 }; | 164 }; |
148 | 165 |
149 /** | 166 /** |
150 * @return {number} Best scale to fit the current image into the current screen. | 167 * @return {number} Best scale to fit the current image into the current screen. |
151 */ | 168 */ |
152 Viewport.prototype.getFittingScale = function() { | 169 Viewport.prototype.getFittingScale = function() { |
153 return this.getFittingScaleForImageSize_( | 170 return this.getFittingScaleForImageSize_( |
154 this.imageBounds_.width, this.imageBounds_.height); | 171 this.imageBounds_.width, this.imageBounds_.height); |
155 }; | 172 }; |
(...skipping 11 matching lines...) Expand all Loading... |
167 var scaleY = this.screenBounds_.height / height; | 184 var scaleY = this.screenBounds_.height / height; |
168 // Scales > (1 / devicePixelRatio) do not look good. Also they are | 185 // Scales > (1 / devicePixelRatio) do not look good. Also they are |
169 // not really useful as we do not have any pixel-level operations. | 186 // not really useful as we do not have any pixel-level operations. |
170 return Math.min(1 / window.devicePixelRatio, scaleX, scaleY); | 187 return Math.min(1 / window.devicePixelRatio, scaleX, scaleY); |
171 }; | 188 }; |
172 | 189 |
173 /** | 190 /** |
174 * Set the scale to fit the image into the screen. | 191 * Set the scale to fit the image into the screen. |
175 */ | 192 */ |
176 Viewport.prototype.fitImage = function() { | 193 Viewport.prototype.fitImage = function() { |
177 var scale = this.getFittingScale(); | 194 this.setScale(this.getFittingScale()); |
178 this.setScale(scale, true); | |
179 }; | 195 }; |
180 | 196 |
181 /** | 197 /** |
182 * @return {number} X-offset of the viewport. | 198 * @return {number} X-offset of the viewport. |
183 */ | 199 */ |
184 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; | 200 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; |
185 | 201 |
186 /** | 202 /** |
187 * @return {number} Y-offset of the viewport. | 203 * @return {number} Y-offset of the viewport. |
188 */ | 204 */ |
(...skipping 20 matching lines...) Expand all Loading... |
209 * @return {Rect} The image bounds in image coordinates. | 225 * @return {Rect} The image bounds in image coordinates. |
210 */ | 226 */ |
211 Viewport.prototype.getImageBounds = function() { return this.imageBounds_; }; | 227 Viewport.prototype.getImageBounds = function() { return this.imageBounds_; }; |
212 | 228 |
213 /** | 229 /** |
214 * @return {Rect} The screen bounds in screen coordinates. | 230 * @return {Rect} The screen bounds in screen coordinates. |
215 */ | 231 */ |
216 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; | 232 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; |
217 | 233 |
218 /** | 234 /** |
219 * @return {Rect} The visible part of the image, in image coordinates. | 235 * @return {Rect} The size of screen cache canvas. |
220 */ | 236 */ |
221 Viewport.prototype.getImageClipped = function() { return this.imageClipped_; }; | 237 Viewport.prototype.getDeviceBounds = function() { |
222 | 238 var size = this.getImageElementBoundsOnScreen(); |
223 /** | 239 return new Rect( |
224 * @return {Rect} The visible part of the image, in screen coordinates. | 240 size.width * window.devicePixelRatio, |
225 */ | 241 size.height * window.devicePixelRatio); |
226 Viewport.prototype.getScreenClipped = function() { | |
227 return this.screenClipped_; | |
228 }; | 242 }; |
229 | 243 |
230 /** | 244 /** |
231 * A counter that is incremented with each viewport state change. | 245 * A counter that is incremented with each viewport state change. |
232 * Clients that cache anything that depends on the viewport state should keep | 246 * Clients that cache anything that depends on the viewport state should keep |
233 * track of this counter. | 247 * track of this counter. |
234 * @return {number} counter. | 248 * @return {number} counter. |
235 */ | 249 */ |
236 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; | 250 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; |
237 | 251 |
238 /** | 252 /** |
239 * Called on event view port state change. | 253 * Called on event view port state change. |
240 */ | 254 */ |
241 Viewport.prototype.invalidateCaches = function() { this.generation_++; }; | 255 Viewport.prototype.invalidateCaches = function() { this.generation_++; }; |
242 | 256 |
243 /** | 257 /** |
244 * @return {Rect} The image bounds in screen coordinates. | 258 * @return {Rect} The image bounds in screen coordinates. |
245 */ | 259 */ |
246 Viewport.prototype.getImageBoundsOnScreen = function() { | 260 Viewport.prototype.getImageBoundsOnScreen = function() { |
247 return this.imageOnScreen_; | 261 return this.imageBoundsOnScreen_; |
248 }; | 262 }; |
249 | 263 |
250 /** | 264 /** |
| 265 * The image bounds in screen coordinates. |
| 266 * This returns the bounds of element before applying zoom and offset. |
| 267 * @return {Rect} |
| 268 */ |
| 269 Viewport.prototype.getImageElementBoundsOnScreen = function() { |
| 270 return this.imageElementBoundsOnScreen_; |
| 271 }; |
| 272 |
| 273 /** |
| 274 * The image bounds on screen, which is clipped with the screen size. |
| 275 * @return {Rect} |
| 276 */ |
| 277 Viewport.prototype.getImageBoundsOnScreenClipped = function() { |
| 278 return this.imageBoundsOnScreenClipped_; |
| 279 }; |
| 280 |
| 281 /** |
251 * @param {number} size Size in screen coordinates. | 282 * @param {number} size Size in screen coordinates. |
252 * @return {number} Size in image coordinates. | 283 * @return {number} Size in image coordinates. |
253 */ | 284 */ |
254 Viewport.prototype.screenToImageSize = function(size) { | 285 Viewport.prototype.screenToImageSize = function(size) { |
255 return size / this.getScale(); | 286 return size / this.getScale(); |
256 }; | 287 }; |
257 | 288 |
258 /** | 289 /** |
259 * @param {number} x X in screen coordinates. | 290 * @param {number} x X in screen coordinates. |
260 * @return {number} X in image coordinates. | 291 * @return {number} X in image coordinates. |
261 */ | 292 */ |
262 Viewport.prototype.screenToImageX = function(x) { | 293 Viewport.prototype.screenToImageX = function(x) { |
263 return Math.round((x - this.imageOnScreen_.left) / this.getScale()); | 294 return Math.round((x - this.imageBoundsOnScreen_.left) / this.getScale()); |
264 }; | 295 }; |
265 | 296 |
266 /** | 297 /** |
267 * @param {number} y Y in screen coordinates. | 298 * @param {number} y Y in screen coordinates. |
268 * @return {number} Y in image coordinates. | 299 * @return {number} Y in image coordinates. |
269 */ | 300 */ |
270 Viewport.prototype.screenToImageY = function(y) { | 301 Viewport.prototype.screenToImageY = function(y) { |
271 return Math.round((y - this.imageOnScreen_.top) / this.getScale()); | 302 return Math.round((y - this.imageBoundsOnScreen_.top) / this.getScale()); |
272 }; | 303 }; |
273 | 304 |
274 /** | 305 /** |
275 * @param {Rect} rect Rectangle in screen coordinates. | 306 * @param {Rect} rect Rectangle in screen coordinates. |
276 * @return {Rect} Rectangle in image coordinates. | 307 * @return {Rect} Rectangle in image coordinates. |
277 */ | 308 */ |
278 Viewport.prototype.screenToImageRect = function(rect) { | 309 Viewport.prototype.screenToImageRect = function(rect) { |
279 return new Rect( | 310 return new Rect( |
280 this.screenToImageX(rect.left), | 311 this.screenToImageX(rect.left), |
281 this.screenToImageY(rect.top), | 312 this.screenToImageY(rect.top), |
282 this.screenToImageSize(rect.width), | 313 this.screenToImageSize(rect.width), |
283 this.screenToImageSize(rect.height)); | 314 this.screenToImageSize(rect.height)); |
284 }; | 315 }; |
285 | 316 |
286 /** | 317 /** |
287 * @param {number} size Size in image coordinates. | 318 * @param {number} size Size in image coordinates. |
288 * @return {number} Size in screen coordinates. | 319 * @return {number} Size in screen coordinates. |
289 */ | 320 */ |
290 Viewport.prototype.imageToScreenSize = function(size) { | 321 Viewport.prototype.imageToScreenSize = function(size) { |
291 return size * this.getScale(); | 322 return size * this.getScale(); |
292 }; | 323 }; |
293 | 324 |
294 /** | 325 /** |
295 * @param {number} x X in image coordinates. | 326 * @param {number} x X in image coordinates. |
296 * @return {number} X in screen coordinates. | 327 * @return {number} X in screen coordinates. |
297 */ | 328 */ |
298 Viewport.prototype.imageToScreenX = function(x) { | 329 Viewport.prototype.imageToScreenX = function(x) { |
299 return Math.round(this.imageOnScreen_.left + x * this.getScale()); | 330 return Math.round(this.imageBoundsOnScreen_.left + x * this.getScale()); |
300 }; | 331 }; |
301 | 332 |
302 /** | 333 /** |
303 * @param {number} y Y in image coordinates. | 334 * @param {number} y Y in image coordinates. |
304 * @return {number} Y in screen coordinates. | 335 * @return {number} Y in screen coordinates. |
305 */ | 336 */ |
306 Viewport.prototype.imageToScreenY = function(y) { | 337 Viewport.prototype.imageToScreenY = function(y) { |
307 return Math.round(this.imageOnScreen_.top + y * this.getScale()); | 338 return Math.round(this.imageBoundsOnScreen_.top + y * this.getScale()); |
308 }; | 339 }; |
309 | 340 |
310 /** | 341 /** |
311 * @param {Rect} rect Rectangle in image coordinates. | 342 * @param {Rect} rect Rectangle in image coordinates. |
312 * @return {Rect} Rectangle in screen coordinates. | 343 * @return {Rect} Rectangle in screen coordinates. |
313 */ | 344 */ |
314 Viewport.prototype.imageToScreenRect = function(rect) { | 345 Viewport.prototype.imageToScreenRect = function(rect) { |
315 return new Rect( | 346 return new Rect( |
316 this.imageToScreenX(rect.left), | 347 this.imageToScreenX(rect.left), |
317 this.imageToScreenY(rect.top), | 348 this.imageToScreenY(rect.top), |
318 Math.round(this.imageToScreenSize(rect.width)), | 349 Math.round(this.imageToScreenSize(rect.width)), |
319 Math.round(this.imageToScreenSize(rect.height))); | 350 Math.round(this.imageToScreenSize(rect.height))); |
320 }; | 351 }; |
321 | 352 |
322 /** | 353 /** |
323 * Convert a rectangle from screen coordinates to 'device' coordinates. | |
324 * | |
325 * This conversion enlarges the original rectangle devicePixelRatio times | |
326 * with the screen center as a fixed point. | |
327 * | |
328 * @param {Rect} rect Rectangle in screen coordinates. | |
329 * @return {Rect} Rectangle in device coordinates. | |
330 */ | |
331 Viewport.prototype.screenToDeviceRect = function(rect) { | |
332 var ratio = window.devicePixelRatio; | |
333 var screenCenterX = Math.round( | |
334 this.screenBounds_.left + this.screenBounds_.width / 2); | |
335 var screenCenterY = Math.round( | |
336 this.screenBounds_.top + this.screenBounds_.height / 2); | |
337 return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, | |
338 screenCenterY + (rect.top - screenCenterY) * ratio, | |
339 rect.width * ratio, | |
340 rect.height * ratio); | |
341 }; | |
342 | |
343 /** | |
344 * @return {Rect} The visible part of the image, in device coordinates. | |
345 */ | |
346 Viewport.prototype.getDeviceClipped = function() { | |
347 return this.screenToDeviceRect(this.getScreenClipped()); | |
348 }; | |
349 | |
350 /** | |
351 * @return {boolean} True if some part of the image is clipped by the screen. | 354 * @return {boolean} True if some part of the image is clipped by the screen. |
352 */ | 355 */ |
353 Viewport.prototype.isClipped = function() { | 356 Viewport.prototype.isClipped = function() { |
354 return this.getMarginX_() < 0 || this.getMarginY_() < 0; | 357 return this.getMarginX_() < 0 || this.getMarginY_() < 0; |
355 }; | 358 }; |
356 | 359 |
357 /** | 360 /** |
358 * @return {number} Horizontal margin. | 361 * @return {number} Horizontal margin. |
359 * Negative if the image is clipped horizontally. | 362 * Negative if the image is clipped horizontally. |
360 * @private | 363 * @private |
(...skipping 27 matching lines...) Expand all Loading... |
388 * @param {number} y Y-offset. | 391 * @param {number} y Y-offset. |
389 * @return {number} Y-offset clamped to the valid range. | 392 * @return {number} Y-offset clamped to the valid range. |
390 * @private | 393 * @private |
391 */ | 394 */ |
392 Viewport.prototype.clampOffsetY_ = function(y) { | 395 Viewport.prototype.clampOffsetY_ = function(y) { |
393 var limit = Math.round(Math.max(0, -this.getMarginY_() / this.getScale())); | 396 var limit = Math.round(Math.max(0, -this.getMarginY_() / this.getScale())); |
394 return ImageUtil.clamp(-limit, y, limit); | 397 return ImageUtil.clamp(-limit, y, limit); |
395 }; | 398 }; |
396 | 399 |
397 /** | 400 /** |
| 401 * @private |
| 402 */ |
| 403 Viewport.prototype.getCenteredRect_ = function( |
| 404 width, height, offsetX, offsetY) { |
| 405 return new Rect( |
| 406 ~~((this.screenBounds_.width - width) / 2) + offsetX, |
| 407 ~~((this.screenBounds_.height - height) / 2) + offsetY, |
| 408 width, |
| 409 height); |
| 410 }; |
| 411 |
| 412 /** |
398 * Recalculate the viewport parameters. | 413 * Recalculate the viewport parameters. |
399 */ | 414 */ |
400 Viewport.prototype.update = function() { | 415 Viewport.prototype.update = function() { |
401 var scale = this.getScale(); | 416 var scale = this.getScale(); |
402 | 417 |
403 // Image bounds in screen coordinates. | 418 // Image bounds on screen. |
404 this.imageOnScreen_ = new Rect( | 419 this.imageBoundsOnScreen_ = this.getCenteredRect_( |
405 this.getMarginX_(), | 420 ~~(this.imageBounds_.width * scale * this.zoom_), |
406 this.getMarginY_(), | 421 ~~(this.imageBounds_.height * scale * this.zoom_), |
407 Math.round(this.imageBounds_.width * scale), | 422 this.offsetX_, |
408 Math.round(this.imageBounds_.height * scale)); | 423 this.offsetY_); |
409 | 424 |
410 // A visible part of the image in image coordinates. | 425 // Image bounds of element (that is not applied zoom and offset) on screen. |
411 this.imageClipped_ = new Rect(this.imageBounds_); | 426 this.imageElementBoundsOnScreen_ = this.getCenteredRect_( |
| 427 ~~(this.imageBounds_.width * scale), |
| 428 ~~(this.imageBounds_.height * scale), |
| 429 0, |
| 430 0); |
412 | 431 |
413 // A visible part of the image in screen coordinates. | 432 // Image bounds on screen cliped with the screen bounds. |
414 this.screenClipped_ = new Rect(this.screenBounds_); | 433 var left = Math.max(this.imageBoundsOnScreen_.left, 0); |
415 | 434 var top = Math.max(this.imageBoundsOnScreen_.top, 0); |
416 // Adjust for the offset. | 435 var right = Math.min( |
417 if (this.imageOnScreen_.left < 0) { | 436 this.imageBoundsOnScreen_.right, this.screenBounds_.width); |
418 this.imageOnScreen_.left += | 437 var bottom = Math.min( |
419 Math.round(this.clampOffsetX_(this.offsetX_) * scale); | 438 this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); |
420 this.imageClipped_.left = Math.round(-this.imageOnScreen_.left / scale); | 439 this.imageBoundsOnScreenClipped_ = new Rect( |
421 this.imageClipped_.width = Math.round(this.screenBounds_.width / scale); | 440 left, top, right - left, bottom - top); |
422 } else { | |
423 this.screenClipped_.left = this.imageOnScreen_.left; | |
424 this.screenClipped_.width = this.imageOnScreen_.width; | |
425 } | |
426 | |
427 if (this.imageOnScreen_.top < 0) { | |
428 this.imageOnScreen_.top += | |
429 Math.round(this.clampOffsetY_(this.offsetY_) * scale); | |
430 this.imageClipped_.top = Math.round(-this.imageOnScreen_.top / scale); | |
431 this.imageClipped_.height = Math.round(this.screenBounds_.height / scale); | |
432 } else { | |
433 this.screenClipped_.top = this.imageOnScreen_.top; | |
434 this.screenClipped_.height = this.imageOnScreen_.height; | |
435 } | |
436 }; | 441 }; |
437 | 442 |
438 /** | 443 /** |
439 * Obtains CSS transformation for the screen image. | 444 * Obtains CSS transformation for the screen image. |
440 * @return {string} Transformation description. | 445 * @return {string} Transformation description. |
441 */ | 446 */ |
442 Viewport.prototype.getTransformation = function() { | 447 Viewport.prototype.getTransformation = function() { |
443 return 'scale(' + (1 / window.devicePixelRatio * this.zoom_) + ')'; | 448 return 'scale(' + (1 / window.devicePixelRatio * this.zoom_) + ')'; |
444 }; | 449 }; |
445 | 450 |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 ].join(' '); | 510 ].join(' '); |
506 }; | 511 }; |
507 | 512 |
508 /** | 513 /** |
509 * Obtains CSS transformaton that makes the image fit to the screen rectangle. | 514 * Obtains CSS transformaton that makes the image fit to the screen rectangle. |
510 * | 515 * |
511 * @param {Rect} screenRect Screen rectangle. | 516 * @param {Rect} screenRect Screen rectangle. |
512 * @return {string} Transformation description. | 517 * @return {string} Transformation description. |
513 */ | 518 */ |
514 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { | 519 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { |
515 var screenImageWidth = this.imageBounds_.width * this.getScale(); | 520 var imageBounds = this.getImageElementBoundsOnScreen(); |
516 var screenImageHeight = this.imageBounds_.height * this.getScale(); | 521 var scaleX = screenRect.width / imageBounds.width; |
517 var scaleX = screenRect.width / screenImageWidth; | 522 var scaleY = screenRect.height / imageBounds.height; |
518 var scaleY = screenRect.height / screenImageHeight; | |
519 var screenWidth = this.screenBounds_.width; | 523 var screenWidth = this.screenBounds_.width; |
520 var screenHeight = this.screenBounds_.height; | 524 var screenHeight = this.screenBounds_.height; |
521 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; | 525 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; |
522 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; | 526 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; |
523 return [ | 527 return [ |
524 'translate(' + dx + 'px,' + dy + 'px)', | 528 'translate(' + dx + 'px,' + dy + 'px)', |
525 'scale(' + scaleX + ',' + scaleY + ')', | 529 'scale(' + scaleX + ',' + scaleY + ')', |
526 this.getTransformation() | 530 this.getTransformation() |
527 ].join(' '); | 531 ].join(' '); |
528 }; | 532 }; |
OLD | NEW |