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 */ |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 */ | 65 */ |
66 this.offsetX_ = 0; | 66 this.offsetX_ = 0; |
67 | 67 |
68 /** | 68 /** |
69 * Offset specified by user operations. | 69 * Offset specified by user operations. |
70 * @type {number} | 70 * @type {number} |
71 */ | 71 */ |
72 this.offsetY_ = 0; | 72 this.offsetY_ = 0; |
73 | 73 |
74 /** | 74 /** |
| 75 * Integer Rotation value. |
| 76 * The rotation angle is this.rotation_ * 90. |
| 77 * @type {number} |
| 78 */ |
| 79 this.rotation_ = 0; |
| 80 |
| 81 /** |
75 * Generation of the screen size image cache. | 82 * Generation of the screen size image cache. |
76 * This is incremented every time when the size of image cache is changed. | 83 * This is incremented every time when the size of image cache is changed. |
77 * @type {number} | 84 * @type {number} |
78 * @private | 85 * @private |
79 */ | 86 */ |
80 this.generation_ = 0; | 87 this.generation_ = 0; |
81 | 88 |
82 this.update_(); | 89 this.update_(); |
83 Object.seal(this); | 90 Object.seal(this); |
84 } | 91 } |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 | 164 |
158 /** | 165 /** |
159 * Obtains whether the picture is zoomed or not. | 166 * Obtains whether the picture is zoomed or not. |
160 * @return {boolean} | 167 * @return {boolean} |
161 */ | 168 */ |
162 Viewport.prototype.isZoomed = function() { | 169 Viewport.prototype.isZoomed = function() { |
163 return this.zoom_ !== 1; | 170 return this.zoom_ !== 1; |
164 }; | 171 }; |
165 | 172 |
166 /** | 173 /** |
| 174 * Sets the rotation value. |
| 175 * @param {number} rotation New rotation value. |
| 176 */ |
| 177 Viewport.prototype.setRotation = function(rotation) { |
| 178 this.rotation_ = rotation; |
| 179 this.update_(); |
| 180 }; |
| 181 |
| 182 |
| 183 /** |
| 184 * Obtains the rotation value. |
| 185 * @return {number} Current rotation value. |
| 186 */ |
| 187 Viewport.prototype.getRotation = function() { |
| 188 return this.rotation_; |
| 189 }; |
| 190 |
| 191 /** |
167 * Obtains the scale for the specified image size. | 192 * Obtains the scale for the specified image size. |
168 * | 193 * |
169 * @param {number} width Width of the full resolution image. | 194 * @param {number} width Width of the full resolution image. |
170 * @param {number} height Height of the full resolution image. | 195 * @param {number} height Height of the full resolution image. |
171 * @return {number} The ratio of the fullresotion image size and the calculated | 196 * @return {number} The ratio of the fullresotion image size and the calculated |
172 * displayed image size. | 197 * displayed image size. |
173 */ | 198 */ |
174 Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { | 199 Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { |
175 var scaleX = this.screenBounds_.width / width; | 200 var scaleX = this.screenBounds_.width / width; |
176 var scaleY = this.screenBounds_.height / height; | 201 var scaleY = this.screenBounds_.height / height; |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
320 */ | 345 */ |
321 Viewport.prototype.imageToScreenRect = function(rect) { | 346 Viewport.prototype.imageToScreenRect = function(rect) { |
322 return new Rect( | 347 return new Rect( |
323 this.imageToScreenX(rect.left), | 348 this.imageToScreenX(rect.left), |
324 this.imageToScreenY(rect.top), | 349 this.imageToScreenY(rect.top), |
325 Math.round(this.imageToScreenSize(rect.width)), | 350 Math.round(this.imageToScreenSize(rect.width)), |
326 Math.round(this.imageToScreenSize(rect.height))); | 351 Math.round(this.imageToScreenSize(rect.height))); |
327 }; | 352 }; |
328 | 353 |
329 /** | 354 /** |
330 * @return {boolean} True if some part of the image is clipped by the screen. | |
331 */ | |
332 Viewport.prototype.isClipped = function() { | |
333 return this.getMarginX_() < 0 || this.getMarginY_() < 0; | |
334 }; | |
335 | |
336 /** | |
337 * @return {number} Horizontal margin. | |
338 * Negative if the image is clipped horizontally. | |
339 * @private | |
340 */ | |
341 Viewport.prototype.getMarginX_ = function() { | |
342 return Math.round( | |
343 (this.screenBounds_.width - this.imageBounds_.width * this.scale_) / 2); | |
344 }; | |
345 | |
346 /** | |
347 * @return {number} Vertical margin. | |
348 * Negative if the image is clipped vertically. | |
349 * @private | |
350 */ | |
351 Viewport.prototype.getMarginY_ = function() { | |
352 return Math.round( | |
353 (this.screenBounds_.height - this.imageBounds_.height * this.scale_) / 2); | |
354 }; | |
355 | |
356 /** | |
357 * @param {number} x X-offset. | |
358 * @return {number} X-offset clamped to the valid range. | |
359 * @private | |
360 */ | |
361 Viewport.prototype.clampOffsetX_ = function(x) { | |
362 var limit = Math.round(Math.max(0, -this.getMarginX_() / this.scale_)); | |
363 return ImageUtil.clamp(-limit, x, limit); | |
364 }; | |
365 | |
366 /** | |
367 * @param {number} y Y-offset. | |
368 * @return {number} Y-offset clamped to the valid range. | |
369 * @private | |
370 */ | |
371 Viewport.prototype.clampOffsetY_ = function(y) { | |
372 var limit = Math.round(Math.max(0, -this.getMarginY_() / this.scale_)); | |
373 return ImageUtil.clamp(-limit, y, limit); | |
374 }; | |
375 | |
376 /** | |
377 * @private | 355 * @private |
378 */ | 356 */ |
379 Viewport.prototype.getCenteredRect_ = function( | 357 Viewport.prototype.getCenteredRect_ = function( |
380 width, height, offsetX, offsetY) { | 358 width, height, offsetX, offsetY) { |
381 return new Rect( | 359 return new Rect( |
382 ~~((this.screenBounds_.width - width) / 2) + offsetX, | 360 ~~((this.screenBounds_.width - width) / 2) + offsetX, |
383 ~~((this.screenBounds_.height - height) / 2) + offsetY, | 361 ~~((this.screenBounds_.height - height) / 2) + offsetY, |
384 width, | 362 width, |
385 height); | 363 height); |
386 }; | 364 }; |
387 | 365 |
388 /** | 366 /** |
389 * Resets zoom and offset. | 367 * Resets zoom and offset. |
390 */ | 368 */ |
391 Viewport.prototype.resetView = function() { | 369 Viewport.prototype.resetView = function() { |
392 this.zoom_ = 1; | 370 this.zoom_ = 1; |
393 this.offsetX_ = 0; | 371 this.offsetX_ = 0; |
394 this.offsetY_ = 0; | 372 this.offsetY_ = 0; |
| 373 this.rotation_ = 0; |
395 this.update_(); | 374 this.update_(); |
396 }; | 375 }; |
397 | 376 |
398 /** | 377 /** |
399 * Recalculate the viewport parameters. | 378 * Recalculate the viewport parameters. |
400 * @private | 379 * @private |
401 */ | 380 */ |
402 Viewport.prototype.update_ = function() { | 381 Viewport.prototype.update_ = function() { |
403 // Update scale. | 382 // Update scale. |
404 this.scale_ = this.getFittingScaleForImageSize_( | 383 this.scale_ = this.getFittingScaleForImageSize_( |
405 this.imageBounds_.width, this.imageBounds_.height); | 384 this.imageBounds_.width, this.imageBounds_.height); |
406 | 385 |
407 // Limit offset values. | 386 // Limit offset values. |
408 var zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_); | 387 var zoomedWidht; |
409 var zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_); | 388 var zoomedHeight; |
| 389 if (this.rotation_ % 2 == 0) { |
| 390 zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_); |
| 391 zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_); |
| 392 } else { |
| 393 var scale = this.getFittingScaleForImageSize_( |
| 394 this.imageBounds_.height, this.imageBounds_.width); |
| 395 zoomedWidht = ~~(this.imageBounds_.height * scale * this.zoom_); |
| 396 zoomedHeight = ~~(this.imageBounds_.width * scale * this.zoom_); |
| 397 } |
410 var dx = Math.max(zoomedWidht - this.screenBounds_.width, 0) / 2; | 398 var dx = Math.max(zoomedWidht - this.screenBounds_.width, 0) / 2; |
411 var dy = Math.max(zoomedHeight - this.screenBounds_.height, 0) /2; | 399 var dy = Math.max(zoomedHeight - this.screenBounds_.height, 0) /2; |
412 this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx); | 400 this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx); |
413 this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy); | 401 this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy); |
414 | 402 |
415 // Image bounds on screen. | 403 // Image bounds on screen. |
416 this.imageBoundsOnScreen_ = this.getCenteredRect_( | 404 this.imageBoundsOnScreen_ = this.getCenteredRect_( |
417 zoomedWidht, zoomedHeight, this.offsetX_, this.offsetY_); | 405 zoomedWidht, zoomedHeight, this.offsetX_, this.offsetY_); |
418 | 406 |
419 // Image bounds of element (that is not applied zoom and offset) on screen. | 407 // Image bounds of element (that is not applied zoom and offset) on screen. |
(...skipping 14 matching lines...) Expand all Loading... |
434 var top = Math.max(this.imageBoundsOnScreen_.top, 0); | 422 var top = Math.max(this.imageBoundsOnScreen_.top, 0); |
435 var right = Math.min( | 423 var right = Math.min( |
436 this.imageBoundsOnScreen_.right, this.screenBounds_.width); | 424 this.imageBoundsOnScreen_.right, this.screenBounds_.width); |
437 var bottom = Math.min( | 425 var bottom = Math.min( |
438 this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); | 426 this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); |
439 this.imageBoundsOnScreenClipped_ = new Rect( | 427 this.imageBoundsOnScreenClipped_ = new Rect( |
440 left, top, right - left, bottom - top); | 428 left, top, right - left, bottom - top); |
441 }; | 429 }; |
442 | 430 |
443 /** | 431 /** |
| 432 * Clones the viewport. |
| 433 * @return {Viewport} New instance. |
| 434 */ |
| 435 Viewport.prototype.clone = function() { |
| 436 var viewport = new Viewport(); |
| 437 viewport.imageBounds_ = new Rect(this.imageBounds_); |
| 438 viewport.screenBounds_ = new Rect(this.screenBounds_); |
| 439 viewport.scale_ = this.scale_; |
| 440 viewport.zoom_ = this.zoom_; |
| 441 viewport.offsetX_ = this.offsetX_; |
| 442 viewport.offsetY_ = this.offsetY_; |
| 443 viewport.rotation_ = this.rotation_; |
| 444 viewport.generation_ = this.generation_; |
| 445 viewport.update_(); |
| 446 return viewport; |
| 447 }; |
| 448 |
| 449 /** |
444 * Obtains CSS transformation for the screen image. | 450 * Obtains CSS transformation for the screen image. |
445 * @return {string} Transformation description. | 451 * @return {string} Transformation description. |
446 */ | 452 */ |
447 Viewport.prototype.getTransformation = function() { | 453 Viewport.prototype.getTransformation = function() { |
448 return 'translate(' + this.offsetX_ + 'px, ' + this.offsetY_ + 'px) ' + | 454 var rotationScaleAdjustment; |
449 'scale(' + this.zoom_ + ')'; | 455 if (this.rotation_ % 2) { |
| 456 rotationScaleAdjustment = this.getFittingScaleForImageSize_( |
| 457 this.imageBounds_.height, this.imageBounds_.width) / this.scale_; |
| 458 } else { |
| 459 rotationScaleAdjustment = 1; |
| 460 } |
| 461 return [ |
| 462 'translate(' + this.offsetX_ + 'px, ' + this.offsetY_ + 'px) ', |
| 463 'rotate(' + (this.rotation_ * 90) + 'deg)', |
| 464 'scale(' + (this.zoom_ * rotationScaleAdjustment) + ')' |
| 465 ].join(' '); |
450 }; | 466 }; |
451 | 467 |
452 /** | 468 /** |
453 * Obtains shift CSS transformation for the screen image. | 469 * Obtains shift CSS transformation for the screen image. |
454 * @param {number} dx Amount of shift. | 470 * @param {number} dx Amount of shift. |
455 * @return {string} Transformation description. | 471 * @return {string} Transformation description. |
456 */ | 472 */ |
457 Viewport.prototype.getShiftTransformation = function(dx) { | 473 Viewport.prototype.getShiftTransformation = function(dx) { |
458 return 'translateX(' + dx + 'px) ' + this.getTransformation(); | 474 return 'translateX(' + dx + 'px) ' + this.getTransformation(); |
459 }; | 475 }; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
524 var screenWidth = this.screenBounds_.width; | 540 var screenWidth = this.screenBounds_.width; |
525 var screenHeight = this.screenBounds_.height; | 541 var screenHeight = this.screenBounds_.height; |
526 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; | 542 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; |
527 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; | 543 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; |
528 return [ | 544 return [ |
529 'translate(' + dx + 'px,' + dy + 'px)', | 545 'translate(' + dx + 'px,' + dy + 'px)', |
530 'scale(' + scaleX + ',' + scaleY + ')', | 546 'scale(' + scaleX + ',' + scaleY + ')', |
531 this.getTransformation() | 547 this.getTransformation() |
532 ].join(' '); | 548 ].join(' '); |
533 }; | 549 }; |
OLD | NEW |