| 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 |