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 * @type {number} |
| 30 * @private |
| 31 */ |
15 this.scale_ = 1; | 32 this.scale_ = 1; |
16 this.offsetX_ = 0; | 33 this.offsetX_ = 0; |
17 this.offsetY_ = 0; | 34 this.offsetY_ = 0; |
18 | 35 |
19 this.generation_ = 0; | 36 this.generation_ = 0; |
20 | 37 |
21 this.repaintCallbacks_ = []; | 38 this.repaintCallbacks_ = []; |
22 this.update(); | 39 this.update(); |
23 } | 40 } |
24 | 41 |
25 /* | |
26 * Viewport modification. | |
27 */ | |
28 | |
29 /** | 42 /** |
30 * @param {number} width Image width. | 43 * @param {number} width Image width. |
31 * @param {number} height Image height. | 44 * @param {number} height Image height. |
32 */ | 45 */ |
33 Viewport.prototype.setImageSize = function(width, height) { | 46 Viewport.prototype.setImageSize = function(width, height) { |
34 this.imageBounds_ = new Rect(width, height); | 47 this.imageBounds_ = new Rect(width, height); |
35 this.invalidateCaches(); | 48 this.invalidateCaches(); |
36 }; | 49 }; |
37 | 50 |
38 /** | 51 /** |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 Viewport.prototype.setScale = function(scale, notify) { | 92 Viewport.prototype.setScale = function(scale, notify) { |
80 if (this.scale_ == scale) return; | 93 if (this.scale_ == scale) return; |
81 this.scale_ = scale; | 94 this.scale_ = scale; |
82 this.invalidateCaches(); | 95 this.invalidateCaches(); |
83 }; | 96 }; |
84 | 97 |
85 /** | 98 /** |
86 * @return {number} Best scale to fit the current image into the current screen. | 99 * @return {number} Best scale to fit the current image into the current screen. |
87 */ | 100 */ |
88 Viewport.prototype.getFittingScale = function() { | 101 Viewport.prototype.getFittingScale = function() { |
89 var scaleX = this.screenBounds_.width / this.imageBounds_.width; | 102 return this.getFittingScaleForImageSize_( |
90 var scaleY = this.screenBounds_.height / this.imageBounds_.height; | 103 this.imageBounds_.width, this.imageBounds_.height); |
91 // Scales > (1 / this.getDevicePixelRatio()) do not look good. Also they are | |
92 // not really useful as we do not have any pixel-level operations. | |
93 return Math.min(1 / Viewport.getDevicePixelRatio(), scaleX, scaleY); | |
94 }; | 104 }; |
95 | 105 |
96 /** | 106 /** |
| 107 * Obtains the scale for the specified image size. |
| 108 * |
| 109 * @param {number} width Width of the full resolution image. |
| 110 * @param {number} height Height of the full resolution image. |
| 111 * @return {number} The ratio of the fullresotion image size and the calculated |
| 112 * displayed image size. |
| 113 */ |
| 114 Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { |
| 115 var scaleX = this.screenBounds_.width / width; |
| 116 var scaleY = this.screenBounds_.height / height; |
| 117 // Scales > (1 / devicePixelRatio) do not look good. Also they are |
| 118 // not really useful as we do not have any pixel-level operations. |
| 119 return Math.min(1 / window.devicePixelRatio, scaleX, scaleY); |
| 120 }; |
| 121 |
| 122 /** |
97 * Set the scale to fit the image into the screen. | 123 * Set the scale to fit the image into the screen. |
98 */ | 124 */ |
99 Viewport.prototype.fitImage = function() { | 125 Viewport.prototype.fitImage = function() { |
100 var scale = this.getFittingScale(); | 126 var scale = this.getFittingScale(); |
101 this.setScale(scale, true); | 127 this.setScale(scale, true); |
102 }; | 128 }; |
103 | 129 |
104 /** | 130 /** |
105 * @return {number} X-offset of the viewport. | 131 * @return {number} X-offset of the viewport. |
106 */ | 132 */ |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 */ | 301 */ |
276 Viewport.prototype.imageToScreenRect = function(rect) { | 302 Viewport.prototype.imageToScreenRect = function(rect) { |
277 return new Rect( | 303 return new Rect( |
278 this.imageToScreenX(rect.left), | 304 this.imageToScreenX(rect.left), |
279 this.imageToScreenY(rect.top), | 305 this.imageToScreenY(rect.top), |
280 Math.round(this.imageToScreenSize(rect.width)), | 306 Math.round(this.imageToScreenSize(rect.width)), |
281 Math.round(this.imageToScreenSize(rect.height))); | 307 Math.round(this.imageToScreenSize(rect.height))); |
282 }; | 308 }; |
283 | 309 |
284 /** | 310 /** |
285 * @return {number} The number of physical pixels in one CSS pixel. | |
286 */ | |
287 Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio; }; | |
288 | |
289 /** | |
290 * Convert a rectangle from screen coordinates to 'device' coordinates. | 311 * Convert a rectangle from screen coordinates to 'device' coordinates. |
291 * | 312 * |
292 * This conversion enlarges the original rectangle devicePixelRatio times | 313 * This conversion enlarges the original rectangle devicePixelRatio times |
293 * with the screen center as a fixed point. | 314 * with the screen center as a fixed point. |
294 * | 315 * |
295 * @param {Rect} rect Rectangle in screen coordinates. | 316 * @param {Rect} rect Rectangle in screen coordinates. |
296 * @return {Rect} Rectangle in device coordinates. | 317 * @return {Rect} Rectangle in device coordinates. |
297 */ | 318 */ |
298 Viewport.prototype.screenToDeviceRect = function(rect) { | 319 Viewport.prototype.screenToDeviceRect = function(rect) { |
299 var ratio = Viewport.getDevicePixelRatio(); | 320 var ratio = window.devicePixelRatio; |
300 var screenCenterX = Math.round( | 321 var screenCenterX = Math.round( |
301 this.screenBounds_.left + this.screenBounds_.width / 2); | 322 this.screenBounds_.left + this.screenBounds_.width / 2); |
302 var screenCenterY = Math.round( | 323 var screenCenterY = Math.round( |
303 this.screenBounds_.top + this.screenBounds_.height / 2); | 324 this.screenBounds_.top + this.screenBounds_.height / 2); |
304 return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, | 325 return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, |
305 screenCenterY + (rect.top - screenCenterY) * ratio, | 326 screenCenterY + (rect.top - screenCenterY) * ratio, |
306 rect.width * ratio, | 327 rect.width * ratio, |
307 rect.height * ratio); | 328 rect.height * ratio); |
308 }; | 329 }; |
309 | 330 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 }; | 431 }; |
411 | 432 |
412 /** | 433 /** |
413 * Repaint all clients. | 434 * Repaint all clients. |
414 */ | 435 */ |
415 Viewport.prototype.repaint = function() { | 436 Viewport.prototype.repaint = function() { |
416 this.update(); | 437 this.update(); |
417 for (var i = 0; i != this.repaintCallbacks_.length; i++) | 438 for (var i = 0; i != this.repaintCallbacks_.length; i++) |
418 this.repaintCallbacks_[i](); | 439 this.repaintCallbacks_[i](); |
419 }; | 440 }; |
| 441 |
| 442 /** |
| 443 * Obtains CSS transformation for the screen image. |
| 444 * @return {string} Transformation description. |
| 445 */ |
| 446 Viewport.prototype.getTransformation = function() { |
| 447 return 'scale(' + (1 / window.devicePixelRatio) + ')'; |
| 448 }; |
| 449 |
| 450 /** |
| 451 * Obtains shift CSS transformation for the screen image. |
| 452 * @param {number} dx Amount of shift. |
| 453 * @return {string} Transformation description. |
| 454 */ |
| 455 Viewport.prototype.getShiftTransformation = function(dx) { |
| 456 return 'translateX(' + dx + 'px) ' + this.getTransformation(); |
| 457 }; |
| 458 |
| 459 /** |
| 460 * Obtains CSS transformation that makes the rotated image fit the original |
| 461 * image. The new rotated image that the transformation is applied to looks the |
| 462 * same with original image. |
| 463 * |
| 464 * @param {boolean} orientation Orientation of the rotation from the original |
| 465 * image to the rotated image. True is for clockwise and false is for |
| 466 * counterclockwise. |
| 467 * @return {string} Transformation description. |
| 468 */ |
| 469 Viewport.prototype.getInverseTransformForRotatedImage = function(orientation) { |
| 470 var previousImageWidth = this.imageBounds_.height; |
| 471 var previousImageHeight = this.imageBounds_.width; |
| 472 var oldScale = this.getFittingScaleForImageSize_( |
| 473 previousImageWidth, previousImageHeight); |
| 474 var scaleRatio = oldScale / this.getScale(); |
| 475 var degree = orientation ? '-90deg' : '90deg'; |
| 476 return [ |
| 477 'scale(' + scaleRatio + ')', |
| 478 'rotate(' + degree + ')', |
| 479 this.getTransformation() |
| 480 ].join(' '); |
| 481 }; |
| 482 |
| 483 /** |
| 484 * Obtains CSS transformation that makes the cropped image fit the original |
| 485 * image. The new cropped image that the transformaton is applied to fits to the |
| 486 * the cropped rectangle in the original image. |
| 487 * |
| 488 * @param {number} imageWidth Width of the original image. |
| 489 * @param {number} imageHeight Height of the origianl image. |
| 490 * @param {Rect} imageCropRect Crop rectangle in the image's coordinate system. |
| 491 * @return {string} Transformation description. |
| 492 */ |
| 493 Viewport.prototype.getInverseTransformForCroppedImage = |
| 494 function(imageWidth, imageHeight, imageCropRect) { |
| 495 var wholeScale = this.getFittingScaleForImageSize_( |
| 496 imageWidth, imageHeight); |
| 497 var croppedScale = this.getFittingScaleForImageSize_( |
| 498 imageCropRect.width, imageCropRect.height); |
| 499 var dx = |
| 500 (imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) * |
| 501 wholeScale; |
| 502 var dy = |
| 503 (imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) * |
| 504 wholeScale; |
| 505 return [ |
| 506 'translate(' + dx + 'px,' + dy + 'px)', |
| 507 'scale(' + wholeScale / croppedScale + ')', |
| 508 this.getTransformation() |
| 509 ].join(' '); |
| 510 }; |
| 511 |
| 512 /** |
| 513 * Obtains CSS transformaton that makes the image fit to the screen rectangle. |
| 514 * |
| 515 * @param {Rect} screenRect Screen rectangle. |
| 516 * @return {string} Transformation description. |
| 517 */ |
| 518 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { |
| 519 var screenImageWidth = this.imageBounds_.width * this.getScale(); |
| 520 var screenImageHeight = this.imageBounds_.height * this.getScale(); |
| 521 var scaleX = screenRect.width / screenImageWidth; |
| 522 var scaleY = screenRect.height / screenImageHeight; |
| 523 var screenWidth = this.screenBounds_.width; |
| 524 var screenHeight = this.screenBounds_.height; |
| 525 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; |
| 526 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; |
| 527 return [ |
| 528 'translate(' + dx + 'px,' + dy + 'px)', |
| 529 'scale(' + scaleX + ',' + scaleY + ')', |
| 530 this.getTransformation() |
| 531 ].join(' '); |
| 532 }; |
OLD | NEW |