Index: chrome/browser/resources/file_manager/js/image_editor/image_buffer.js |
diff --git a/chrome/browser/resources/file_manager/js/image_editor/image_buffer.js b/chrome/browser/resources/file_manager/js/image_editor/image_buffer.js |
index 874d3281105d498e94393aa8f567e6630c32bc34..75bb05e8c555aaa5e1b403c966358562e42fb47c 100644 |
--- a/chrome/browser/resources/file_manager/js/image_editor/image_buffer.js |
+++ b/chrome/browser/resources/file_manager/js/image_editor/image_buffer.js |
@@ -10,224 +10,22 @@ |
*/ |
function ImageBuffer(screenCanvas) { |
this.screenCanvas_ = screenCanvas; |
- this.screenContext_ = this.screenCanvas_.getContext("2d"); |
- this.scale_ = 1; |
- this.offsetX_ = 0; |
- this.offsetY_ = 0; |
+ this.viewport_ = new Viewport(this.repaint.bind(this)); |
+ this.viewport_.setScreenSize(screenCanvas.width, screenCanvas.height); |
- this.overlays_ = []; |
+ this.content_ = new ImageBuffer.Content( |
+ this.viewport_, screenCanvas.ownerDocument); |
- this.setImageCanvas(this.createBlankCanvas(0, 0)); |
+ this.overlays_ = []; |
+ this.addOverlay(new ImageBuffer.Margin(this.viewport_)); |
+ this.addOverlay(this.content_); |
+ this.addOverlay(new ImageBuffer.Overview(this.viewport_, this.content_)); |
} |
-/* |
- * Viewport manipulation. |
- */ |
- |
-ImageBuffer.prototype.setScaleControl = function(scaleControl) { |
- this.scaleControl_ = scaleControl; |
-}; |
- |
-ImageBuffer.prototype.getScale = function () { return this.scale_ }; |
- |
-ImageBuffer.prototype.setScale = function (scale, notify) { |
- if (this.scale_ == scale) return; |
- this.scale_ = scale; |
- if (notify && this.scaleControl_) this.scaleControl_.displayScale(scale); |
-}; |
- |
-ImageBuffer.prototype.getFittingScale = function() { |
- var scaleX = this.screenCanvas_.width / this.imageCanvas_.width; |
- var scaleY = this.screenCanvas_.height / this.imageCanvas_.height; |
- return Math.min(scaleX, scaleY) * 0.85; |
-}; |
- |
-ImageBuffer.prototype.fitImage = function() { |
- var scale = this.getFittingScale(); |
- if (this.scaleControl_) this.scaleControl_.setMinScale(scale); |
- this.setScale(scale, true); |
-}; |
- |
-ImageBuffer.prototype.resizeScreen = function(width, height, keepFitting) { |
- var wasFitting = this.getScale() == this.getFittingScale(); |
- |
- this.screenCanvas_.width = width; |
- this.screenCanvas_.height = height; |
- |
- var minScale = this.getFittingScale(); |
- if (this.scaleControl_) this.scaleControl_.setMinScale(minScale); |
- if ((wasFitting && keepFitting) || this.getScale() < minScale) { |
- this.setScale(minScale, true); |
- } |
- this.repaint(); |
-}; |
- |
-ImageBuffer.prototype.getOffsetX = function () { return this.offsetX_; }; |
- |
-ImageBuffer.prototype.getOffsetY = function () { return this.offsetY_; }; |
- |
-ImageBuffer.prototype.setOffset = function(x, y, ignoreClipping) { |
- if (!ignoreClipping) { |
- var limitX = Math.max(0, -this.marginX_ / this.getScale()); |
- var limitY = Math.max(0, -this.marginY_ / this.getScale()); |
- x = ImageUtil.clip(-limitX, x, limitX); |
- y = ImageUtil.clip(-limitY, y, limitY); |
- } |
- if (this.offsetX_ == x && this.offsetY_ == y) return; |
- this.offsetX_ = x; |
- this.offsetY_ = y; |
-}; |
- |
-ImageBuffer.prototype.setCenter = function(x, y, ignoreClipping) { |
- this.setOffset( |
- this.imageWhole_.width / 2 - x, |
- this.imageWhole_.height / 2 - y, |
- ignoreClipping); |
-}; |
- |
-/** |
- * @return {Rect} The visible part of the image, in image coordinates. |
- */ |
-ImageBuffer.prototype.getClippedImage = function() { |
- return this.imageVisible_; |
-}; |
- |
-/** |
- * @return {Rect} The visible part of the image, in screen coordinates. |
- */ |
-ImageBuffer.prototype.getClippedScreen = function() { |
- return this.screenVisible_; |
-}; |
- |
-/** |
- * Returns a closure that can be called to pan the image. |
- * Useful for implementing non-trivial variants of panning (overview etc). |
- * @param {Number} originalX The x coordinate on the screen canvas that |
- * corresponds to zero change to offsetX. |
- * @param {Number} originalY The y coordinate on the screen canvas that |
- * corresponds to zero change to offsetY. |
- * @param {Function} scaleFunc returns the current image to screen scale. |
- * @param {Function} hitFunc returns true if (x,y) is in the valid region. |
- */ |
-ImageBuffer.prototype.createOffsetSetter_ = function ( |
- originalX, originalY, scaleFunc, hitFunc) { |
- var self = this; |
- var originalOffsetX = this.offsetX_; |
- var originalOffsetY = this.offsetY_; |
- if (!hitFunc) hitFunc = function() { return true; } |
- if (!scaleFunc) scaleFunc = this.getScale.bind(this); |
- return function(x, y) { |
- if (hitFunc(x, y)) { |
- var scale = scaleFunc(); |
- self.setOffset( |
- originalOffsetX + (x - originalX) / scale, |
- originalOffsetY + (y - originalY) / scale); |
- self.repaint(); |
- } |
- }; |
-}; |
- |
-/** |
- * @return {Boolean} True if the entire image is visible on the screen canvas. |
- */ |
-ImageBuffer.prototype.isFullyVisible = function () { |
- return this.marginX_ >= 0 && this.marginY_ >= 0; |
-}; |
- |
-ImageBuffer.prototype.updateViewPort = function () { |
- var scale = this.getScale(); |
- |
- this.screenWhole_ = new Rect(this.screenCanvas_); |
- this.imageWhole_ = new Rect(this.imageCanvas_); |
- |
- // Image bounds in screen coordinates. |
- this.imageOnScreen_ = {}; |
- |
- this.imageOnScreen_.width = Math.floor(this.imageWhole_.width * scale); |
- this.imageOnScreen_.height = Math.floor(this.imageWhole_.height * scale); |
- |
- this.marginX_ = Math.floor( |
- (this.screenCanvas_.width - this.imageOnScreen_.width) / 2); |
- this.marginY_ = Math.floor( |
- (this.screenCanvas_.height - this.imageOnScreen_.height) / 2); |
- |
- this.imageOnScreen_.left = this.marginX_; |
- this.imageOnScreen_.top = this.marginY_; |
- |
- this.imageVisible_ = new Rect(this.imageWhole_); |
- this.screenVisible_ = new Rect(this.screenWhole_); |
- |
- if (this.marginX_ < 0) { |
- this.imageOnScreen_.left += |
- ImageUtil.clip(this.marginX_, this.offsetX_ * scale, -this.marginX_); |
- this.imageVisible_.left = -this.imageOnScreen_.left / scale; |
- this.imageVisible_.width = this.screenCanvas_.width / scale; |
- } else { |
- this.screenVisible_.left = this.imageOnScreen_.left; |
- this.screenVisible_.width = this.imageOnScreen_.width; |
- } |
- |
- if (this.marginY_ < 0) { |
- this.imageOnScreen_.top += |
- ImageUtil.clip(this.marginY_, this.offsetY_ * scale, -this.marginY_); |
- this.imageVisible_.top = -this.imageOnScreen_.top / scale; |
- this.imageVisible_.height = this.screenCanvas_.height / scale; |
- } else { |
- this.screenVisible_.top = this.imageOnScreen_.top; |
- this.screenVisible_.height = this.imageOnScreen_.height; |
- } |
- |
- this.updateOverlays(); |
-}; |
- |
-/* |
- * Coordinate conversion between the screen canvas and the image. |
- */ |
- |
-ImageBuffer.prototype.screenToImageSize = function(size) { |
- return size / this.getScale(); |
-}; |
- |
-ImageBuffer.prototype.screenToImageX = function(x) { |
- return Math.round((x - this.imageOnScreen_.left) / this.getScale()); |
-}; |
- |
-ImageBuffer.prototype.screenToImageY = function(y) { |
- return Math.round((y - this.imageOnScreen_.top) / this.getScale()); |
-}; |
- |
-ImageBuffer.prototype.screenToImageRect = function(rect) { |
- return new Rect( |
- this.screenToImageX(rect.left), |
- this.screenToImageY(rect.top), |
- this.screenToImageSize(rect.width), |
- this.screenToImageSize(rect.height)); |
-}; |
- |
-ImageBuffer.prototype.imageToScreenSize = function(size) { |
- return size * this.getScale(); |
-}; |
- |
-ImageBuffer.prototype.imageToScreenX = function(x) { |
- return Math.round(this.imageOnScreen_.left + x * this.getScale()); |
-}; |
- |
-ImageBuffer.prototype.imageToScreenY = function(y) { |
- return Math.round(this.imageOnScreen_.top + y * this.getScale()); |
-}; |
- |
-ImageBuffer.prototype.imageToScreenRect = function(rect) { |
- return new Rect( |
- this.imageToScreenX(rect.left), |
- this.imageToScreenY(rect.top), |
- this.imageToScreenSize(rect.width), |
- this.imageToScreenSize(rect.height)); |
-}; |
+ImageBuffer.prototype.getViewport = function() { return this.viewport_ }; |
-/* |
- * Content manipulation. |
- */ |
+ImageBuffer.prototype.getContent = function() { return this.content_ }; |
/** |
* Loads the new content. |
@@ -241,123 +39,43 @@ ImageBuffer.prototype.load = function(source) { |
image.onload = function(e) { self.load(e.target); }; |
image.src = source; |
} else { |
- this.imageCanvas_.width = source.width, |
- this.imageCanvas_.height = source.height; |
- this.drawImage(source); |
- |
- if (this.scaleControl_) |
- this.scaleControl_.displayImageSize( |
- this.imageCanvas_.width, this.imageCanvas_.height); |
- this.fitImage(); |
+ this.content_.load(source); |
this.repaint(); |
} |
}; |
-ImageBuffer.prototype.getImageCanvas = function() { return this.imageCanvas_; }; |
- |
-/** |
- * Replaces the off-screen canvas. |
- * To be used when the editor modifies the image dimensions. |
- * @param {HTMLCanvasElement} canvas |
- */ |
-ImageBuffer.prototype.setImageCanvas = function(canvas) { |
- this.imageCanvas_ = canvas; |
- this.imageContext_ = canvas.getContext("2d"); |
- if (this.scaleControl_) |
- this.scaleControl_.displayImageSize( |
- this.imageCanvas_.width, this.imageCanvas_.height); |
-}; |
- |
-/** |
- * @return {HTMLCanvasElement} A new blank canvas of the required size. |
- */ |
-ImageBuffer.prototype.createBlankCanvas = function (width, height) { |
- var canvas = this.screenCanvas_.ownerDocument.createElement('canvas'); |
- canvas.width = width; |
- canvas.height = height; |
- return canvas; |
-}; |
- |
-/** |
- * @return {HTMLCanvasElement} A new canvas with a copy of the content. |
- */ |
-ImageBuffer.prototype.copyImageCanvas = function () { |
- var canvas = this.createBlankCanvas( |
- this.imageCanvas_.width, this.imageCanvas_.height); |
- canvas.getContext('2d').drawImage(this.imageCanvas_, 0, 0); |
- return canvas; |
-}; |
+ImageBuffer.prototype.resizeScreen = function(width, height, keepFitting) { |
+ this.screenCanvas_.width = width; |
+ this.screenCanvas_.height = height; |
-/** |
- * @return {ImageData} A new ImageData object with a copy of the content. |
- */ |
-ImageBuffer.prototype.copyImageData = function () { |
- return this.imageContext_.getImageData( |
- 0, 0, this.imageCanvas_.width, this.imageCanvas_.height); |
-}; |
+ var wasFitting = |
+ this.viewport_.getScale() == this.viewport_.getFittingScale(); |
-/** |
- * @param {HTMLImageElement|HTMLCanvasElement} image |
- */ |
-ImageBuffer.prototype.drawImage = function(image) { |
- ImageUtil.trace.resetTimer('drawImage'); |
- this.imageContext_.drawImage(image, 0, 0); |
- ImageUtil.trace.reportTimer('drawImage'); |
-}; |
+ this.viewport_.setScreenSize(width, height); |
-/** |
- * @param {ImageData} imageData |
- */ |
-ImageBuffer.prototype.drawImageData = function (imageData) { |
- ImageUtil.trace.resetTimer('putImageData'); |
- this.imageContext_.putImageData(imageData, 0, 0); |
- ImageUtil.trace.reportTimer('putImageData'); |
+ var minScale = this.viewport_.getFittingScale(); |
+ if ((wasFitting && keepFitting) || this.viewport_.getScale() < minScale) { |
+ this.viewport_.setScale(minScale, true); |
+ } |
+ this.repaint(); |
}; |
/** |
* Paints the content on the screen canvas taking the current scale and offset |
* into account. |
*/ |
-ImageBuffer.prototype.repaint = function () { |
- ImageUtil.trace.resetTimer('repaint'); |
- |
- this.updateViewPort(); |
- |
- this.screenContext_.save(); |
- |
- this.screenContext_.fillStyle = '#F0F0F0'; |
- this.screenContext_.strokeStyle = '#000000'; |
- |
- Rect.drawImage(this.screenContext_, this.imageCanvas_, |
- this.imageOnScreen_, this.imageWhole_); |
- Rect.fillBetween(this.screenContext_, this.imageOnScreen_, |
- this.screenWhole_); |
- Rect.stroke(this.screenContext_, this.imageOnScreen_); |
- |
- this.screenContext_.restore(); |
- |
- this.drawOverlays(this.screenContext_); |
- |
- ImageUtil.trace.reportTimer('repaint'); |
+ImageBuffer.prototype.repaint = function (opt_fromOverlay) { |
+ this.viewport_.update(); |
+ this.drawOverlays(this.screenCanvas_.getContext("2d"), opt_fromOverlay); |
}; |
-/** |
- * ImageBuffer.Overlay is a pluggable extension that modifies the outlook |
- * and the behavior of the ImageBuffer instance. |
- */ |
-ImageBuffer.Overlay = function() {}; |
- |
-ImageBuffer.Overlay.prototype.getZIndex = function() { return 0 }; |
- |
-ImageBuffer.Overlay.prototype.updateViewPort = function() {} |
- |
-ImageBuffer.Overlay.prototype.draw = function() {} |
- |
-ImageBuffer.Overlay.prototype.getCursorStyle = function() { return null }; |
- |
-ImageBuffer.Overlay.prototype.onClick = function() { return false }; |
- |
-ImageBuffer.Overlay.prototype.getDragHandler = function() { return null }; |
+ImageBuffer.prototype.repaintScreenRect = function (screenRect, imageRect) { |
+ Rect.drawImage( |
+ this.screenCanvas_.getContext('2d'), |
+ this.getContent().getCanvas(), |
+ screenRect || this.getViewport().imageToScreenRect(screenRect), |
+ imageRect || this.getViewport().screenToImageRect(screenRect)); |
+}; |
/** |
* @param {ImageBuffer.Overlay} overlay |
@@ -386,21 +104,18 @@ ImageBuffer.prototype.removeOverlay = function (overlay) { |
}; |
/** |
- * Updates viewport configuration on all overlays. |
- */ |
-ImageBuffer.prototype.updateOverlays = function (context) { |
- for (var i = 0; i != this.overlays_.length; i++) { |
- this.overlays_[i].updateViewPort(this); |
- } |
-} |
- |
-/** |
* Draws overlays in the ascending Z-order. |
+ * Skips overlays below opt_startFrom. |
*/ |
-ImageBuffer.prototype.drawOverlays = function (context) { |
+ImageBuffer.prototype.drawOverlays = function (context, opt_fromOverlay) { |
+ var skip = true; |
for (var i = 0; i != this.overlays_.length; i++) { |
+ var overlay = this.overlays_[i]; |
+ if (!opt_fromOverlay || opt_fromOverlay == overlay) skip = false; |
+ if (skip) continue; |
+ |
context.save(); |
- this.overlays_[i].draw(context); |
+ overlay.draw(context); |
context.restore(); |
} |
}; |
@@ -414,11 +129,6 @@ ImageBuffer.prototype.getCursorStyle = function (x, y, mouseDown) { |
var style = this.overlays_[i].getCursorStyle(x, y, mouseDown); |
if (style) return style; |
} |
- |
- // Indicate that the image is draggable. |
- if (!this.isFullyVisible() && this.screenVisible_.inside(x, y)) |
- return 'move'; |
- |
return 'default'; |
}; |
@@ -442,116 +152,279 @@ ImageBuffer.prototype.getDragHandler = function (x, y) { |
var handler = this.overlays_[i].getDragHandler(x, y); |
if (handler) return handler; |
} |
+ return null; |
+}; |
+ |
+/** |
+ * ImageBuffer.Overlay is a pluggable extension that modifies the outlook |
+ * and the behavior of the ImageBuffer instance. |
+ */ |
+ImageBuffer.Overlay = function() {}; |
+ |
+ImageBuffer.Overlay.prototype.getZIndex = function() { return 0 }; |
+ |
+ImageBuffer.Overlay.prototype.draw = function() {}; |
+ |
+ImageBuffer.Overlay.prototype.getCursorStyle = function() { return null }; |
+ |
+ImageBuffer.Overlay.prototype.onClick = function() { return false }; |
+ |
+ImageBuffer.Overlay.prototype.getDragHandler = function() { return null }; |
+ |
+ |
+/** |
+ * The margin overlay draws the image outline and paints the margins. |
+ */ |
+ImageBuffer.Margin = function(viewport) { |
+ this.viewport_ = viewport; |
+}; |
+ |
+ImageBuffer.Margin.prototype = {__proto__: ImageBuffer.Overlay.prototype}; |
+ |
+// Draw below everything including the content. |
+ImageBuffer.Margin.prototype.getZIndex = function() { return -2 }; |
+ |
+ImageBuffer.Margin.prototype.draw = function(context) { |
+ context.save(); |
+ context.fillStyle = '#F0F0F0'; |
+ context.strokeStyle = '#000000'; |
+ Rect.fillBetween(context, |
+ this.viewport_.getImageBoundsOnScreen(), |
+ this.viewport_.getScreenBounds()); |
+ Rect.stroke(context, this.viewport_.getImageBoundsOnScreen()); |
+ context.restore(); |
+}; |
+ |
+/** |
+ * The overlay containing the image. |
+ */ |
+ImageBuffer.Content = function(viewport, document) { |
+ this.viewport_ = viewport; |
+ this.document_ = document; |
+ |
+ this.generation_ = 0; |
+ |
+ this.setCanvas(this.createBlankCanvas(0, 0)); |
+}; |
+ |
+ImageBuffer.Content.prototype = {__proto__: ImageBuffer.Overlay.prototype}; |
+ |
+// Draw below overlays with the default zIndex. |
+ImageBuffer.Content.prototype.getZIndex = function() { return -1 }; |
+ |
+ImageBuffer.Content.prototype.draw = function(context) { |
+ Rect.drawImage( |
+ context, this.canvas_, this.viewport_.getImageBoundsOnScreen()); |
+}; |
+ |
+ImageBuffer.Content.prototype.getCursorStyle = function (x, y, mouseDown) { |
+ // Indicate that the image is draggable. |
+ if (this.viewport_.isClipped() && |
+ this.viewport_.getScreenClipped().inside(x, y)) |
+ return 'move'; |
+ |
+ return null; |
+}; |
- if (!this.isFullyVisible() && this.screenVisible_.inside(x, y)) { |
+ImageBuffer.Content.prototype.getDragHandler = function (x, y) { |
+ var cursor = this.getCursorStyle(x, y); |
+ if (cursor == 'move') { |
// Return the handler that drags the entire image. |
- return this.createOffsetSetter_(x, y, this.getScale.bind(this)); |
+ return this.viewport_.createOffsetSetter(x, y); |
} |
return null; |
}; |
+ImageBuffer.Content.prototype.getCacheGeneration = function() { |
+ return this.generation_; |
+}; |
+ |
+ImageBuffer.Content.prototype.invalidateCaches = function() { |
+ this.generation_++; |
+}; |
+ |
+ImageBuffer.Content.prototype.getCanvas = function() { return this.canvas_ }; |
+ |
/** |
- * Overview overlay draws the image thumbnail in the bottom right corner. |
- * Indicates the currently visible part. |
- * Supports panning by dragging. |
+ * Replaces the off-screen canvas. |
+ * To be used when the editor modifies the image dimensions. |
+ * If the logical width/height are supplied they override the canvas dimensions |
+ * and the canvas contents is scaled when displayed. |
+ * @param {HTMLCanvasElement} canvas |
+ * @param {Number} opt_width Logical width (=canvas.width by default) |
+ * @param {Number} opt_height Logical height (=canvas.height by default) |
*/ |
+ImageBuffer.Content.prototype.setCanvas = function( |
+ canvas, opt_width, opt_height) { |
+ this.canvas_ = canvas; |
+ this.viewport_.setImageSize(opt_width || canvas.width, |
+ opt_height || canvas.height); |
-ImageBuffer.Overview = function() {}; |
+ this.invalidateCaches(); |
+}; |
-ImageBuffer.Overview.MAX_SIZE = 150; |
-ImageBuffer.Overview.RIGHT = 7; |
-ImageBuffer.Overview.BOTTOM = 50; |
+/** |
+ * @return {HTMLCanvasElement} A new blank canvas of the required size. |
+ */ |
+ImageBuffer.Content.prototype.createBlankCanvas = function (width, height) { |
+ var canvas = this.document_.createElement('canvas'); |
+ canvas.width = width; |
+ canvas.height = height; |
+ return canvas; |
+}; |
+ |
+/** |
+ * @param {Number} opt_width Width of the copy, original width by default. |
+ * @param {Number} opt_height Height of the copy, original height by default. |
+ * @return {HTMLCanvasElement} A new canvas with a copy of the content. |
+ */ |
+ImageBuffer.Content.prototype.copyCanvas = function (opt_width, opt_height) { |
+ var canvas = this.createBlankCanvas(opt_width || this.canvas_.width, |
+ opt_height || this.canvas_.height); |
+ Rect.drawImage(canvas.getContext('2d'), this.canvas_); |
+ return canvas; |
+}; |
+ |
+/** |
+ * @return {ImageData} A new ImageData object with a copy of the content. |
+ */ |
+ImageBuffer.Content.prototype.copyImageData = function (opt_width, opt_height) { |
+ return this.canvas_.getContext("2d").getImageData( |
+ 0, 0, opt_width || this.canvas_.width, opt_height || this.canvas_.height); |
+}; |
+ |
+/** |
+ * @param {HTMLImageElement|HTMLCanvasElement} image |
+ */ |
+ImageBuffer.Content.prototype.load = function(image) { |
+ this.canvas_.width = image.width; |
+ this.canvas_.height = image.height; |
+ |
+ Rect.drawImage(this.canvas_.getContext("2d"), image); |
+ this.invalidateCaches(); |
+ |
+ this.viewport_.setImageSize(image.width, image.height); |
+ this.viewport_.fitImage(); |
+}; |
+ |
+/** |
+ * @param {ImageData} imageData |
+ */ |
+ImageBuffer.Content.prototype.drawImageData = function (imageData, x, y) { |
+ this.canvas_.getContext("2d").putImageData(imageData, x, y); |
+ this.invalidateCaches(); |
+}; |
+ |
+/** |
+ * The overview overlay draws the image thumbnail in the bottom right corner. |
+ * Indicates the currently visible part. Supports panning by dragging. |
+ */ |
+ImageBuffer.Overview = function(viewport, content) { |
+ this.viewport_ = viewport; |
+ this.content_ = content; |
+ this.contentGeneration_ = 0; |
+}; |
ImageBuffer.Overview.prototype = {__proto__: ImageBuffer.Overlay.prototype}; |
-ImageBuffer.Overview.prototype.getZIndex = function() { return 100; } |
+// Draw above everything. |
+ImageBuffer.Overview.prototype.getZIndex = function() { return 100 }; |
-ImageBuffer.Overview.prototype.updateViewPort = function(buffer) { |
- this.buffer_ = buffer; |
+ImageBuffer.Overview.MAX_SIZE = 150; |
+ImageBuffer.Overview.RIGHT = 7; |
+ImageBuffer.Overview.BOTTOM = 50; |
- this.whole_ = null; |
- this.visible_ = null; |
+ImageBuffer.Overview.prototype.update = function() { |
+ var imageBounds = this.viewport_.getImageBounds(); |
- if (this.buffer_.isFullyVisible()) return; |
+ if (this.contentGeneration_ != this.content_.getCacheGeneration()) { |
+ this.contentGeneration_ = this.content_.getCacheGeneration(); |
- var screenWhole = this.buffer_.screenWhole_; |
- var imageWhole = this.buffer_.imageWhole_; |
- var imageVisible = this.buffer_.imageVisible_; |
+ var aspect = imageBounds.width / imageBounds.height; |
+ if (aspect > 1) { |
+ this.bounds_ = new Rect(ImageBuffer.Overview.MAX_SIZE, |
+ ImageBuffer.Overview.MAX_SIZE / aspect); |
+ } else { |
+ this.bounds_ = new Rect(ImageBuffer.Overview.MAX_SIZE * aspect, |
+ ImageBuffer.Overview.MAX_SIZE); |
+ } |
- var aspect = imageWhole.width / imageWhole.height; |
- if (aspect > 1) { |
- this.whole_ = new Rect(ImageBuffer.Overview.MAX_SIZE, |
- ImageBuffer.Overview.MAX_SIZE / aspect); |
- } else { |
- this.whole_ = new Rect(ImageBuffer.Overview.MAX_SIZE * aspect, |
- ImageBuffer.Overview.MAX_SIZE); |
+ this.canvas_ = |
+ this.content_.copyCanvas(this.bounds_.width, this.bounds_.height); |
} |
- this.whole_ = this.whole_.moveTo( |
- screenWhole.width - ImageBuffer.Overview.RIGHT - this.whole_.width, |
- screenWhole.height - ImageBuffer.Overview.BOTTOM - this.whole_.height); |
+ this.clipped_ = null; |
+ |
+ if (this.viewport_.isClipped()) { |
+ var screenBounds = this.viewport_.getScreenBounds(); |
- this.scale_ = this.whole_.width / imageWhole.width; |
+ this.bounds_ = this.bounds_.moveTo( |
+ screenBounds.width - ImageBuffer.Overview.RIGHT - this.bounds_.width, |
+ screenBounds.height - ImageBuffer.Overview.BOTTOM - |
+ this.bounds_.height); |
- this.visible_ = imageVisible. |
- scale(this.scale_). |
- shift(this.whole_.left, this.whole_.top); |
+ this.scale_ = this.bounds_.width / imageBounds.width; |
+ |
+ this.clipped_ = this.viewport_.getImageClipped(). |
+ scale(this.scale_). |
+ shift(this.bounds_.left, this.bounds_.top); |
+ } |
}; |
ImageBuffer.Overview.prototype.draw = function(context) { |
- if (!this.visible_) return; |
+ this.update(); |
+ |
+ if (!this.clipped_) return; |
// Draw the thumbnail. |
- Rect.drawImage(context, this.buffer_.imageCanvas_, |
- this.whole_, this.buffer_.imageWhole_); |
+ Rect.drawImage(context, this.canvas_, this.bounds_); |
// Draw the thumbnail border. |
context.strokeStyle = '#000000'; |
- Rect.stroke(context, this.whole_); |
+ Rect.stroke(context, this.bounds_); |
// Draw the shadow over the off-screen part of the thumbnail. |
context.globalAlpha = 0.3; |
context.fillStyle = '#000000'; |
- Rect.fillBetween(context, this.visible_, this.whole_); |
+ Rect.fillBetween(context, this.clipped_, this.bounds_); |
// Outline the on-screen part of the thumbnail. |
context.strokeStyle = '#FFFFFF'; |
- Rect.stroke(context, this.visible_); |
+ Rect.stroke(context, this.clipped_); |
}; |
ImageBuffer.Overview.prototype.getCursorStyle = function(x, y) { |
- if (!this.whole_ || !this.whole_.inside(x, y)) return null; |
+ if (!this.bounds_ || !this.bounds_.inside(x, y)) return null; |
// Indicate that the on-screen part is draggable. |
- if (this.visible_.inside(x, y)) return 'move'; |
+ if (this.clipped_.inside(x, y)) return 'move'; |
- // Indicathe that the rest of the thumbnail is clickable. |
+ // Indicate that the rest of the thumbnail is clickable. |
return 'crosshair'; |
}; |
ImageBuffer.Overview.prototype.onClick = function(x, y) { |
- if (!this.whole_ || !this.whole_.inside(x, y)) return false; |
- |
- if (this.visible_.inside(x, y)) return false; |
- |
- this.buffer_.setCenter( |
- (x - this.whole_.left) / this.scale_, |
- (y - this.whole_.top) / this.scale_); |
- this.buffer_.repaint(); |
+ if (this.getCursorStyle(x, y) != 'crosshair') return false; |
+ this.viewport_.setCenter( |
+ (x - this.bounds_.left) / this.scale_, |
+ (y - this.bounds_.top) / this.scale_); |
+ this.viewport_.repaint(); |
return true; |
}; |
ImageBuffer.Overview.prototype.getDragHandler = function(x, y) { |
- if (!this.whole_ || !this.whole_.inside(x, y)) return null; |
+ var cursor = this.getCursorStyle(x, y); |
- if (this.visible_.inside(x, y)) { |
+ if (cursor == 'move') { |
var self = this; |
function scale() { return -self.scale_;} |
- function hit(x, y) { return self.whole_ && self.whole_.inside(x, y); } |
- return this.buffer_.createOffsetSetter_(x, y, scale, hit); |
- } else { |
+ function hit(x, y) { return self.bounds_ && self.bounds_.inside(x, y); } |
+ return this.viewport_.createOffsetSetter(x, y, scale, hit); |
+ } else if (cursor == 'crosshair') { |
// Force non-draggable behavior. |
return function() {}; |
+ } else { |
+ return null; |
} |
}; |