| 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 {ImageRect} |
| 15 * @private | 15 * @private |
| 16 */ | 16 */ |
| 17 this.imageBounds_ = new Rect(); | 17 this.imageBounds_ = new ImageRect(); |
| 18 | 18 |
| 19 /** | 19 /** |
| 20 * Size of the application window. | 20 * Size of the application window. |
| 21 * @type {Rect} | 21 * @type {ImageRect} |
| 22 * @private | 22 * @private |
| 23 */ | 23 */ |
| 24 this.screenBounds_ = new Rect(); | 24 this.screenBounds_ = new ImageRect(); |
| 25 | 25 |
| 26 /** | 26 /** |
| 27 * Bounds of the image element on screen without zoom and offset. | 27 * Bounds of the image element on screen without zoom and offset. |
| 28 * @type {Rect} | 28 * @type {ImageRect} |
| 29 * @private | 29 * @private |
| 30 */ | 30 */ |
| 31 this.imageElementBoundsOnScreen_ = null; | 31 this.imageElementBoundsOnScreen_ = null; |
| 32 | 32 |
| 33 /** | 33 /** |
| 34 * Bounds of the image with zoom and offset. | 34 * Bounds of the image with zoom and offset. |
| 35 * @type {Rect} | 35 * @type {ImageRect} |
| 36 * @private | 36 * @private |
| 37 */ | 37 */ |
| 38 this.imageBoundsOnScreen_ = null; | 38 this.imageBoundsOnScreen_ = null; |
| 39 | 39 |
| 40 /** | 40 /** |
| 41 * Image bounds that is clipped with the screen bounds. | 41 * Image bounds that is clipped with the screen bounds. |
| 42 * @type {Rect} | 42 * @type {ImageRect} |
| 43 * @private | 43 * @private |
| 44 */ | 44 */ |
| 45 this.imageBoundsOnScreenClipped_ = null; | 45 this.imageBoundsOnScreenClipped_ = null; |
| 46 | 46 |
| 47 /** | 47 /** |
| 48 * 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 |
| 49 * not zoom operated by users. | 49 * not zoom operated by users. |
| 50 * @type {number} | 50 * @type {number} |
| 51 * @private | 51 * @private |
| 52 */ | 52 */ |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 * @type {Array.<number>} | 99 * @type {Array.<number>} |
| 100 * @const | 100 * @const |
| 101 */ | 101 */ |
| 102 Viewport.ZOOM_RATIOS = Object.freeze([1, 1.5, 2, 3]); | 102 Viewport.ZOOM_RATIOS = Object.freeze([1, 1.5, 2, 3]); |
| 103 | 103 |
| 104 /** | 104 /** |
| 105 * @param {number} width Image width. | 105 * @param {number} width Image width. |
| 106 * @param {number} height Image height. | 106 * @param {number} height Image height. |
| 107 */ | 107 */ |
| 108 Viewport.prototype.setImageSize = function(width, height) { | 108 Viewport.prototype.setImageSize = function(width, height) { |
| 109 this.imageBounds_ = new Rect(width, height); | 109 this.imageBounds_ = new ImageRect(width, height); |
| 110 this.update_(); | 110 this.update_(); |
| 111 }; | 111 }; |
| 112 | 112 |
| 113 /** | 113 /** |
| 114 * @param {number} width Screen width. | 114 * @param {number} width Screen width. |
| 115 * @param {number} height Screen height. | 115 * @param {number} height Screen height. |
| 116 */ | 116 */ |
| 117 Viewport.prototype.setScreenSize = function(width, height) { | 117 Viewport.prototype.setScreenSize = function(width, height) { |
| 118 this.screenBounds_ = new Rect(width, height); | 118 this.screenBounds_ = new ImageRect(width, height); |
| 119 this.update_(); | 119 this.update_(); |
| 120 }; | 120 }; |
| 121 | 121 |
| 122 /** | 122 /** |
| 123 * Sets zoom value directly. | 123 * Sets zoom value directly. |
| 124 * @param {number} zoom New zoom value. | 124 * @param {number} zoom New zoom value. |
| 125 */ | 125 */ |
| 126 Viewport.prototype.setZoom = function(zoom) { | 126 Viewport.prototype.setZoom = function(zoom) { |
| 127 var zoomMin = Viewport.ZOOM_RATIOS[0]; | 127 var zoomMin = Viewport.ZOOM_RATIOS[0]; |
| 128 var zoomMax = Viewport.ZOOM_RATIOS[Viewport.ZOOM_RATIOS.length - 1]; | 128 var zoomMax = Viewport.ZOOM_RATIOS[Viewport.ZOOM_RATIOS.length - 1]; |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 */ | 225 */ |
| 226 Viewport.prototype.setOffset = function(x, y) { | 226 Viewport.prototype.setOffset = function(x, y) { |
| 227 if (this.offsetX_ == x && this.offsetY_ == y) | 227 if (this.offsetX_ == x && this.offsetY_ == y) |
| 228 return; | 228 return; |
| 229 this.offsetX_ = x; | 229 this.offsetX_ = x; |
| 230 this.offsetY_ = y; | 230 this.offsetY_ = y; |
| 231 this.update_(); | 231 this.update_(); |
| 232 }; | 232 }; |
| 233 | 233 |
| 234 /** | 234 /** |
| 235 * @return {Rect} The image bounds in image coordinates. | 235 * @return {ImageRect} The image bounds in image coordinates. |
| 236 */ | 236 */ |
| 237 Viewport.prototype.getImageBounds = function() { return this.imageBounds_; }; | 237 Viewport.prototype.getImageBounds = function() { return this.imageBounds_; }; |
| 238 | 238 |
| 239 /** | 239 /** |
| 240 * @return {Rect} The screen bounds in screen coordinates. | 240 * @return {ImageRect} The screen bounds in screen coordinates. |
| 241 */ | 241 */ |
| 242 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; | 242 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; |
| 243 | 243 |
| 244 /** | 244 /** |
| 245 * @return {Rect} The size of screen cache canvas. | 245 * @return {ImageRect} The size of screen cache canvas. |
| 246 */ | 246 */ |
| 247 Viewport.prototype.getDeviceBounds = function() { | 247 Viewport.prototype.getDeviceBounds = function() { |
| 248 var size = this.getImageElementBoundsOnScreen(); | 248 var size = this.getImageElementBoundsOnScreen(); |
| 249 return new Rect( | 249 return new ImageRect( |
| 250 size.width * window.devicePixelRatio, | 250 size.width * window.devicePixelRatio, |
| 251 size.height * window.devicePixelRatio); | 251 size.height * window.devicePixelRatio); |
| 252 }; | 252 }; |
| 253 | 253 |
| 254 /** | 254 /** |
| 255 * A counter that is incremented with each viewport state change. | 255 * A counter that is incremented with each viewport state change. |
| 256 * Clients that cache anything that depends on the viewport state should keep | 256 * Clients that cache anything that depends on the viewport state should keep |
| 257 * track of this counter. | 257 * track of this counter. |
| 258 * @return {number} counter. | 258 * @return {number} counter. |
| 259 */ | 259 */ |
| 260 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; | 260 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; |
| 261 | 261 |
| 262 /** | 262 /** |
| 263 * @return {Rect} The image bounds in screen coordinates. | 263 * @return {ImageRect} The image bounds in screen coordinates. |
| 264 */ | 264 */ |
| 265 Viewport.prototype.getImageBoundsOnScreen = function() { | 265 Viewport.prototype.getImageBoundsOnScreen = function() { |
| 266 return this.imageBoundsOnScreen_; | 266 return this.imageBoundsOnScreen_; |
| 267 }; | 267 }; |
| 268 | 268 |
| 269 /** | 269 /** |
| 270 * The image bounds in screen coordinates. | 270 * The image bounds in screen coordinates. |
| 271 * This returns the bounds of element before applying zoom and offset. | 271 * This returns the bounds of element before applying zoom and offset. |
| 272 * @return {Rect} | 272 * @return {ImageRect} |
| 273 */ | 273 */ |
| 274 Viewport.prototype.getImageElementBoundsOnScreen = function() { | 274 Viewport.prototype.getImageElementBoundsOnScreen = function() { |
| 275 return this.imageElementBoundsOnScreen_; | 275 return this.imageElementBoundsOnScreen_; |
| 276 }; | 276 }; |
| 277 | 277 |
| 278 /** | 278 /** |
| 279 * The image bounds on screen, which is clipped with the screen size. | 279 * The image bounds on screen, which is clipped with the screen size. |
| 280 * @return {Rect} | 280 * @return {ImageRect} |
| 281 */ | 281 */ |
| 282 Viewport.prototype.getImageBoundsOnScreenClipped = function() { | 282 Viewport.prototype.getImageBoundsOnScreenClipped = function() { |
| 283 return this.imageBoundsOnScreenClipped_; | 283 return this.imageBoundsOnScreenClipped_; |
| 284 }; | 284 }; |
| 285 | 285 |
| 286 /** | 286 /** |
| 287 * @param {number} size Size in screen coordinates. | 287 * @param {number} size Size in screen coordinates. |
| 288 * @return {number} Size in image coordinates. | 288 * @return {number} Size in image coordinates. |
| 289 */ | 289 */ |
| 290 Viewport.prototype.screenToImageSize = function(size) { | 290 Viewport.prototype.screenToImageSize = function(size) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 301 | 301 |
| 302 /** | 302 /** |
| 303 * @param {number} y Y in screen coordinates. | 303 * @param {number} y Y in screen coordinates. |
| 304 * @return {number} Y in image coordinates. | 304 * @return {number} Y in image coordinates. |
| 305 */ | 305 */ |
| 306 Viewport.prototype.screenToImageY = function(y) { | 306 Viewport.prototype.screenToImageY = function(y) { |
| 307 return Math.round((y - this.imageBoundsOnScreen_.top) / this.scale_); | 307 return Math.round((y - this.imageBoundsOnScreen_.top) / this.scale_); |
| 308 }; | 308 }; |
| 309 | 309 |
| 310 /** | 310 /** |
| 311 * @param {Rect} rect Rectangle in screen coordinates. | 311 * @param {ImageRect} rect Rectangle in screen coordinates. |
| 312 * @return {Rect} Rectangle in image coordinates. | 312 * @return {ImageRect} Rectangle in image coordinates. |
| 313 */ | 313 */ |
| 314 Viewport.prototype.screenToImageRect = function(rect) { | 314 Viewport.prototype.screenToImageRect = function(rect) { |
| 315 return new Rect( | 315 return new ImageRect( |
| 316 this.screenToImageX(rect.left), | 316 this.screenToImageX(rect.left), |
| 317 this.screenToImageY(rect.top), | 317 this.screenToImageY(rect.top), |
| 318 this.screenToImageSize(rect.width), | 318 this.screenToImageSize(rect.width), |
| 319 this.screenToImageSize(rect.height)); | 319 this.screenToImageSize(rect.height)); |
| 320 }; | 320 }; |
| 321 | 321 |
| 322 /** | 322 /** |
| 323 * @param {number} size Size in image coordinates. | 323 * @param {number} size Size in image coordinates. |
| 324 * @return {number} Size in screen coordinates. | 324 * @return {number} Size in screen coordinates. |
| 325 */ | 325 */ |
| (...skipping 11 matching lines...) Expand all Loading... |
| 337 | 337 |
| 338 /** | 338 /** |
| 339 * @param {number} y Y in image coordinates. | 339 * @param {number} y Y in image coordinates. |
| 340 * @return {number} Y in screen coordinates. | 340 * @return {number} Y in screen coordinates. |
| 341 */ | 341 */ |
| 342 Viewport.prototype.imageToScreenY = function(y) { | 342 Viewport.prototype.imageToScreenY = function(y) { |
| 343 return Math.round(this.imageBoundsOnScreen_.top + y * this.scale_); | 343 return Math.round(this.imageBoundsOnScreen_.top + y * this.scale_); |
| 344 }; | 344 }; |
| 345 | 345 |
| 346 /** | 346 /** |
| 347 * @param {Rect} rect Rectangle in image coordinates. | 347 * @param {ImageRect} rect Rectangle in image coordinates. |
| 348 * @return {Rect} Rectangle in screen coordinates. | 348 * @return {ImageRect} Rectangle in screen coordinates. |
| 349 */ | 349 */ |
| 350 Viewport.prototype.imageToScreenRect = function(rect) { | 350 Viewport.prototype.imageToScreenRect = function(rect) { |
| 351 return new Rect( | 351 return new ImageRect( |
| 352 this.imageToScreenX(rect.left), | 352 this.imageToScreenX(rect.left), |
| 353 this.imageToScreenY(rect.top), | 353 this.imageToScreenY(rect.top), |
| 354 Math.round(this.imageToScreenSize(rect.width)), | 354 Math.round(this.imageToScreenSize(rect.width)), |
| 355 Math.round(this.imageToScreenSize(rect.height))); | 355 Math.round(this.imageToScreenSize(rect.height))); |
| 356 }; | 356 }; |
| 357 | 357 |
| 358 /** | 358 /** |
| 359 * @param {number} width Width of the rectangle. | 359 * @param {number} width Width of the rectangle. |
| 360 * @param {number} height Height of the rectangle. | 360 * @param {number} height Height of the rectangle. |
| 361 * @param {number} offsetX X-offset of center position of the rectangle. | 361 * @param {number} offsetX X-offset of center position of the rectangle. |
| 362 * @param {number} offsetY Y-offset of center position of the rectangle. | 362 * @param {number} offsetY Y-offset of center position of the rectangle. |
| 363 * @return {Rect} Rectangle with given geometry. | 363 * @return {ImageRect} Rectangle with given geometry. |
| 364 * @private | 364 * @private |
| 365 */ | 365 */ |
| 366 Viewport.prototype.getCenteredRect_ = function( | 366 Viewport.prototype.getCenteredRect_ = function( |
| 367 width, height, offsetX, offsetY) { | 367 width, height, offsetX, offsetY) { |
| 368 return new Rect( | 368 return new ImageRect( |
| 369 ~~((this.screenBounds_.width - width) / 2) + offsetX, | 369 ~~((this.screenBounds_.width - width) / 2) + offsetX, |
| 370 ~~((this.screenBounds_.height - height) / 2) + offsetY, | 370 ~~((this.screenBounds_.height - height) / 2) + offsetY, |
| 371 width, | 371 width, |
| 372 height); | 372 height); |
| 373 }; | 373 }; |
| 374 | 374 |
| 375 /** | 375 /** |
| 376 * Resets zoom and offset. | 376 * Resets zoom and offset. |
| 377 */ | 377 */ |
| 378 Viewport.prototype.resetView = function() { | 378 Viewport.prototype.resetView = function() { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 this.generation_++; | 426 this.generation_++; |
| 427 } | 427 } |
| 428 | 428 |
| 429 // Image bounds on screen clipped with the screen bounds. | 429 // Image bounds on screen clipped with the screen bounds. |
| 430 var left = Math.max(this.imageBoundsOnScreen_.left, 0); | 430 var left = Math.max(this.imageBoundsOnScreen_.left, 0); |
| 431 var top = Math.max(this.imageBoundsOnScreen_.top, 0); | 431 var top = Math.max(this.imageBoundsOnScreen_.top, 0); |
| 432 var right = Math.min( | 432 var right = Math.min( |
| 433 this.imageBoundsOnScreen_.right, this.screenBounds_.width); | 433 this.imageBoundsOnScreen_.right, this.screenBounds_.width); |
| 434 var bottom = Math.min( | 434 var bottom = Math.min( |
| 435 this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); | 435 this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); |
| 436 this.imageBoundsOnScreenClipped_ = new Rect( | 436 this.imageBoundsOnScreenClipped_ = new ImageRect( |
| 437 left, top, right - left, bottom - top); | 437 left, top, right - left, bottom - top); |
| 438 }; | 438 }; |
| 439 | 439 |
| 440 /** | 440 /** |
| 441 * Clones the viewport. | 441 * Clones the viewport. |
| 442 * @return {Viewport} New instance. | 442 * @return {Viewport} New instance. |
| 443 */ | 443 */ |
| 444 Viewport.prototype.clone = function() { | 444 Viewport.prototype.clone = function() { |
| 445 var viewport = new Viewport(); | 445 var viewport = new Viewport(); |
| 446 viewport.imageBounds_ = new Rect(this.imageBounds_); | 446 viewport.imageBounds_ = new ImageRect(this.imageBounds_); |
| 447 viewport.screenBounds_ = new Rect(this.screenBounds_); | 447 viewport.screenBounds_ = new ImageRect(this.screenBounds_); |
| 448 viewport.scale_ = this.scale_; | 448 viewport.scale_ = this.scale_; |
| 449 viewport.zoom_ = this.zoom_; | 449 viewport.zoom_ = this.zoom_; |
| 450 viewport.offsetX_ = this.offsetX_; | 450 viewport.offsetX_ = this.offsetX_; |
| 451 viewport.offsetY_ = this.offsetY_; | 451 viewport.offsetY_ = this.offsetY_; |
| 452 viewport.rotation_ = this.rotation_; | 452 viewport.rotation_ = this.rotation_; |
| 453 viewport.generation_ = this.generation_; | 453 viewport.generation_ = this.generation_; |
| 454 viewport.update_(); | 454 viewport.update_(); |
| 455 return viewport; | 455 return viewport; |
| 456 }; | 456 }; |
| 457 | 457 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 ].join(' '); | 507 ].join(' '); |
| 508 }; | 508 }; |
| 509 | 509 |
| 510 /** | 510 /** |
| 511 * Obtains CSS transformation that makes the cropped image fit the original | 511 * Obtains CSS transformation that makes the cropped image fit the original |
| 512 * image. The new cropped image that the transformation is applied to fits to | 512 * image. The new cropped image that the transformation is applied to fits to |
| 513 * the cropped rectangle in the original image. | 513 * the cropped rectangle in the original image. |
| 514 * | 514 * |
| 515 * @param {number} imageWidth Width of the original image. | 515 * @param {number} imageWidth Width of the original image. |
| 516 * @param {number} imageHeight Height of the original image. | 516 * @param {number} imageHeight Height of the original image. |
| 517 * @param {Rect} imageCropRect Crop rectangle in the image's coordinate system. | 517 * @param {ImageRect} imageCropRect Crop rectangle in the image's coordinate |
| 518 * system. |
| 518 * @return {string} Transformation description. | 519 * @return {string} Transformation description. |
| 519 */ | 520 */ |
| 520 Viewport.prototype.getInverseTransformForCroppedImage = | 521 Viewport.prototype.getInverseTransformForCroppedImage = |
| 521 function(imageWidth, imageHeight, imageCropRect) { | 522 function(imageWidth, imageHeight, imageCropRect) { |
| 522 var wholeScale = this.getFittingScaleForImageSize_( | 523 var wholeScale = this.getFittingScaleForImageSize_( |
| 523 imageWidth, imageHeight); | 524 imageWidth, imageHeight); |
| 524 var croppedScale = this.getFittingScaleForImageSize_( | 525 var croppedScale = this.getFittingScaleForImageSize_( |
| 525 imageCropRect.width, imageCropRect.height); | 526 imageCropRect.width, imageCropRect.height); |
| 526 var dx = | 527 var dx = |
| 527 (imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) * | 528 (imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) * |
| 528 wholeScale; | 529 wholeScale; |
| 529 var dy = | 530 var dy = |
| 530 (imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) * | 531 (imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) * |
| 531 wholeScale; | 532 wholeScale; |
| 532 return [ | 533 return [ |
| 533 'translate(' + dx + 'px,' + dy + 'px)', | 534 'translate(' + dx + 'px,' + dy + 'px)', |
| 534 'scale(' + wholeScale / croppedScale + ')', | 535 'scale(' + wholeScale / croppedScale + ')', |
| 535 this.getTransformation() | 536 this.getTransformation() |
| 536 ].join(' '); | 537 ].join(' '); |
| 537 }; | 538 }; |
| 538 | 539 |
| 539 /** | 540 /** |
| 540 * Obtains CSS transformation that makes the image fit to the screen rectangle. | 541 * Obtains CSS transformation that makes the image fit to the screen rectangle. |
| 541 * | 542 * |
| 542 * @param {Rect} screenRect Screen rectangle. | 543 * @param {ImageRect} screenRect Screen rectangle. |
| 543 * @return {string} Transformation description. | 544 * @return {string} Transformation description. |
| 544 */ | 545 */ |
| 545 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { | 546 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { |
| 546 var imageBounds = this.getImageElementBoundsOnScreen(); | 547 var imageBounds = this.getImageElementBoundsOnScreen(); |
| 547 var scaleX = screenRect.width / imageBounds.width; | 548 var scaleX = screenRect.width / imageBounds.width; |
| 548 var scaleY = screenRect.height / imageBounds.height; | 549 var scaleY = screenRect.height / imageBounds.height; |
| 549 var screenWidth = this.screenBounds_.width; | 550 var screenWidth = this.screenBounds_.width; |
| 550 var screenHeight = this.screenBounds_.height; | 551 var screenHeight = this.screenBounds_.height; |
| 551 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; | 552 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; |
| 552 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; | 553 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; |
| 553 return [ | 554 return [ |
| 554 'translate(' + dx + 'px,' + dy + 'px)', | 555 'translate(' + dx + 'px,' + dy + 'px)', |
| 555 'scale(' + scaleX + ',' + scaleY + ')', | 556 'scale(' + scaleX + ',' + scaleY + ')', |
| 556 this.getTransformation() | 557 this.getTransformation() |
| 557 ].join(' '); | 558 ].join(' '); |
| 558 }; | 559 }; |
| OLD | NEW |