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 |