Index: ui/file_manager/gallery/js/image_editor/viewport.js |
diff --git a/ui/file_manager/gallery/js/image_editor/viewport.js b/ui/file_manager/gallery/js/image_editor/viewport.js |
index 4e744174593df1eef1993aa75ee199316ab14458..5f63d1b8c5bfa999300c67fc4b93fd883331622f 100644 |
--- a/ui/file_manager/gallery/js/image_editor/viewport.js |
+++ b/ui/file_manager/gallery/js/image_editor/viewport.js |
@@ -24,6 +24,27 @@ function Viewport() { |
this.screenBounds_ = new Rect(); |
/** |
+ * Bounds of the image element on screen without zoom and offset. |
+ * @type {Rect} |
+ * @private |
+ */ |
+ this.imageElementBoundsOnScreen_ = null; |
+ |
+ /** |
+ * Bounds of the image with zoom and offset. |
+ * @type {Rect} |
+ * @private |
+ */ |
+ this.imageBoundsOnScreen_ = null; |
+ |
+ /** |
+ * Image bounds that is clipped with the screen bounds. |
+ * @type {Rect} |
+ * @private |
+ */ |
+ this.imageBoundsOnScreenClipped_ = null; |
+ |
+ /** |
* Scale from the full resolution image to the screen displayed image. This is |
* not zoom operated by users. |
* @type {number} |
@@ -45,12 +66,28 @@ function Viewport() { |
*/ |
this.zoom_ = 1; |
+ /** |
+ * Offset specified by user operations. |
+ * @type {number} |
+ */ |
this.offsetX_ = 0; |
+ |
+ /** |
+ * Offset specified by user operations. |
+ * @type {number} |
+ */ |
this.offsetY_ = 0; |
+ /** |
+ * Generation of the screen size image cache. |
+ * This is incremented every time when the size of image cache is changed. |
+ * @type {number} |
+ * @private |
+ */ |
this.generation_ = 0; |
this.update(); |
+ Object.seal(this); |
} |
/** |
@@ -75,6 +112,7 @@ Viewport.ZOOM_RATIOS = Object.freeze({ |
*/ |
Viewport.prototype.setImageSize = function(width, height) { |
this.imageBounds_ = new Rect(width, height); |
+ this.update(); |
this.invalidateCaches(); |
}; |
@@ -84,6 +122,7 @@ Viewport.prototype.setImageSize = function(width, height) { |
*/ |
Viewport.prototype.setScreenSize = function(width, height) { |
this.screenBounds_ = new Rect(width, height); |
+ this.update(); |
this.invalidateCaches(); |
}; |
@@ -95,9 +134,9 @@ Viewport.prototype.setZoomIndex = function(zoomIndex) { |
// Ignore the invalid zoomIndex. |
if (!Viewport.ZOOM_RATIOS[zoomIndex.toString()]) |
return; |
- |
this.zoomIndex_ = zoomIndex; |
this.zoom_ = Viewport.ZOOM_RATIOS[zoomIndex]; |
+ this.update(); |
}; |
/** |
@@ -109,40 +148,18 @@ Viewport.prototype.getZoomIndex = function() { |
}; |
/** |
- * Set the size by an HTML element. |
- * |
- * @param {HTMLElement} frame The element acting as the "screen". |
- */ |
-Viewport.prototype.sizeByFrame = function(frame) { |
- this.setScreenSize(frame.clientWidth, frame.clientHeight); |
-}; |
- |
-/** |
- * Set the size and scale to fit an HTML element. |
- * |
- * @param {HTMLElement} frame The element acting as the "screen". |
- */ |
-Viewport.prototype.sizeByFrameAndFit = function(frame) { |
- var wasFitting = this.getScale() == this.getFittingScale(); |
- this.sizeByFrame(frame); |
- var minScale = this.getFittingScale(); |
- if (wasFitting || (this.getScale() < minScale)) { |
- this.setScale(minScale, true); |
- } |
-}; |
- |
-/** |
* @return {number} Scale. |
*/ |
Viewport.prototype.getScale = function() { return this.scale_; }; |
/** |
* @param {number} scale The new scale. |
- * @param {boolean} notify True if the change should be reflected in the UI. |
*/ |
-Viewport.prototype.setScale = function(scale, notify) { |
- if (this.scale_ == scale) return; |
+Viewport.prototype.setScale = function(scale) { |
+ if (this.scale_ == scale) |
+ return; |
this.scale_ = scale; |
+ this.update(); |
this.invalidateCaches(); |
}; |
@@ -174,8 +191,7 @@ Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) { |
* Set the scale to fit the image into the screen. |
*/ |
Viewport.prototype.fitImage = function() { |
- var scale = this.getFittingScale(); |
- this.setScale(scale, true); |
+ this.setScale(this.getFittingScale()); |
}; |
/** |
@@ -216,15 +232,13 @@ Viewport.prototype.getImageBounds = function() { return this.imageBounds_; }; |
Viewport.prototype.getScreenBounds = function() { return this.screenBounds_; }; |
/** |
- * @return {Rect} The visible part of the image, in image coordinates. |
+ * @return {Rect} The size of screen cache canvas. |
*/ |
-Viewport.prototype.getImageClipped = function() { return this.imageClipped_; }; |
- |
-/** |
- * @return {Rect} The visible part of the image, in screen coordinates. |
- */ |
-Viewport.prototype.getScreenClipped = function() { |
- return this.screenClipped_; |
+Viewport.prototype.getDeviceBounds = function() { |
+ var size = this.getImageElementBoundsOnScreen(); |
+ return new Rect( |
+ size.width * window.devicePixelRatio, |
+ size.height * window.devicePixelRatio); |
}; |
/** |
@@ -244,7 +258,24 @@ Viewport.prototype.invalidateCaches = function() { this.generation_++; }; |
* @return {Rect} The image bounds in screen coordinates. |
*/ |
Viewport.prototype.getImageBoundsOnScreen = function() { |
- return this.imageOnScreen_; |
+ return this.imageBoundsOnScreen_; |
+}; |
+ |
+/** |
+ * The image bounds in screen coordinates. |
+ * This returns the bounds of element before applying zoom and offset. |
+ * @return {Rect} |
+ */ |
+Viewport.prototype.getImageElementBoundsOnScreen = function() { |
+ return this.imageElementBoundsOnScreen_; |
+}; |
+ |
+/** |
+ * The image bounds on screen, which is clipped with the screen size. |
+ * @return {Rect} |
+ */ |
+Viewport.prototype.getImageBoundsOnScreenClipped = function() { |
+ return this.imageBoundsOnScreenClipped_; |
}; |
/** |
@@ -260,7 +291,7 @@ Viewport.prototype.screenToImageSize = function(size) { |
* @return {number} X in image coordinates. |
*/ |
Viewport.prototype.screenToImageX = function(x) { |
- return Math.round((x - this.imageOnScreen_.left) / this.getScale()); |
+ return Math.round((x - this.imageBoundsOnScreen_.left) / this.getScale()); |
}; |
/** |
@@ -268,7 +299,7 @@ Viewport.prototype.screenToImageX = function(x) { |
* @return {number} Y in image coordinates. |
*/ |
Viewport.prototype.screenToImageY = function(y) { |
- return Math.round((y - this.imageOnScreen_.top) / this.getScale()); |
+ return Math.round((y - this.imageBoundsOnScreen_.top) / this.getScale()); |
}; |
/** |
@@ -296,7 +327,7 @@ Viewport.prototype.imageToScreenSize = function(size) { |
* @return {number} X in screen coordinates. |
*/ |
Viewport.prototype.imageToScreenX = function(x) { |
- return Math.round(this.imageOnScreen_.left + x * this.getScale()); |
+ return Math.round(this.imageBoundsOnScreen_.left + x * this.getScale()); |
}; |
/** |
@@ -304,7 +335,7 @@ Viewport.prototype.imageToScreenX = function(x) { |
* @return {number} Y in screen coordinates. |
*/ |
Viewport.prototype.imageToScreenY = function(y) { |
- return Math.round(this.imageOnScreen_.top + y * this.getScale()); |
+ return Math.round(this.imageBoundsOnScreen_.top + y * this.getScale()); |
}; |
/** |
@@ -320,34 +351,6 @@ Viewport.prototype.imageToScreenRect = function(rect) { |
}; |
/** |
- * Convert a rectangle from screen coordinates to 'device' coordinates. |
- * |
- * This conversion enlarges the original rectangle devicePixelRatio times |
- * with the screen center as a fixed point. |
- * |
- * @param {Rect} rect Rectangle in screen coordinates. |
- * @return {Rect} Rectangle in device coordinates. |
- */ |
-Viewport.prototype.screenToDeviceRect = function(rect) { |
- var ratio = window.devicePixelRatio; |
- var screenCenterX = Math.round( |
- this.screenBounds_.left + this.screenBounds_.width / 2); |
- var screenCenterY = Math.round( |
- this.screenBounds_.top + this.screenBounds_.height / 2); |
- return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, |
- screenCenterY + (rect.top - screenCenterY) * ratio, |
- rect.width * ratio, |
- rect.height * ratio); |
-}; |
- |
-/** |
- * @return {Rect} The visible part of the image, in device coordinates. |
- */ |
-Viewport.prototype.getDeviceClipped = function() { |
- return this.screenToDeviceRect(this.getScreenClipped()); |
-}; |
- |
-/** |
* @return {boolean} True if some part of the image is clipped by the screen. |
*/ |
Viewport.prototype.isClipped = function() { |
@@ -395,44 +398,46 @@ Viewport.prototype.clampOffsetY_ = function(y) { |
}; |
/** |
+ * @private |
+ */ |
+Viewport.prototype.getCenteredRect_ = function( |
+ width, height, offsetX, offsetY) { |
+ return new Rect( |
+ ~~((this.screenBounds_.width - width) / 2) + offsetX, |
+ ~~((this.screenBounds_.height - height) / 2) + offsetY, |
+ width, |
+ height); |
+}; |
+ |
+/** |
* Recalculate the viewport parameters. |
*/ |
Viewport.prototype.update = function() { |
var scale = this.getScale(); |
- // Image bounds in screen coordinates. |
- this.imageOnScreen_ = new Rect( |
- this.getMarginX_(), |
- this.getMarginY_(), |
- Math.round(this.imageBounds_.width * scale), |
- Math.round(this.imageBounds_.height * scale)); |
- |
- // A visible part of the image in image coordinates. |
- this.imageClipped_ = new Rect(this.imageBounds_); |
- |
- // A visible part of the image in screen coordinates. |
- this.screenClipped_ = new Rect(this.screenBounds_); |
- |
- // Adjust for the offset. |
- if (this.imageOnScreen_.left < 0) { |
- this.imageOnScreen_.left += |
- Math.round(this.clampOffsetX_(this.offsetX_) * scale); |
- this.imageClipped_.left = Math.round(-this.imageOnScreen_.left / scale); |
- this.imageClipped_.width = Math.round(this.screenBounds_.width / scale); |
- } else { |
- this.screenClipped_.left = this.imageOnScreen_.left; |
- this.screenClipped_.width = this.imageOnScreen_.width; |
- } |
+ // Image bounds on screen. |
+ this.imageBoundsOnScreen_ = this.getCenteredRect_( |
+ ~~(this.imageBounds_.width * scale * this.zoom_), |
+ ~~(this.imageBounds_.height * scale * this.zoom_), |
+ this.offsetX_, |
+ this.offsetY_); |
- if (this.imageOnScreen_.top < 0) { |
- this.imageOnScreen_.top += |
- Math.round(this.clampOffsetY_(this.offsetY_) * scale); |
- this.imageClipped_.top = Math.round(-this.imageOnScreen_.top / scale); |
- this.imageClipped_.height = Math.round(this.screenBounds_.height / scale); |
- } else { |
- this.screenClipped_.top = this.imageOnScreen_.top; |
- this.screenClipped_.height = this.imageOnScreen_.height; |
- } |
+ // Image bounds of element (that is not applied zoom and offset) on screen. |
+ this.imageElementBoundsOnScreen_ = this.getCenteredRect_( |
+ ~~(this.imageBounds_.width * scale), |
+ ~~(this.imageBounds_.height * scale), |
+ 0, |
+ 0); |
+ |
+ // Image bounds on screen cliped with the screen bounds. |
+ var left = Math.max(this.imageBoundsOnScreen_.left, 0); |
+ var top = Math.max(this.imageBoundsOnScreen_.top, 0); |
+ var right = Math.min( |
+ this.imageBoundsOnScreen_.right, this.screenBounds_.width); |
+ var bottom = Math.min( |
+ this.imageBoundsOnScreen_.bottom, this.screenBounds_.height); |
+ this.imageBoundsOnScreenClipped_ = new Rect( |
+ left, top, right - left, bottom - top); |
}; |
/** |
@@ -512,10 +517,9 @@ Viewport.prototype.getInverseTransformForCroppedImage = |
* @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 imageBounds = this.getImageElementBoundsOnScreen(); |
+ var scaleX = screenRect.width / imageBounds.width; |
+ var scaleY = screenRect.height / imageBounds.height; |
var screenWidth = this.screenBounds_.width; |
var screenHeight = this.screenBounds_.height; |
var dx = screenRect.left + screenRect.width / 2 - screenWidth / 2; |