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 /** | 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 |