Chromium Code Reviews| 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 /** | |
| 13 * Size of the full resolution image. | |
| 14 * @type {Rect} | |
| 15 * @private | |
| 16 */ | |
| 12 this.imageBounds_ = new Rect(); | 17 this.imageBounds_ = new Rect(); |
| 18 | |
| 19 /** | |
| 20 * Size of the application window. | |
| 21 * @type {Rect} | |
| 22 * @private | |
| 23 */ | |
| 13 this.screenBounds_ = new Rect(); | 24 this.screenBounds_ = new Rect(); |
| 14 | 25 |
| 26 /** | |
| 27 * Scale from the full resolution image to the screen displayed image. This is | |
| 28 * not zoom operated by users. | |
| 29 */ | |
|
mtomasz
2014/07/10 07:26:52
optional: // for normal comments, /** ... */ for j
hirono
2014/07/10 08:16:06
Done.
| |
| 15 this.scale_ = 1; | 30 this.scale_ = 1; |
| 16 this.offsetX_ = 0; | 31 this.offsetX_ = 0; |
| 17 this.offsetY_ = 0; | 32 this.offsetY_ = 0; |
| 18 | 33 |
| 19 this.generation_ = 0; | 34 this.generation_ = 0; |
| 20 | 35 |
| 21 this.scaleControl_ = null; | |
| 22 this.repaintCallbacks_ = []; | 36 this.repaintCallbacks_ = []; |
| 23 this.update(); | 37 this.update(); |
| 24 } | 38 } |
| 25 | 39 |
| 26 /* | |
| 27 * Viewport modification. | |
| 28 */ | |
| 29 | |
| 30 /** | |
| 31 * @param {Object} scaleControl The UI object responsible for scaling. | |
| 32 */ | |
| 33 Viewport.prototype.setScaleControl = function(scaleControl) { | |
| 34 this.scaleControl_ = scaleControl; | |
| 35 }; | |
| 36 | |
| 37 /** | 40 /** |
| 38 * @param {number} width Image width. | 41 * @param {number} width Image width. |
| 39 * @param {number} height Image height. | 42 * @param {number} height Image height. |
| 40 */ | 43 */ |
| 41 Viewport.prototype.setImageSize = function(width, height) { | 44 Viewport.prototype.setImageSize = function(width, height) { |
| 42 this.imageBounds_ = new Rect(width, height); | 45 this.imageBounds_ = new Rect(width, height); |
| 43 if (this.scaleControl_) this.scaleControl_.displayImageSize(width, height); | |
| 44 this.invalidateCaches(); | 46 this.invalidateCaches(); |
| 45 }; | 47 }; |
| 46 | 48 |
| 47 /** | 49 /** |
| 48 * @param {number} width Screen width. | 50 * @param {number} width Screen width. |
| 49 * @param {number} height Screen height. | 51 * @param {number} height Screen height. |
| 50 */ | 52 */ |
| 51 Viewport.prototype.setScreenSize = function(width, height) { | 53 Viewport.prototype.setScreenSize = function(width, height) { |
| 52 this.screenBounds_ = new Rect(width, height); | 54 this.screenBounds_ = new Rect(width, height); |
| 53 if (this.scaleControl_) | |
| 54 this.scaleControl_.setMinScale(this.getFittingScale()); | |
| 55 this.invalidateCaches(); | 55 this.invalidateCaches(); |
| 56 }; | 56 }; |
| 57 | 57 |
| 58 /** | 58 /** |
| 59 * Set the size by an HTML element. | 59 * Set the size by an HTML element. |
| 60 * | 60 * |
| 61 * @param {HTMLElement} frame The element acting as the "screen". | 61 * @param {HTMLElement} frame The element acting as the "screen". |
| 62 */ | 62 */ |
| 63 Viewport.prototype.sizeByFrame = function(frame) { | 63 Viewport.prototype.sizeByFrame = function(frame) { |
| 64 this.setScreenSize(frame.clientWidth, frame.clientHeight); | 64 this.setScreenSize(frame.clientWidth, frame.clientHeight); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 83 */ | 83 */ |
| 84 Viewport.prototype.getScale = function() { return this.scale_; }; | 84 Viewport.prototype.getScale = function() { return this.scale_; }; |
| 85 | 85 |
| 86 /** | 86 /** |
| 87 * @param {number} scale The new scale. | 87 * @param {number} scale The new scale. |
| 88 * @param {boolean} notify True if the change should be reflected in the UI. | 88 * @param {boolean} notify True if the change should be reflected in the UI. |
| 89 */ | 89 */ |
| 90 Viewport.prototype.setScale = function(scale, notify) { | 90 Viewport.prototype.setScale = function(scale, notify) { |
| 91 if (this.scale_ == scale) return; | 91 if (this.scale_ == scale) return; |
| 92 this.scale_ = scale; | 92 this.scale_ = scale; |
| 93 if (notify && this.scaleControl_) this.scaleControl_.displayScale(scale); | |
| 94 this.invalidateCaches(); | 93 this.invalidateCaches(); |
| 95 }; | 94 }; |
| 96 | 95 |
| 97 /** | 96 /** |
| 98 * @return {number} Best scale to fit the current image into the current screen. | 97 * @return {number} Best scale to fit the current image into the current screen. |
| 99 */ | 98 */ |
| 100 Viewport.prototype.getFittingScale = function() { | 99 Viewport.prototype.getFittingScale = function() { |
| 101 var scaleX = this.screenBounds_.width / this.imageBounds_.width; | 100 return this.getFittingScaleForImageSize_( |
| 102 var scaleY = this.screenBounds_.height / this.imageBounds_.height; | 101 this.imageBounds_.width, this.imageBounds_.height); |
| 102 }; | |
| 103 | |
| 104 Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { | |
|
mtomasz
2014/07/10 07:26:52
nit: Please add jsdoc.
hirono
2014/07/10 08:16:05
Done.
| |
| 105 var scaleX = this.screenBounds_.width / width; | |
| 106 var scaleY = this.screenBounds_.height / height; | |
| 103 // Scales > (1 / this.getDevicePixelRatio()) do not look good. Also they are | 107 // Scales > (1 / this.getDevicePixelRatio()) do not look good. Also they are |
|
mtomasz
2014/07/10 07:26:52
Please update the comment (this.getDevicePixelRati
hirono
2014/07/10 08:16:06
Done.
| |
| 104 // not really useful as we do not have any pixel-level operations. | 108 // not really useful as we do not have any pixel-level operations. |
| 105 return Math.min(1 / Viewport.getDevicePixelRatio(), scaleX, scaleY); | 109 return Math.min(1 / devicePixelRatio, scaleX, scaleY); |
|
mtomasz
2014/07/10 07:26:52
nit: devicePixelRatio looks undefined. How about:
hirono
2014/07/10 08:16:06
Done.
| |
| 106 }; | 110 } |
| 107 | 111 |
| 108 /** | 112 /** |
| 109 * Set the scale to fit the image into the screen. | 113 * Set the scale to fit the image into the screen. |
| 110 */ | 114 */ |
| 111 Viewport.prototype.fitImage = function() { | 115 Viewport.prototype.fitImage = function() { |
| 112 var scale = this.getFittingScale(); | 116 var scale = this.getFittingScale(); |
| 113 if (this.scaleControl_) this.scaleControl_.setMinScale(scale); | |
| 114 this.setScale(scale, true); | 117 this.setScale(scale, true); |
| 115 }; | 118 }; |
| 116 | 119 |
| 117 /** | 120 /** |
| 118 * @return {number} X-offset of the viewport. | 121 * @return {number} X-offset of the viewport. |
| 119 */ | 122 */ |
| 120 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; | 123 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; |
| 121 | 124 |
| 122 /** | 125 /** |
| 123 * @return {number} Y-offset of the viewport. | 126 * @return {number} Y-offset of the viewport. |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 */ | 291 */ |
| 289 Viewport.prototype.imageToScreenRect = function(rect) { | 292 Viewport.prototype.imageToScreenRect = function(rect) { |
| 290 return new Rect( | 293 return new Rect( |
| 291 this.imageToScreenX(rect.left), | 294 this.imageToScreenX(rect.left), |
| 292 this.imageToScreenY(rect.top), | 295 this.imageToScreenY(rect.top), |
| 293 Math.round(this.imageToScreenSize(rect.width)), | 296 Math.round(this.imageToScreenSize(rect.width)), |
| 294 Math.round(this.imageToScreenSize(rect.height))); | 297 Math.round(this.imageToScreenSize(rect.height))); |
| 295 }; | 298 }; |
| 296 | 299 |
| 297 /** | 300 /** |
| 298 * @return {number} The number of physical pixels in one CSS pixel. | |
| 299 */ | |
| 300 Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio; }; | |
| 301 | |
| 302 /** | |
| 303 * Convert a rectangle from screen coordinates to 'device' coordinates. | 301 * Convert a rectangle from screen coordinates to 'device' coordinates. |
| 304 * | 302 * |
| 305 * This conversion enlarges the original rectangle devicePixelRatio times | 303 * This conversion enlarges the original rectangle devicePixelRatio times |
| 306 * with the screen center as a fixed point. | 304 * with the screen center as a fixed point. |
| 307 * | 305 * |
| 308 * @param {Rect} rect Rectangle in screen coordinates. | 306 * @param {Rect} rect Rectangle in screen coordinates. |
| 309 * @return {Rect} Rectangle in device coordinates. | 307 * @return {Rect} Rectangle in device coordinates. |
| 310 */ | 308 */ |
| 311 Viewport.prototype.screenToDeviceRect = function(rect) { | 309 Viewport.prototype.screenToDeviceRect = function(rect) { |
| 312 var ratio = Viewport.getDevicePixelRatio(); | 310 var ratio = devicePixelRatio; |
| 313 var screenCenterX = Math.round( | 311 var screenCenterX = Math.round( |
| 314 this.screenBounds_.left + this.screenBounds_.width / 2); | 312 this.screenBounds_.left + this.screenBounds_.width / 2); |
| 315 var screenCenterY = Math.round( | 313 var screenCenterY = Math.round( |
| 316 this.screenBounds_.top + this.screenBounds_.height / 2); | 314 this.screenBounds_.top + this.screenBounds_.height / 2); |
| 317 return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, | 315 return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, |
| 318 screenCenterY + (rect.top - screenCenterY) * ratio, | 316 screenCenterY + (rect.top - screenCenterY) * ratio, |
| 319 rect.width * ratio, | 317 rect.width * ratio, |
| 320 rect.height * ratio); | 318 rect.height * ratio); |
| 321 }; | 319 }; |
| 322 | 320 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 423 }; | 421 }; |
| 424 | 422 |
| 425 /** | 423 /** |
| 426 * Repaint all clients. | 424 * Repaint all clients. |
| 427 */ | 425 */ |
| 428 Viewport.prototype.repaint = function() { | 426 Viewport.prototype.repaint = function() { |
| 429 this.update(); | 427 this.update(); |
| 430 for (var i = 0; i != this.repaintCallbacks_.length; i++) | 428 for (var i = 0; i != this.repaintCallbacks_.length; i++) |
| 431 this.repaintCallbacks_[i](); | 429 this.repaintCallbacks_[i](); |
| 432 }; | 430 }; |
| 431 | |
| 432 /** | |
| 433 * Obtains CSS transformation for the screen image. | |
| 434 * @return {string} Transformation description. | |
| 435 */ | |
| 436 Viewport.prototype.getTransformation = function() { | |
| 437 return 'scale(' + (1 / devicePixelRatio) + ')'; | |
| 438 }; | |
| 439 | |
| 440 /** | |
| 441 * Obtains shift CSS transformation for the screen image. | |
| 442 * @param {number} dx Amount of shift. | |
| 443 * @return {string} Transformation description. | |
| 444 */ | |
| 445 Viewport.prototype.getShiftTransformation = function(dx) { | |
| 446 return 'translateX(' + dx + 'px) ' + this.getTransformation(); | |
| 447 }; | |
| 448 | |
| 449 /** | |
| 450 * Obtains CSS transformation that makes the rotated image fit the original | |
| 451 * image. The new rotated image that the transformation is applied to looks the | |
| 452 * same with original image. | |
| 453 * | |
| 454 * @param {boolean} orientation Orientation of the rotation from the original | |
| 455 * image to the rotated image. True is for clockwise and false is for | |
| 456 * counterclockwise. | |
| 457 * @return {string} Transformation description. | |
| 458 */ | |
| 459 Viewport.prototype.getInverseTransformForRotatedImage = function(orientation) { | |
| 460 var previousImageWidth = this.imageBounds_.height; | |
| 461 var previousImageHeight = this.imageBounds_.width; | |
| 462 var oldScale = this.getFittingScaleForImageSize_( | |
| 463 previousImageWidth, previousImageHeight); | |
| 464 var scaleRatio = oldScale / this.getScale(); | |
| 465 var degree = orientation ? '-90deg' : '90deg'; | |
| 466 return [ | |
| 467 'scale(' + (scaleRatio) + ')', | |
|
mtomasz
2014/07/10 07:26:52
nit: inner () redundant?
hirono
2014/07/10 08:16:06
Done.
| |
| 468 'rotate(' + degree + ')', | |
| 469 this.getTransformation() | |
| 470 ].join(' '); | |
| 471 }; | |
| 472 | |
| 473 /** | |
| 474 * Obtains CSS transformation that makes the cropped image fit the original | |
| 475 * image. The new cropped image that the transformaton is applied to fits to the | |
| 476 * the cropped rectangle in the original image. | |
| 477 * | |
| 478 * @param {number} imageWidth Width of the original image. | |
| 479 * @param {number} imageHeight Height of the origianl image. | |
| 480 * @param {Rect} imageCropRect Crop rectangle in the image's coordinate system. | |
| 481 * @return {string} Transformation description. | |
| 482 */ | |
| 483 Viewport.prototype.getInverseTransformForCroppedImage = | |
| 484 function(imageWidth, imageHeight, imageCropRect) { | |
| 485 var wholeScale = this.getFittingScaleForImageSize_( | |
| 486 imageWidth, imageHeight); | |
| 487 var croppedScale = this.getFittingScaleForImageSize_( | |
| 488 imageCropRect.width, imageCropRect.height); | |
| 489 var dx = | |
| 490 (imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) * | |
| 491 wholeScale; | |
| 492 var dy = | |
| 493 (imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) * | |
| 494 wholeScale; | |
| 495 return [ | |
| 496 'translate(' + dx + 'px,' + dy + 'px)', | |
| 497 'scale(' + wholeScale / croppedScale + ')', | |
| 498 this.getTransformation() | |
| 499 ].join(' '); | |
| 500 }; | |
| 501 | |
| 502 /** | |
| 503 * Obtains CSS transformaton that makes the image fit to the screen rectangle. | |
| 504 * | |
| 505 * @param {Rect} screenRect Screen rectangle. | |
| 506 * @return {string} Transformation description. | |
| 507 */ | |
| 508 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { | |
| 509 var screenImageWidth = this.imageBounds_.width * this.getScale(); | |
| 510 var screenImageHeight = this.imageBounds_.height * this.getScale(); | |
| 511 var scaleX = screenRect.width / screenImageWidth; | |
| 512 var scaleY = screenRect.height /screenImageHeight; | |
|
mtomasz
2014/07/10 07:26:52
nit: space after '/'.
hirono
2014/07/10 08:16:06
Done.
| |
| 513 var screenWidth = this.screenBounds_.width; | |
| 514 var screenHeight = this.screenBounds_.height; | |
| 515 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; | |
| 516 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; | |
| 517 return [ | |
| 518 'translate(' + dx + 'px,' + dy + 'px)', | |
| 519 'scale(' + scaleX + ',' + scaleY + ')', | |
| 520 this.getTransformation() | |
| 521 ].join(' '); | |
| 522 }; | |
| OLD | NEW |