Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Unified Diff: chrome/browser/resources/file_manager/js/image_editor/image_transform.js

Issue 7541075: Pre-scaling images to speed up feedback in Image Editor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/file_manager/js/image_editor/image_transform.js
diff --git a/chrome/browser/resources/file_manager/js/image_editor/image_transform.js b/chrome/browser/resources/file_manager/js/image_editor/image_transform.js
index 0b39f20dafb1600399ecbc0893ed420d1ed84f79..68531560354bb97480202c8d8c00b294d9effd40 100644
--- a/chrome/browser/resources/file_manager/js/image_editor/image_transform.js
+++ b/chrome/browser/resources/file_manager/js/image_editor/image_transform.js
@@ -15,7 +15,7 @@ ImageEditor.Mode.Resize.prototype = {__proto__: ImageEditor.Mode.prototype};
ImageEditor.Mode.register(ImageEditor.Mode.Resize);
ImageEditor.Mode.Resize.prototype.createTools = function(toolbar) {
- var canvas = this.getBuffer().getImageCanvas();
+ var canvas = this.getContent().getCanvas();
this.widthRange_ =
toolbar.addRange('width', 0, canvas.width, canvas.width * 2);
this.heightRange_ =
@@ -23,19 +23,12 @@ ImageEditor.Mode.Resize.prototype.createTools = function(toolbar) {
};
ImageEditor.Mode.Resize.prototype.commit = function() {
- var newCanvas = this.getBuffer().createBlankCanvas(
- this.widthRange_.getValue(), this.heightRange_.getValue());
-
- var srcCanvas = this.getBuffer().getImageCanvas();
-
- var context = newCanvas.getContext("2d");
ImageUtil.trace.resetTimer('transform');
- Rect.drawImage(
- context, srcCanvas, new Rect(newCanvas), new Rect(srcCanvas));
+ var newCanvas = this.getContent().copyCanvas(
+ this.widthRange_.getValue(), this.heightRange_.getValue());
ImageUtil.trace.reportTimer('transform');
-
- this.getBuffer().setImageCanvas(newCanvas);
- this.getBuffer().fitImage();
+ this.getContent().setCanvas(newCanvas);
+ this.getViewport().fitImage();
};
/**
@@ -50,25 +43,23 @@ ImageEditor.Mode.Rotate.prototype = {__proto__: ImageEditor.Mode.prototype};
ImageEditor.Mode.register(ImageEditor.Mode.Rotate);
-ImageEditor.Mode.Rotate.prototype.commit = function() {
- this.getBuffer().fitImage();
-};
+ImageEditor.Mode.Rotate.prototype.commit = function() {};
ImageEditor.Mode.Rotate.prototype.rollback = function() {
if (this.backup_) {
- this.getBuffer().setImageCanvas(this.backup_);
+ this.getContent().setCanvas(this.backup_);
this.backup_ = null;
- this.transform_ = null;
}
+ this.transform_ = null;
};
ImageEditor.Mode.Rotate.prototype.createTools = function(toolbar) {
- toolbar.addButton("Left", this.doTransform.bind(this, 1, 1, 3));
- toolbar.addButton("Right", this.doTransform.bind(this, 1, 1, 1));
- toolbar.addButton("Flip V", this.doTransform.bind(this, 1, -1, 0));
- toolbar.addButton("Flip H", this.doTransform.bind(this, -1, 1, 0));
+ toolbar.addButton("Left", this.modifyTransform.bind(this, 1, 1, 3));
+ toolbar.addButton("Right", this.modifyTransform.bind(this, 1, 1, 1));
+ toolbar.addButton("Flip V", this.modifyTransform.bind(this, 1, -1, 0));
+ toolbar.addButton("Flip H", this.modifyTransform.bind(this, -1, 1, 0));
- var srcCanvas = this.getBuffer().getImageCanvas();
+ var srcCanvas = this.getContent().getCanvas();
var width = srcCanvas.width;
var height = srcCanvas.height;
@@ -81,79 +72,165 @@ ImageEditor.Mode.Rotate.prototype.createTools = function(toolbar) {
this.tiltRange_.addEventListener('mouseup', this.onTiltStop.bind(this));
};
+ImageEditor.Mode.Rotate.prototype.getOriginal = function() {
+ if (!this.backup_) {
+ this.backup_ = this.getContent().getCanvas();
+ }
+ return this.backup_;
+};
+
+ImageEditor.Mode.Rotate.prototype.getTransform = function() {
+ if (!this.transform_) {
+ this.transform_ = new ImageEditor.Mode.Rotate.Transform();
+ }
+ return this.transform_;
+};
+
ImageEditor.Mode.Rotate.prototype.onTiltStart = function() {
this.tiltDrag_ = true;
+
+ var original = this.getOriginal();
+
+ // Downscale the original image to the overview thumbnail size.
+ var downScale = ImageBuffer.Overview.MAX_SIZE /
+ Math.max(original.width, original.height);
+
+ this.preScaledOriginal_ = this.getContent().createBlankCanvas(
+ original.width * downScale, original.height * downScale);
+ Rect.drawImage(this.preScaledOriginal_.getContext('2d'), original);
+
+ // Translate the current offset into the original image coordinate space.
+ var viewport = this.getViewport();
+ var originalOffset = this.getTransform().transformOffsetToBaseline(
+ viewport.getOffsetX(), viewport.getOffsetY());
+
+ // Find the part of the original image that is sufficient to pre-render
+ // the rotation results.
+ var screenClipped = viewport.getScreenClipped();
+ var diagonal = viewport.screenToImageSize(
+ Math.sqrt(screenClipped.width * screenClipped.width +
+ screenClipped.height * screenClipped.height));
+
+ var originalBounds = new Rect(original);
+
+ var originalPreclipped = new Rect(
+ originalBounds.width / 2 - originalOffset.x - diagonal / 2,
+ originalBounds.height / 2 - originalOffset.y - diagonal / 2,
+ diagonal,
+ diagonal).clamp(originalBounds);
+
+ // We assume that the scale is not changing during the mouse drag.
+ var scale = viewport.getScale();
+ this.preClippedOriginal_ = this.getContent().createBlankCanvas(
+ originalPreclipped.width * scale, originalPreclipped.height * scale);
+
+ Rect.drawImage(this.preClippedOriginal_.getContext('2d'), original, null,
+ originalPreclipped);
+
this.repaint();
};
ImageEditor.Mode.Rotate.prototype.onTiltStop = function() {
this.tiltDrag_ = false;
- this.repaint();
+ if (this.preScaledOriginal_) {
+ this.preScaledOriginal_ = false;
+ this.preClippedOriginal_ = false;
+ this.applyTransform();
+ } else {
+ this.repaint();
+ }
};
ImageEditor.Mode.Rotate.prototype.draw = function(context) {
if (!this.tiltDrag_) return;
- var rect = this.getBuffer().getClippedScreen();
+ var screenClipped = this.getViewport().getScreenClipped();
+
+ if (this.preClippedOriginal_) {
+ ImageUtil.trace.resetTimer('preview');
+ var transformed = this.getContent().createBlankCanvas(
+ screenClipped.width, screenClipped.height);
+ this.getTransform().apply(
+ transformed.getContext('2d'), this.preClippedOriginal_);
+ Rect.drawImage(context, transformed, screenClipped);
+ ImageUtil.trace.reportTimer('preview');
+ }
+
const STEP = 50;
context.save();
context.globalAlpha = 0.4;
context.strokeStyle = "#C0C0C0";
- for(var x = 0; x <= rect.width - STEP; x += STEP) {
- context.strokeRect(rect.left + x, rect.top, STEP, rect.height);
+
+ for(var x = Math.floor(screenClipped.left / STEP) * STEP;
+ x < screenClipped.left + screenClipped.width;
+ x += STEP) {
+ var left = Math.max(screenClipped.left, x);
+ var right = Math.min(screenClipped.left + screenClipped.width, x + STEP);
+ context.strokeRect(
+ left, screenClipped.top, right - left, screenClipped.height);
}
- for(var y = 0; y <= rect.height - STEP; y += STEP) {
- context.strokeRect(rect.left, rect.top + y, rect.width, STEP);
+
+ for(var y = Math.floor(screenClipped.top / STEP) * STEP;
+ y < screenClipped.top + screenClipped.height;
+ y += STEP) {
+ var top = Math.max(screenClipped.top, y);
+ var bottom = Math.min(screenClipped.top + screenClipped.height, y + STEP);
+ context.strokeRect(
+ screenClipped.left, top, screenClipped.width, bottom - top);
}
+
context.restore();
};
-ImageEditor.Mode.Rotate.prototype.doTransform =
+ImageEditor.Mode.Rotate.prototype.modifyTransform =
function(scaleX, scaleY, turn90) {
- if (!this.backup_) {
- this.backup_ = this.getBuffer().getImageCanvas();
- this.transform_ = new ImageEditor.Mode.Rotate.Transform();
- }
- var baselineOffset = this.transform_.transformOffsetToBaseline(
- this.getBuffer().getOffsetX(), this.getBuffer().getOffsetY());
+ var transform = this.getTransform();
+ var viewport = this.getViewport();
+
+ var baselineOffset = transform.transformOffsetToBaseline(
+ viewport.getOffsetX(), viewport.getOffsetY());
- this.transform_.modify(scaleX, scaleY, turn90, this.tiltRange_.getValue());
+ transform.modify(scaleX, scaleY, turn90, this.tiltRange_.getValue());
+
+ var newOffset = transform.transformOffsetFromBaseline(
+ baselineOffset.x, baselineOffset.y);
+
+ // Ignoring offset clipping makes rotation behave more naturally.
+ viewport.setOffset(newOffset.x, newOffset.y, true /*ignore clipping*/);
if (scaleX * scaleY < 0) {
- this.tiltRange_.setValue(this.transform_.tilt);
+ this.tiltRange_.setValue(transform.tilt);
}
- var srcCanvas = this.backup_;
+ this.applyTransform();
+};
+
+ImageEditor.Mode.Rotate.prototype.applyTransform = function() {
+ var srcCanvas = this.getOriginal();
var newSize = this.transform_.getTiltedRectSize(
srcCanvas.width, srcCanvas.height);
- var dstCanvas =
- this.getBuffer().createBlankCanvas(newSize.width, newSize.height);
+ var scale = 1;
- var context = dstCanvas.getContext("2d");
- context.save();
- context.translate(dstCanvas.width / 2, dstCanvas.height / 2);
- context.rotate(this.transform_.getAngle());
- context.scale(this.transform_.scaleX, this.transform_.scaleY);
+ if (this.preScaledOriginal_) {
+ scale = this.preScaledOriginal_.width / srcCanvas.width;
+ srcCanvas = this.preScaledOriginal_;
+ }
+
+ var dstCanvas = this.getContent().createBlankCanvas(
+ newSize.width * scale, newSize.height * scale);
ImageUtil.trace.resetTimer('transform');
- context.drawImage(srcCanvas, -srcCanvas.width / 2, -srcCanvas.height / 2);
+ this.transform_.apply(dstCanvas.getContext('2d'), srcCanvas);
ImageUtil.trace.reportTimer('transform');
- context.restore();
+ this.getContent().setCanvas(dstCanvas, newSize.width, newSize.height);
- var newOffset = this.transform_.transformOffsetFromBaseline(
- baselineOffset.x, baselineOffset.y);
-
- // Ignoring offset clipping make rotation behave more natural.
- this.getBuffer().setOffset(
- newOffset.x, newOffset.y, true /*ignore clipping*/);
- this.getBuffer().setImageCanvas(dstCanvas);
- this.getBuffer().repaint();
+ this.repaint();
};
ImageEditor.Mode.Rotate.prototype.update = function(values) {
- this.doTransform(1, 1, 0);
+ this.modifyTransform(1, 1, 0);
};
ImageEditor.Mode.Rotate.Transform = function() {
@@ -161,7 +238,7 @@ ImageEditor.Mode.Rotate.Transform = function() {
this.scaleY = 1;
this.turn90 = 0;
this.tilt = 0;
-}
+};
ImageEditor.Mode.Rotate.Transform.prototype.modify =
function(scaleX, scaleY, turn90, tilt) {
@@ -224,6 +301,15 @@ ImageEditor.Mode.Rotate.Transform.prototype.getTiltedRectSize =
}
};
+ImageEditor.Mode.Rotate.Transform.prototype.apply = function(
+ context, srcCanvas) {
+ context.save();
+ context.translate(context.canvas.width / 2, context.canvas.height / 2);
+ context.rotate(this.getAngle());
+ context.scale(this.scaleX, this.scaleY);
+ context.drawImage(srcCanvas, -srcCanvas.width / 2, -srcCanvas.height / 2);
+ context.restore();
+};
/**
* Crop mode.
@@ -246,17 +332,17 @@ ImageEditor.Mode.Crop.GRAB_RADIUS = 5;
ImageEditor.Mode.Crop.prototype.commit = function() {
var cropImageRect = this.cropRect_.getRect();
- var newCanvas = this.getBuffer().
+ var newCanvas = this.getContent().
createBlankCanvas(cropImageRect.width, cropImageRect.height);
var newContext = newCanvas.getContext("2d");
ImageUtil.trace.resetTimer('transform');
- Rect.drawImage(newContext, this.getBuffer().getImageCanvas(),
+ Rect.drawImage(newContext, this.getContent().getCanvas(),
new Rect(newCanvas), cropImageRect);
ImageUtil.trace.reportTimer('transform');
- this.getBuffer().setImageCanvas(newCanvas);
- this.getBuffer().fitImage();
+ this.getContent().setCanvas(newCanvas);
+ this.getViewport().fitImage();
};
ImageEditor.Mode.Crop.prototype.rollback = function() {
@@ -264,17 +350,17 @@ ImageEditor.Mode.Crop.prototype.rollback = function() {
};
ImageEditor.Mode.Crop.prototype.createDefaultCrop = function() {
- var rect = new Rect(this.getBuffer().getClippedImage());
+ var rect = new Rect(this.getViewport().getImageClipped());
rect = rect.inflate (-rect.width / 6, -rect.height / 6);
this.cropRect_ = new DraggableRect(
- rect, this.getBuffer(), ImageEditor.Mode.Crop.GRAB_RADIUS);
+ rect, this.getViewport(), ImageEditor.Mode.Crop.GRAB_RADIUS);
};
ImageEditor.Mode.Crop.prototype.draw = function(context) {
var R = ImageEditor.Mode.Crop.GRAB_RADIUS;
- var inner = this.getBuffer().imageToScreenRect(this.cropRect_.getRect());
- var outer = this.getBuffer().getClippedScreen();
+ var inner = this.getViewport().imageToScreenRect(this.cropRect_.getRect());
+ var outer = this.getViewport().getScreenClipped();
var inner_bottom = inner.top + inner.height;
var inner_right = inner.left + inner.width;
@@ -324,7 +410,7 @@ ImageEditor.Mode.Crop.prototype.getDragHandler = function(x, y) {
* A draggable rectangle over the image.
*/
-function DraggableRect(rect, buffer, sensitivity) {
+function DraggableRect(rect, viewport, sensitivity) {
// The bounds are not held in a regular rectangle (with width/height).
// left/top/right/bottom held instead for convenience.
@@ -334,7 +420,7 @@ function DraggableRect(rect, buffer, sensitivity) {
this.bounds_[DraggableRect.TOP] = rect.top;
this.bounds_[DraggableRect.BOTTOM] = rect.top + rect.height;
- this.buffer_ = buffer;
+ this.viewport_ = viewport;
this.sensitivity_ = sensitivity;
this.oppositeSide_ = {};
@@ -350,10 +436,10 @@ function DraggableRect(rect, buffer, sensitivity) {
this.cssSide_[DraggableRect.RIGHT] = 'e';
this.cssSide_[DraggableRect.BOTTOM] = 's';
this.cssSide_[DraggableRect.NONE] = '';
-};
+}
// Static members to simplify reflective access to the bounds.
-DraggableRect.LEFT = 'left'
+DraggableRect.LEFT = 'left';
DraggableRect.RIGHT = 'right';
DraggableRect.TOP = 'top';
DraggableRect.BOTTOM = 'bottom';
@@ -368,7 +454,7 @@ DraggableRect.prototype.getDragMode = function(x, y) {
};
var bounds = this.bounds_;
- var R = this.buffer_.screenToImageSize(this.sensitivity_);
+ var R = this.viewport_.screenToImageSize(this.sensitivity_);
var circle = new Circle(x, y, R);
@@ -410,7 +496,7 @@ DraggableRect.prototype.getCursorStyle = function(x, y, mouseDown) {
mode = this.dragMode_;
} else {
mode = this.getDragMode(
- this.buffer_.screenToImageX(x), this.buffer_.screenToImageY(y));
+ this.viewport_.screenToImageX(x), this.viewport_.screenToImageY(y));
}
if (mode.whole) return 'hand';
if (mode.outside) return 'crosshair';
@@ -418,10 +504,10 @@ DraggableRect.prototype.getCursorStyle = function(x, y, mouseDown) {
};
DraggableRect.prototype.getDragHandler = function(x, y) {
- x = this.buffer_.screenToImageX(x);
- y = this.buffer_.screenToImageY(y);
+ x = this.viewport_.screenToImageX(x);
+ y = this.viewport_.screenToImageY(y);
- var clipRect = this.buffer_.getClippedImage();
+ var clipRect = this.viewport_.getImageClipped();
if (!clipRect.inside(x, y)) return null;
this.dragMode_ = this.getDragMode(x, y);
@@ -487,16 +573,16 @@ DraggableRect.prototype.getDragHandler = function(x, y) {
}
function convertX(x) {
- return ImageUtil.clip(
+ return ImageUtil.clamp(
clipRect.left,
- self.buffer_.screenToImageX(x) + mouseBiasX,
+ self.viewport_.screenToImageX(x) + mouseBiasX,
clipRect.left + clipRect.width - fixedWidth);
}
function convertY(y) {
- return ImageUtil.clip(
+ return ImageUtil.clamp(
clipRect.top,
- self.buffer_.screenToImageY(y) + mouseBiasY,
+ self.viewport_.screenToImageY(y) + mouseBiasY,
clipRect.top + clipRect.height - fixedHeight);
}

Powered by Google App Engine
This is Rietveld 408576698