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 /** | 5 /** |
6 * Formats string by replacing place holder with actual values. | |
7 * @param {string} str String includes placeholder '$n'. n starts from 1. | |
8 * @param {...*} var_args Values inserted into the place holders. | |
9 * @return {string} | |
10 */ | |
11 function formatString(str, var_args) { | |
12 var args = arguments; | |
13 return str.replace(/\$[1-9]/g, function(placeHolder) { | |
14 return args[placeHolder[1]]; | |
15 }); | |
16 } | |
17 | |
18 /** | |
6 * Viewport class controls the way the image is displayed (scale, offset etc). | 19 * Viewport class controls the way the image is displayed (scale, offset etc). |
7 * @constructor | 20 * @constructor |
8 * @struct | 21 * @struct |
9 */ | 22 */ |
10 function Viewport() { | 23 function Viewport() { |
11 /** | 24 /** |
12 * Size of the full resolution image. | 25 * Size of the full resolution image. |
13 * @type {!ImageRect} | 26 * @type {!ImageRect} |
14 * @private | 27 * @private |
15 */ | 28 */ |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
185 | 198 |
186 /** | 199 /** |
187 * Obtains the rotation value. | 200 * Obtains the rotation value. |
188 * @return {number} Current rotation value. | 201 * @return {number} Current rotation value. |
189 */ | 202 */ |
190 Viewport.prototype.getRotation = function() { | 203 Viewport.prototype.getRotation = function() { |
191 return this.rotation_; | 204 return this.rotation_; |
192 }; | 205 }; |
193 | 206 |
194 /** | 207 /** |
195 * Obtains the scale for the specified image size. | 208 * Returns image scale so that it matches screen size as long as it does not |
209 * exceed maximum size. | |
196 * | 210 * |
197 * @param {number} width Width of the full resolution image. | 211 * @param {number} width Width of image. |
198 * @param {number} height Height of the full resolution image. | 212 * @param {number} height Height of image. |
213 * @param {number} maxWidth Max width of image. | |
214 * @param {number} maxHeight Max height of image. | |
199 * @return {number} The ratio of the full resotion image size and the calculated | 215 * @return {number} The ratio of the full resotion image size and the calculated |
200 * displayed image size. | 216 * displayed image size. |
201 * @private | 217 * @private |
202 */ | 218 */ |
203 Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { | 219 Viewport.prototype.getFittingScaleForImageSize_ = function( |
204 var scaleX = this.screenBounds_.width / width; | 220 width, height, maxWidth, maxHeight) { |
205 var scaleY = this.screenBounds_.height / height; | 221 return Math.min( |
206 return Math.min(scaleX, scaleY, 1); | 222 maxWidth / width, |
223 maxHeight / height, | |
224 this.screenBounds_.width / width, | |
225 this.screenBounds_.height / height); | |
207 }; | 226 }; |
208 | 227 |
209 /** | 228 /** |
210 * Returns offset X. | 229 * Returns offset X. |
211 * @return {number} X-offset of the viewport. | 230 * @return {number} X-offset of the viewport. |
212 */ | 231 */ |
213 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; | 232 Viewport.prototype.getOffsetX = function() { return this.offsetX_; }; |
214 | 233 |
215 /** | 234 /** |
216 * Returns offset Y. | 235 * Returns offset Y. |
(...skipping 24 matching lines...) Expand all Loading... | |
241 * Returns screen bounds. | 260 * Returns screen bounds. |
242 * @return {!ImageRect} The screen bounds in screen coordinates. | 261 * @return {!ImageRect} The screen bounds in screen coordinates. |
243 */ | 262 */ |
244 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; | 263 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; |
245 | 264 |
246 /** | 265 /** |
247 * Returns device bounds. | 266 * Returns device bounds. |
248 * @return {!ImageRect} The size of screen cache canvas. | 267 * @return {!ImageRect} The size of screen cache canvas. |
249 */ | 268 */ |
250 Viewport.prototype.getDeviceBounds = function() { | 269 Viewport.prototype.getDeviceBounds = function() { |
251 var size = this.getImageElementBoundsOnScreen(); | |
252 return ImageRect.createFromWidthAndHeight( | 270 return ImageRect.createFromWidthAndHeight( |
253 size.width * window.devicePixelRatio, | 271 this.imageElementBoundsOnScreen_.width * window.devicePixelRatio, |
254 size.height * window.devicePixelRatio); | 272 this.imageElementBoundsOnScreen_.height * window.devicePixelRatio); |
255 }; | 273 }; |
256 | 274 |
257 /** | 275 /** |
258 * A counter that is incremented with each viewport state change. | 276 * A counter that is incremented with each viewport state change. |
259 * Clients that cache anything that depends on the viewport state should keep | 277 * Clients that cache anything that depends on the viewport state should keep |
260 * track of this counter. | 278 * track of this counter. |
261 * @return {number} counter. | 279 * @return {number} counter. |
262 */ | 280 */ |
263 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; | 281 Viewport.prototype.getCacheGeneration = function() { return this.generation_; }; |
264 | 282 |
265 /** | 283 /** |
266 * Returns image bounds in screen coordinates. | 284 * Returns image bounds in screen coordinates. |
267 * @return {!ImageRect} The image bounds in screen coordinates. | 285 * @return {!ImageRect} The image bounds in screen coordinates. |
268 */ | 286 */ |
269 Viewport.prototype.getImageBoundsOnScreen = function() { | 287 Viewport.prototype.getImageBoundsOnScreen = function() { |
270 assert(this.imageBoundsOnScreen_); | 288 assert(this.imageBoundsOnScreen_); |
271 return this.imageBoundsOnScreen_; | 289 return this.imageBoundsOnScreen_; |
272 }; | 290 }; |
273 | 291 |
274 /** | 292 /** |
275 * The image bounds in screen coordinates. | |
276 * This returns the bounds of element before applying zoom and offset. | |
277 * @return {!ImageRect} | |
278 */ | |
279 Viewport.prototype.getImageElementBoundsOnScreen = function() { | |
280 assert(this.imageElementBoundsOnScreen_); | |
281 return this.imageElementBoundsOnScreen_; | |
282 }; | |
283 | |
284 /** | |
285 * The image bounds on screen, which is clipped with the screen size. | 293 * The image bounds on screen, which is clipped with the screen size. |
286 * @return {!ImageRect} | 294 * @return {!ImageRect} |
287 */ | 295 */ |
288 Viewport.prototype.getImageBoundsOnScreenClipped = function() { | 296 Viewport.prototype.getImageBoundsOnScreenClipped = function() { |
289 assert(this.imageBoundsOnScreenClipped_); | 297 assert(this.imageBoundsOnScreenClipped_); |
290 return this.imageBoundsOnScreenClipped_; | 298 return this.imageBoundsOnScreenClipped_; |
291 }; | 299 }; |
292 | 300 |
293 /** | 301 /** |
294 * Returns size in image coordinates. | 302 * Returns size in image coordinates. |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
399 this.update_(); | 407 this.update_(); |
400 }; | 408 }; |
401 | 409 |
402 /** | 410 /** |
403 * Recalculate the viewport parameters. | 411 * Recalculate the viewport parameters. |
404 * @private | 412 * @private |
405 */ | 413 */ |
406 Viewport.prototype.update_ = function() { | 414 Viewport.prototype.update_ = function() { |
407 // Update scale. | 415 // Update scale. |
408 this.scale_ = this.getFittingScaleForImageSize_( | 416 this.scale_ = this.getFittingScaleForImageSize_( |
417 this.imageBounds_.width, this.imageBounds_.height, | |
409 this.imageBounds_.width, this.imageBounds_.height); | 418 this.imageBounds_.width, this.imageBounds_.height); |
410 | 419 |
411 // Limit offset values. | 420 // Limit offset values. |
412 var zoomedWidht; | 421 var zoomedWidht; |
413 var zoomedHeight; | 422 var zoomedHeight; |
414 if (this.rotation_ % 2 == 0) { | 423 if (this.rotation_ % 2 == 0) { |
415 zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_); | 424 zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_); |
416 zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_); | 425 zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_); |
417 } else { | 426 } else { |
418 var scale = this.getFittingScaleForImageSize_( | 427 var scale = this.getFittingScaleForImageSize_( |
428 this.imageBounds_.height, this.imageBounds_.width, | |
419 this.imageBounds_.height, this.imageBounds_.width); | 429 this.imageBounds_.height, this.imageBounds_.width); |
420 zoomedWidht = ~~(this.imageBounds_.height * scale * this.zoom_); | 430 zoomedWidht = ~~(this.imageBounds_.height * scale * this.zoom_); |
421 zoomedHeight = ~~(this.imageBounds_.width * scale * this.zoom_); | 431 zoomedHeight = ~~(this.imageBounds_.width * scale * this.zoom_); |
422 } | 432 } |
423 var dx = Math.max(zoomedWidht - this.screenBounds_.width, 0) / 2; | 433 var dx = Math.max(zoomedWidht - this.screenBounds_.width, 0) / 2; |
424 var dy = Math.max(zoomedHeight - this.screenBounds_.height, 0) / 2; | 434 var dy = Math.max(zoomedHeight - this.screenBounds_.height, 0) / 2; |
425 this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx); | 435 this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx); |
426 this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy); | 436 this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy); |
427 | 437 |
428 // Image bounds on screen. | 438 // Image bounds on screen. |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
465 viewport.zoom_ = this.zoom_; | 475 viewport.zoom_ = this.zoom_; |
466 viewport.offsetX_ = this.offsetX_; | 476 viewport.offsetX_ = this.offsetX_; |
467 viewport.offsetY_ = this.offsetY_; | 477 viewport.offsetY_ = this.offsetY_; |
468 viewport.rotation_ = this.rotation_; | 478 viewport.rotation_ = this.rotation_; |
469 viewport.generation_ = this.generation_; | 479 viewport.generation_ = this.generation_; |
470 viewport.update_(); | 480 viewport.update_(); |
471 return viewport; | 481 return viewport; |
472 }; | 482 }; |
473 | 483 |
474 /** | 484 /** |
485 * Obtains CSS transformation string that matches the image dimension with | |
486 * |screenRect|. | |
487 * @param {number} width Width of image. | |
488 * @param {number} height Height of image. | |
489 * @param {!ImageRect} screenRect Rectangle in window coordinate system. The | |
490 * origin of the coordinate system at the left upper of the window. | |
491 */ | |
492 Viewport.prototype.getScreenRectTransformation = function( | |
493 width, height, screenRect) { | |
494 var dx = screenRect.left + (screenRect.width - width) / 2; | |
495 var dy = screenRect.top + (screenRect.height - height) / 2; | |
496 | |
497 return formatString( | |
498 'translate($1px,$2px) scale($3,$4)', | |
499 dx, dy, screenRect.width / width, screenRect.height / height); | |
500 }; | |
501 | |
502 /** | |
503 * Obtains CSS transformation string that places the cropped image at the | |
504 * original position in the whole image. | |
505 * @param {number} width Width of cropped image. | |
506 * @param {number} height Width of cropped image. | |
507 * @param {number} wholeWidthMax Max width value that is used for layouting | |
508 * whole image. | |
509 * @param {number} wholeHeightMax Max height value that is used for layouting | |
510 * whole image. | |
511 * @param {!ImageRect} cropRect Crop rectangle in the whole image. The origin is | |
512 * left upper of the whole image. | |
513 */ | |
514 Viewport.prototype.getCroppingTransformation = function( | |
515 width, | |
516 height, | |
517 wholeWidthMax, | |
518 wholeHeightMax, | |
519 cropRect) { | |
520 var fittingScale = this.getFittingScaleForImageSize_( | |
521 wholeWidthMax, wholeHeightMax, wholeWidthMax, wholeHeightMax); | |
522 var wholeWidth = wholeWidthMax * fittingScale; | |
523 var wholeHeight = wholeHeightMax * fittingScale; | |
524 var wholeLeft = (this.screenBounds_.width - wholeWidth) / 2; | |
525 var wholeTop = (this.screenBounds_.height - wholeHeight) / 2; | |
526 return this.getScreenRectTransformation( | |
527 width, | |
528 height, | |
529 new ImageRect( | |
530 wholeLeft + cropRect.left * fittingScale, | |
531 wholeTop + cropRect.top * fittingScale, | |
532 cropRect.width * fittingScale, | |
533 cropRect.height * fittingScale)); | |
534 }; | |
535 | |
536 /** | |
475 * Obtains CSS transformation for the screen image. | 537 * Obtains CSS transformation for the screen image. |
538 * @param {number} width Width of image. | |
539 * @param {number} height Height of image. | |
540 * @param {number=} opt_dx Amount of horizontal shift. | |
476 * @return {string} Transformation description. | 541 * @return {string} Transformation description. |
477 */ | 542 */ |
478 Viewport.prototype.getTransformation = function() { | 543 Viewport.prototype.getTransformation = function(width, height, opt_dx) { |
479 var rotationScaleAdjustment; | 544 return this.getTransformationInternal_( |
480 if (this.rotation_ % 2) { | 545 width, |
481 rotationScaleAdjustment = this.getFittingScaleForImageSize_( | 546 height, |
482 this.imageBounds_.height, this.imageBounds_.width) / this.scale_; | 547 this.rotation_, |
483 } else { | 548 this.zoom_, |
484 rotationScaleAdjustment = 1; | 549 this.offsetX_ + (opt_dx || 0), |
485 } | 550 this.offsetY_); |
486 return [ | |
487 'translate(' + this.offsetX_ + 'px, ' + this.offsetY_ + 'px) ', | |
488 'rotate(' + (this.rotation_ * 90) + 'deg)', | |
489 'scale(' + (this.zoom_ * rotationScaleAdjustment) + ')' | |
490 ].join(' '); | |
491 }; | 551 }; |
492 | 552 |
493 /** | 553 /** |
494 * Obtains shift CSS transformation for the screen image. | |
495 * @param {number} dx Amount of shift. | |
496 * @return {string} Transformation description. | |
497 */ | |
498 Viewport.prototype.getShiftTransformation = function(dx) { | |
499 return 'translateX(' + dx + 'px) ' + this.getTransformation(); | |
500 }; | |
501 | |
502 /** | |
503 * Obtains CSS transformation that makes the rotated image fit the original | 554 * Obtains CSS transformation that makes the rotated image fit the original |
504 * image. The new rotated image that the transformation is applied to looks the | 555 * image. The new rotated image that the transformation is applied to looks the |
505 * same with original image. | 556 * same with original image. |
506 * | 557 * |
507 * @param {boolean} orientation Orientation of the rotation from the original | 558 * @param {number} width Width of image. |
508 * image to the rotated image. True is for clockwise and false is for | 559 * @param {number} height Height of image. |
509 * counterclockwise. | 560 * @param {number} rotation Number of clockwise 90 degree rotation. The rotation |
561 * angle of the image is rotation * 90. | |
510 * @return {string} Transformation description. | 562 * @return {string} Transformation description. |
511 */ | 563 */ |
512 Viewport.prototype.getInverseTransformForRotatedImage = function(orientation) { | 564 Viewport.prototype.getRotatingTransformation = function( |
513 var previousImageWidth = this.imageBounds_.height; | 565 width, height, rotation) { |
514 var previousImageHeight = this.imageBounds_.width; | 566 return this.getTransformationInternal_( |
515 var oldScale = this.getFittingScaleForImageSize_( | 567 width, height, rotation, 1, 0, 0); |
516 previousImageWidth, previousImageHeight); | |
517 var scaleRatio = oldScale / this.scale_; | |
518 var degree = orientation ? '-90deg' : '90deg'; | |
519 return [ | |
520 'scale(' + scaleRatio + ')', | |
521 'rotate(' + degree + ')', | |
522 this.getTransformation() | |
523 ].join(' '); | |
524 }; | 568 }; |
525 | 569 |
526 /** | 570 /** |
527 * Obtains CSS transformation that makes the cropped image fit the original | 571 * Obtains CSS transformation that placed the image in the application window. |
528 * image. The new cropped image that the transformation is applied to fits to | 572 * @param {number} width Width of image. |
529 * the cropped rectangle in the original image. | 573 * @param {number} height Height of image. |
530 * | 574 * @param {number} rotation Number of clockwise 90 degree rotation. The rotation |
531 * @param {number} imageWidth Width of the original image. | 575 * angle of the image is rotation * 90. |
532 * @param {number} imageHeight Height of the original image. | 576 * @param {number} zoom Zoom rate. |
533 * @param {!ImageRect} imageCropRect Crop rectangle in the image's coordinate | 577 * @param {number} offsetX Horizontal offset. |
534 * system. | 578 * @param {number} offsetY Vertical offset. |
535 * @return {string} Transformation description. | 579 * @private |
536 */ | 580 */ |
537 Viewport.prototype.getInverseTransformForCroppedImage = | 581 Viewport.prototype.getTransformationInternal_ = function( |
538 function(imageWidth, imageHeight, imageCropRect) { | 582 width, |
539 var wholeScale = this.getFittingScaleForImageSize_( | 583 height, |
540 imageWidth, imageHeight); | 584 rotation, |
541 var croppedScale = this.getFittingScaleForImageSize_( | 585 zoom, |
542 imageCropRect.width, imageCropRect.height); | 586 offsetX, |
543 var dx = | 587 offsetY) { |
544 (imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) * | 588 var rotatedWidth = rotation % 2 ? height : width; |
545 wholeScale; | 589 var rotatedHeight = rotation % 2 ? width : height; |
546 var dy = | 590 var rotatedMaxWidth = rotation % 2 ? |
547 (imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) * | 591 this.imageBounds_.height : this.imageBounds_.width; |
548 wholeScale; | 592 var rotatedMaxHeight = rotation % 2 ? |
549 return [ | 593 this.imageBounds_.width : this.imageBounds_.height; |
550 'translate(' + dx + 'px,' + dy + 'px)', | 594 |
551 'scale(' + wholeScale / croppedScale + ')', | 595 // Scale. |
552 this.getTransformation() | 596 var fittingScale = this.getFittingScaleForImageSize_( |
553 ].join(' '); | 597 rotatedWidth, rotatedHeight, rotatedMaxWidth, rotatedMaxHeight); |
598 | |
599 // Offset for centering. | |
600 var dx = (this.screenBounds_.width - width) / 2; | |
601 var dy = (this.screenBounds_.height - height) / 2; | |
602 | |
603 var d = formatString( | |
mtomasz
2015/05/14 02:27:55
nit: Per our style guide abbreviations are discour
hirono
2015/05/14 03:08:03
Yes, I removed console.log, and let it return the
| |
604 'translate($1px,$2px) scale($3) rotate($4deg)', | |
605 dx + offsetX, | |
606 dy + offsetY, | |
607 fittingScale * zoom, | |
608 rotation * 90); | |
609 console.log(d); | |
mtomasz
2015/05/14 02:27:55
nit: Remove console.log?
hirono
2015/05/14 03:08:03
Done.
| |
610 return d; | |
554 }; | 611 }; |
555 | |
556 /** | |
557 * Obtains CSS transformation that makes the image fit to the screen rectangle. | |
558 * | |
559 * @param {!ImageRect} screenRect Screen rectangle. | |
560 * @return {string} Transformation description. | |
561 */ | |
562 Viewport.prototype.getScreenRectTransformForImage = function(screenRect) { | |
563 var imageBounds = this.getImageElementBoundsOnScreen(); | |
564 var scaleX = screenRect.width / imageBounds.width; | |
565 var scaleY = screenRect.height / imageBounds.height; | |
566 var screenWidth = this.screenBounds_.width; | |
567 var screenHeight = this.screenBounds_.height; | |
568 var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; | |
569 var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2; | |
570 return [ | |
571 'translate(' + dx + 'px,' + dy + 'px)', | |
572 'scale(' + scaleX + ',' + scaleY + ')', | |
573 this.getTransformation() | |
574 ].join(' '); | |
575 }; | |
OLD | NEW |