Commit e7ff2bca authored by hirono@chromium.org's avatar hirono@chromium.org

Gallery.app: Move geometry calculation for the slide image to Viewport class.

This is a preparation for implementing the zoom feature in Gallery.app.

BUG=245926
TEST=manually

Review URL: https://codereview.chromium.org/384573002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282553 0039d316-1c4b-4281-b951-d872f2087c98
parent 9c30bc44
...@@ -626,9 +626,8 @@ ImageView.prototype.setTransform = function(element, opt_effect, opt_duration) { ...@@ -626,9 +626,8 @@ ImageView.prototype.setTransform = function(element, opt_effect, opt_duration) {
* @return {ImageView.Effect.Zoom} Zoom effect object. * @return {ImageView.Effect.Zoom} Zoom effect object.
*/ */
ImageView.prototype.createZoomEffect = function(screenRect) { ImageView.prototype.createZoomEffect = function(screenRect) {
return new ImageView.Effect.Zoom( return new ImageView.Effect.ZoomToScreen(
this.viewport_.screenToDeviceRect(screenRect), screenRect,
null /* use viewport */,
ImageView.MODE_TRANSITION_DURATION); ImageView.MODE_TRANSITION_DURATION);
}; };
...@@ -644,21 +643,17 @@ ImageView.prototype.createZoomEffect = function(screenRect) { ...@@ -644,21 +643,17 @@ ImageView.prototype.createZoomEffect = function(screenRect) {
*/ */
ImageView.prototype.replaceAndAnimate = function( ImageView.prototype.replaceAndAnimate = function(
canvas, imageCropRect, rotate90) { canvas, imageCropRect, rotate90) {
var oldScale = this.viewport_.getScale(); var oldImageBounds = {
var deviceCropRect = imageCropRect && this.viewport_.screenToDeviceRect( width: this.viewport_.getImageBounds().width,
this.viewport_.imageToScreenRect(imageCropRect)); height: this.viewport_.getImageBounds().height
};
var oldScreenImage = this.screenImage_; var oldScreenImage = this.screenImage_;
this.replaceContent_(canvas); this.replaceContent_(canvas);
var newScreenImage = this.screenImage_; var newScreenImage = this.screenImage_;
// Display the new canvas, initially transformed.
var deviceFullRect = this.viewport_.getDeviceClipped();
var effect = rotate90 ? var effect = rotate90 ?
new ImageView.Effect.Rotate( new ImageView.Effect.Rotate(rotate90 > 0) :
oldScale / this.viewport_.getScale(), -rotate90) : new ImageView.Effect.Zoom(
new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect); oldImageBounds.width, oldImageBounds.height, imageCropRect);
this.setTransform(newScreenImage, effect, 0 /* instant */); this.setTransform(newScreenImage, effect, 0 /* instant */);
...@@ -683,26 +678,20 @@ ImageView.prototype.replaceAndAnimate = function( ...@@ -683,26 +678,20 @@ ImageView.prototype.replaceAndAnimate = function(
* @return {number} Animation duration. * @return {number} Animation duration.
*/ */
ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) {
var deviceFullRect = this.viewport_.getDeviceClipped();
var oldScale = this.viewport_.getScale();
var oldScreenImage = this.screenImage_; var oldScreenImage = this.screenImage_;
this.replaceContent_(canvas); this.replaceContent_(canvas);
var newScreenImage = this.screenImage_; var newScreenImage = this.screenImage_;
var deviceCropRect = this.viewport_.screenToDeviceRect(
this.viewport_.imageToScreenRect(imageCropRect));
var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade');
setFade(true); setFade(true);
oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage);
var effect = new ImageView.Effect.Zoom(
this.viewport_.getImageBounds().width,
this.viewport_.getImageBounds().height,
imageCropRect);
var effect = new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect);
// Animate to the transformed state. // Animate to the transformed state.
this.setTransform(oldScreenImage, effect); this.setTransform(oldScreenImage, effect);
setTimeout(setFade.bind(null, false), 0); setTimeout(setFade.bind(null, false), 0);
setTimeout(function() { setTimeout(function() {
if (oldScreenImage.parentNode) if (oldScreenImage.parentNode)
oldScreenImage.parentNode.removeChild(oldScreenImage); oldScreenImage.parentNode.removeChild(oldScreenImage);
...@@ -711,7 +700,6 @@ ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { ...@@ -711,7 +700,6 @@ ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) {
return effect.getSafeInterval(); return effect.getSafeInterval();
}; };
/** /**
* Generic cache with a limited capacity and LRU eviction. * Generic cache with a limited capacity and LRU eviction.
* @param {number} capacity Maximum number of cached item. * @param {number} capacity Maximum number of cached item.
...@@ -852,15 +840,13 @@ ImageView.Effect.prototype.getSafeInterval = function() { ...@@ -852,15 +840,13 @@ ImageView.Effect.prototype.getSafeInterval = function() {
ImageView.Effect.prototype.getTiming = function() { return this.timing_; }; ImageView.Effect.prototype.getTiming = function() { return this.timing_; };
/** /**
* @param {HTMLCanvasElement} element Element. * Obtains the CSS transformation string of the effect.
* @return {number} Preferred pixel ration to use with this element. * @param {DOMCanvas} element Canvas element to be applied the transforamtion.
* @private * @param {Viewport} viewport Current viewport.
* @return CSS transformation description.
*/ */
ImageView.Effect.getPixelRatio_ = function(element) { ImageView.Effect.prototype.transform = function(element, viewport) {
if (element.constructor.name === 'HTMLCanvasElement') throw new Error('Not implemented.');
return Viewport.getDevicePixelRatio();
else
return 1;
}; };
/** /**
...@@ -868,6 +854,7 @@ ImageView.Effect.getPixelRatio_ = function(element) { ...@@ -868,6 +854,7 @@ ImageView.Effect.getPixelRatio_ = function(element) {
* for devicePixelRatio. * for devicePixelRatio.
* *
* @constructor * @constructor
* @extends {ImageView.Effect}
*/ */
ImageView.Effect.None = function() { ImageView.Effect.None = function() {
ImageView.Effect.call(this, 0); ImageView.Effect.call(this, 0);
...@@ -880,11 +867,11 @@ ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; ...@@ -880,11 +867,11 @@ ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype };
/** /**
* @param {HTMLCanvasElement} element Element. * @param {HTMLCanvasElement} element Element.
* @param {Viewport} viewport Current viewport.
* @return {string} Transform string. * @return {string} Transform string.
*/ */
ImageView.Effect.None.prototype.transform = function(element) { ImageView.Effect.None.prototype.transform = function(element, viewport) {
var ratio = ImageView.Effect.getPixelRatio_(element); return viewport.getTransformation();
return 'scale(' + (1 / ratio) + ')';
}; };
/** /**
...@@ -893,6 +880,7 @@ ImageView.Effect.None.prototype.transform = function(element) { ...@@ -893,6 +880,7 @@ ImageView.Effect.None.prototype.transform = function(element) {
* @param {number} direction -1 for left, 1 for right. * @param {number} direction -1 for left, 1 for right.
* @param {boolean=} opt_slow True if slow (as in slideshow). * @param {boolean=} opt_slow True if slow (as in slideshow).
* @constructor * @constructor
* @extends {ImageView.Effect}
*/ */
ImageView.Effect.Slide = function Slide(direction, opt_slow) { ImageView.Effect.Slide = function Slide(direction, opt_slow) {
ImageView.Effect.call(this, ImageView.Effect.call(this,
...@@ -903,25 +891,21 @@ ImageView.Effect.Slide = function Slide(direction, opt_slow) { ...@@ -903,25 +891,21 @@ ImageView.Effect.Slide = function Slide(direction, opt_slow) {
if (this.direction_ < 0) this.shift_ = -this.shift_; if (this.direction_ < 0) this.shift_ = -this.shift_;
}; };
/**
* Inherits from ImageView.Effect.
*/
ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype };
/** /**
* @return {ImageView.Effect.Slide} Reverse Slide effect. * Reverses the slide effect.
* @return {ImageView.Effect.Slide} Reversed effect.
*/ */
ImageView.Effect.Slide.prototype.getReverse = function() { ImageView.Effect.Slide.prototype.getReverse = function() {
return new ImageView.Effect.Slide(-this.direction_, this.slow_); return new ImageView.Effect.Slide(-this.direction_, this.slow_);
}; };
/** /**
* @param {HTMLCanvasElement} element Element. * @override
* @return {string} Transform string.
*/ */
ImageView.Effect.Slide.prototype.transform = function(element) { ImageView.Effect.Slide.prototype.transform = function(element, viewport) {
var ratio = ImageView.Effect.getPixelRatio_(element); return viewport.getShiftTransformation(this.shift_);
return 'scale(' + (1 / ratio) + ') translate(' + this.shift_ + 'px, 0px)';
}; };
/** /**
...@@ -930,72 +914,75 @@ ImageView.Effect.Slide.prototype.transform = function(element) { ...@@ -930,72 +914,75 @@ ImageView.Effect.Slide.prototype.transform = function(element) {
* Animates the original rectangle to the target rectangle. Both parameters * Animates the original rectangle to the target rectangle. Both parameters
* should be given in device coordinates (accounting for devicePixelRatio). * should be given in device coordinates (accounting for devicePixelRatio).
* *
* @param {Rect} deviceTargetRect Target rectangle. * @param {number} previousImageWidth Width of the full resolution image.
* @param {Rect=} opt_deviceOriginalRect Original rectangle. If omitted, * @param {number} previousImageHeight Hieght of the full resolution image.
* the full viewport will be used at the time of |transform| call. * @param {Rect} imageCropRect Crop rectangle in the full resolution image.
* @param {number=} opt_duration Duration in ms. * @param {number=} opt_duration Duration of the effect.
* @constructor * @constructor
* @extends {ImageView.Effect}
*/ */
ImageView.Effect.Zoom = function( ImageView.Effect.Zoom = function(
deviceTargetRect, opt_deviceOriginalRect, opt_duration) { previousImageWidth, previousImageHeight, imageCropRect, opt_duration) {
ImageView.Effect.call(this, ImageView.Effect.call(this,
opt_duration || ImageView.Effect.DEFAULT_DURATION); opt_duration || ImageView.Effect.DEFAULT_DURATION);
this.target_ = deviceTargetRect; this.previousImageWidth_ = previousImageWidth;
this.original_ = opt_deviceOriginalRect; this.previousImageHeight_ = previousImageHeight;
this.imageCropRect_ = imageCropRect;
}; };
/**
* Inherits from ImageView.Effect.
*/
ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype };
/** /**
* @param {HTMLCanvasElement} element Element. * @override
* @param {Viewport} viewport Viewport.
* @return {string} Transform string.
*/ */
ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { ImageView.Effect.Zoom.prototype.transform = function(element, viewport) {
if (!this.original_) return viewport.getInverseTransformForCroppedImage(
this.original_ = viewport.getDeviceClipped(); this.previousImageWidth_, this.previousImageHeight_, this.imageCropRect_);
};
var ratio = ImageView.Effect.getPixelRatio_(element);
var dx = (this.target_.left + this.target_.width / 2) - /**
(this.original_.left + this.original_.width / 2); * Effect to zoom to a screen rectangle.
var dy = (this.target_.top + this.target_.height / 2) - *
(this.original_.top + this.original_.height / 2); * @param {Rect} screenRect Rectangle in the application window's coordinate.
* @param {number=} opt_duration Duration of effect.
* @constructor
* @extends {ImageView.Effect}
*/
ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) {
ImageView.Effect.call(this, opt_duration);
this.screenRect_ = screenRect;
};
var scaleX = this.target_.width / this.original_.width; ImageView.Effect.ZoomToScreen.prototype = {
var scaleY = this.target_.height / this.original_.height; __proto__: ImageView.Effect.prototype
};
return 'translate(' + (dx / ratio) + 'px,' + (dy / ratio) + 'px) ' + /**
'scaleX(' + (scaleX / ratio) + ') scaleY(' + (scaleY / ratio) + ')'; * @override
*/
ImageView.Effect.ZoomToScreen.prototype.transform = function(
element, viewport) {
return viewport.getScreenRectTransformForImage(this.screenRect_);
}; };
/** /**
* Rotate effect. * Rotation effect.
* *
* @param {number} scale Scale. * @param {boolean} orientation Orientation of rotation. True is for clockwise
* @param {number} rotate90 Rotation in 90 degrees increments. * and false is for counterclockwise.
* @constructor * @constructor
* @extends {ImageView.Effect}
*/ */
ImageView.Effect.Rotate = function(scale, rotate90) { ImageView.Effect.Rotate = function(orientation) {
ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION);
this.scale_ = scale; this.orientation_ = orientation;
this.rotate90_ = rotate90;
}; };
/**
* Inherits from ImageView.Effect.
*/
ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype };
/** /**
* @param {HTMLCanvasElement} element Element. * @override
* @return {string} Transform string.
*/ */
ImageView.Effect.Rotate.prototype.transform = function(element) { ImageView.Effect.Rotate.prototype.transform = function(element, viewport) {
var ratio = ImageView.Effect.getPixelRatio_(element); return viewport.getInverseTransformForRotatedImage(this.orientation_);
return 'rotate(' + (this.rotate90_ * 90) + 'deg) ' +
'scale(' + (this.scale_ / ratio) + ')';
}; };
...@@ -9,9 +9,26 @@ ...@@ -9,9 +9,26 @@
* @constructor * @constructor
*/ */
function Viewport() { function Viewport() {
/**
* Size of the full resolution image.
* @type {Rect}
* @private
*/
this.imageBounds_ = new Rect(); this.imageBounds_ = new Rect();
/**
* Size of the application window.
* @type {Rect}
* @private
*/
this.screenBounds_ = new Rect(); this.screenBounds_ = new Rect();
/**
* Scale from the full resolution image to the screen displayed image. This is
* not zoom operated by users.
* @type {number}
* @private
*/
this.scale_ = 1; this.scale_ = 1;
this.offsetX_ = 0; this.offsetX_ = 0;
this.offsetY_ = 0; this.offsetY_ = 0;
...@@ -22,10 +39,6 @@ function Viewport() { ...@@ -22,10 +39,6 @@ function Viewport() {
this.update(); this.update();
} }
/*
* Viewport modification.
*/
/** /**
* @param {number} width Image width. * @param {number} width Image width.
* @param {number} height Image height. * @param {number} height Image height.
...@@ -86,11 +99,24 @@ Viewport.prototype.setScale = function(scale, notify) { ...@@ -86,11 +99,24 @@ Viewport.prototype.setScale = function(scale, notify) {
* @return {number} Best scale to fit the current image into the current screen. * @return {number} Best scale to fit the current image into the current screen.
*/ */
Viewport.prototype.getFittingScale = function() { Viewport.prototype.getFittingScale = function() {
var scaleX = this.screenBounds_.width / this.imageBounds_.width; return this.getFittingScaleForImageSize_(
var scaleY = this.screenBounds_.height / this.imageBounds_.height; this.imageBounds_.width, this.imageBounds_.height);
// Scales > (1 / this.getDevicePixelRatio()) do not look good. Also they are };
/**
* Obtains the scale for the specified image size.
*
* @param {number} width Width of the full resolution image.
* @param {number} height Height of the full resolution image.
* @return {number} The ratio of the fullresotion image size and the calculated
* displayed image size.
*/
Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) {
var scaleX = this.screenBounds_.width / width;
var scaleY = this.screenBounds_.height / height;
// Scales > (1 / devicePixelRatio) do not look good. Also they are
// not really useful as we do not have any pixel-level operations. // not really useful as we do not have any pixel-level operations.
return Math.min(1 / Viewport.getDevicePixelRatio(), scaleX, scaleY); return Math.min(1 / window.devicePixelRatio, scaleX, scaleY);
}; };
/** /**
...@@ -281,11 +307,6 @@ Viewport.prototype.imageToScreenRect = function(rect) { ...@@ -281,11 +307,6 @@ Viewport.prototype.imageToScreenRect = function(rect) {
Math.round(this.imageToScreenSize(rect.height))); Math.round(this.imageToScreenSize(rect.height)));
}; };
/**
* @return {number} The number of physical pixels in one CSS pixel.
*/
Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio; };
/** /**
* Convert a rectangle from screen coordinates to 'device' coordinates. * Convert a rectangle from screen coordinates to 'device' coordinates.
* *
...@@ -296,7 +317,7 @@ Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio; }; ...@@ -296,7 +317,7 @@ Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio; };
* @return {Rect} Rectangle in device coordinates. * @return {Rect} Rectangle in device coordinates.
*/ */
Viewport.prototype.screenToDeviceRect = function(rect) { Viewport.prototype.screenToDeviceRect = function(rect) {
var ratio = Viewport.getDevicePixelRatio(); var ratio = window.devicePixelRatio;
var screenCenterX = Math.round( var screenCenterX = Math.round(
this.screenBounds_.left + this.screenBounds_.width / 2); this.screenBounds_.left + this.screenBounds_.width / 2);
var screenCenterY = Math.round( var screenCenterY = Math.round(
...@@ -417,3 +438,95 @@ Viewport.prototype.repaint = function() { ...@@ -417,3 +438,95 @@ Viewport.prototype.repaint = function() {
for (var i = 0; i != this.repaintCallbacks_.length; i++) for (var i = 0; i != this.repaintCallbacks_.length; i++)
this.repaintCallbacks_[i](); this.repaintCallbacks_[i]();
}; };
/**
* Obtains CSS transformation for the screen image.
* @return {string} Transformation description.
*/
Viewport.prototype.getTransformation = function() {
return 'scale(' + (1 / window.devicePixelRatio) + ')';
};
/**
* Obtains shift CSS transformation for the screen image.
* @param {number} dx Amount of shift.
* @return {string} Transformation description.
*/
Viewport.prototype.getShiftTransformation = function(dx) {
return 'translateX(' + dx + 'px) ' + this.getTransformation();
};
/**
* Obtains CSS transformation that makes the rotated image fit the original
* image. The new rotated image that the transformation is applied to looks the
* same with original image.
*
* @param {boolean} orientation Orientation of the rotation from the original
* image to the rotated image. True is for clockwise and false is for
* counterclockwise.
* @return {string} Transformation description.
*/
Viewport.prototype.getInverseTransformForRotatedImage = function(orientation) {
var previousImageWidth = this.imageBounds_.height;
var previousImageHeight = this.imageBounds_.width;
var oldScale = this.getFittingScaleForImageSize_(
previousImageWidth, previousImageHeight);
var scaleRatio = oldScale / this.getScale();
var degree = orientation ? '-90deg' : '90deg';
return [
'scale(' + scaleRatio + ')',
'rotate(' + degree + ')',
this.getTransformation()
].join(' ');
};
/**
* Obtains CSS transformation that makes the cropped image fit the original
* image. The new cropped image that the transformaton is applied to fits to the
* the cropped rectangle in the original image.
*
* @param {number} imageWidth Width of the original image.
* @param {number} imageHeight Height of the origianl image.
* @param {Rect} imageCropRect Crop rectangle in the image's coordinate system.
* @return {string} Transformation description.
*/
Viewport.prototype.getInverseTransformForCroppedImage =
function(imageWidth, imageHeight, imageCropRect) {
var wholeScale = this.getFittingScaleForImageSize_(
imageWidth, imageHeight);
var croppedScale = this.getFittingScaleForImageSize_(
imageCropRect.width, imageCropRect.height);
var dx =
(imageCropRect.left + imageCropRect.width / 2 - imageWidth / 2) *
wholeScale;
var dy =
(imageCropRect.top + imageCropRect.height / 2 - imageHeight / 2) *
wholeScale;
return [
'translate(' + dx + 'px,' + dy + 'px)',
'scale(' + wholeScale / croppedScale + ')',
this.getTransformation()
].join(' ');
};
/**
* Obtains CSS transformaton that makes the image fit to the screen rectangle.
*
* @param {Rect} screenRect Screen rectangle.
* @return {string} Transformation description.
*/
Viewport.prototype.getScreenRectTransformForImage = function(screenRect) {
var screenImageWidth = this.imageBounds_.width * this.getScale();
var screenImageHeight = this.imageBounds_.height * this.getScale();
var scaleX = screenRect.width / screenImageWidth;
var scaleY = screenRect.height / screenImageHeight;
var screenWidth = this.screenBounds_.width;
var screenHeight = this.screenBounds_.height;
var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2;
var dy = screenRect.top + screenRect.height / 2 - screenHeight / 2;
return [
'translate(' + dx + 'px,' + dy + 'px)',
'scale(' + scaleX + ',' + scaleY + ')',
this.getTransformation()
].join(' ');
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment