Merge pull request #927 from avandecreme/perf

Optimize sketch canvas clearing and blending.
This commit is contained in:
Ian Gilman 2016-04-29 10:08:47 -07:00
commit 07429f5890
4 changed files with 105 additions and 39 deletions

View File

@ -259,13 +259,17 @@ $.Drawer.prototype = {
} }
}, },
_clear: function ( useSketch ) { _clear: function (useSketch, bounds) {
if ( !this.useCanvas ) { if (!this.useCanvas) {
return; return;
} }
var context = this._getContext( useSketch ); var context = this._getContext(useSketch);
var canvas = context.canvas; if (bounds) {
context.clearRect( 0, 0, canvas.width, canvas.height ); context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
} else {
var canvas = context.canvas;
context.clearRect(0, 0, canvas.width, canvas.height);
}
}, },
/** /**
@ -382,47 +386,80 @@ $.Drawer.prototype = {
/** /**
* Blends the sketch canvas in the main canvas. * Blends the sketch canvas in the main canvas.
* @param {Float} opacity The opacity of the blending. * @param {Object} options The options
* @param {Float} [scale=1] The scale at which tiles were drawn on the sketch. Default is 1. * @param {Float} options.opacity The opacity of the blending.
* Use scale to draw at a lower scale and then enlarge onto the main canvas. * @param {Float} [options.scale=1] The scale at which tiles were drawn on
* @param {OpenSeadragon.Point} [translate] A translation vector that was used to draw the tiles * the sketch. Default is 1.
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values. * Use scale to draw at a lower scale and then enlarge onto the main canvas.
* @returns {undefined} * @param {OpenSeadragon.Point} [options.translate] A translation vector
* that was used to draw the tiles
* @param {String} [options.compositeOperation] - How the image is
* composited onto other images; see compositeOperation in
* {@link OpenSeadragon.Options} for possible values.
* @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
* canvas to blend in the main canvas. If specified, options.scale and
* options.translate get ignored.
*/ */
blendSketch: function(opacity, scale, translate, compositeOperation) { blendSketch: function(opacity, scale, translate, compositeOperation) {
var options = opacity;
if (!$.isPlainObject(options)) {
options = {
opacity: opacity,
scale: scale,
translate: translate,
compositeOperation: compositeOperation
};
}
if (!this.useCanvas || !this.sketchCanvas) { if (!this.useCanvas || !this.sketchCanvas) {
return; return;
} }
scale = scale || 1; opacity = options.opacity;
var position = translate instanceof $.Point ? compositeOperation = options.compositeOperation;
translate : var bounds = options.bounds;
new $.Point(0, 0);
var widthExt = 0;
var heightExt = 0;
if (translate) {
var widthDiff = this.sketchCanvas.width - this.canvas.width;
var heightDiff = this.sketchCanvas.height - this.canvas.height;
widthExt = Math.round(widthDiff / 2);
heightExt = Math.round(heightDiff / 2);
}
this.context.save(); this.context.save();
this.context.globalAlpha = opacity; this.context.globalAlpha = opacity;
if (compositeOperation) { if (compositeOperation) {
this.context.globalCompositeOperation = compositeOperation; this.context.globalCompositeOperation = compositeOperation;
} }
this.context.drawImage( if (bounds) {
this.sketchCanvas, this.context.drawImage(
position.x - widthExt * scale, this.sketchCanvas,
position.y - heightExt * scale, bounds.x,
(this.canvas.width + 2 * widthExt) * scale, bounds.y,
(this.canvas.height + 2 * heightExt) * scale, bounds.width,
-widthExt, bounds.height,
-heightExt, bounds.x,
this.canvas.width + 2 * widthExt, bounds.y,
this.canvas.height + 2 * heightExt bounds.width,
); bounds.height
);
} else {
scale = options.scale || 1;
translate = options.translate;
var position = translate instanceof $.Point ?
translate : new $.Point(0, 0);
var widthExt = 0;
var heightExt = 0;
if (translate) {
var widthDiff = this.sketchCanvas.width - this.canvas.width;
var heightDiff = this.sketchCanvas.height - this.canvas.height;
widthExt = Math.round(widthDiff / 2);
heightExt = Math.round(heightDiff / 2);
}
this.context.drawImage(
this.sketchCanvas,
position.x - widthExt * scale,
position.y - heightExt * scale,
(this.canvas.width + 2 * widthExt) * scale,
(this.canvas.height + 2 * heightExt) * scale,
-widthExt,
-heightExt,
this.canvas.width + 2 * widthExt,
this.canvas.height + 2 * heightExt
);
}
this.context.restore(); this.context.restore();
}, },

View File

@ -367,6 +367,20 @@ $.Rect.prototype = {
maxY - minY); maxY - minY);
}, },
/**
* Retrieves the smallest horizontal (degrees=0) rectangle which contains
* this rectangle and has integers x, y, width and height
* @returns {OpenSeadragon.Rect}
*/
getIntegerBoundingBox: function() {
var boundingBox = this.getBoundingBox();
var x = Math.floor(boundingBox.x);
var y = Math.floor(boundingBox.y);
var width = Math.ceil(boundingBox.width + boundingBox.x - x);
var height = Math.ceil(boundingBox.height + boundingBox.y - y);
return new $.Rect(x, y, width, height);
},
/** /**
* Provides a string representation of the rectangle which is useful for * Provides a string representation of the rectangle which is useful for
* debugging. * debugging.

View File

@ -195,7 +195,7 @@ $.Tile.prototype = {
// private // private
_hasTransparencyChannel: function() { _hasTransparencyChannel: function() {
return this.context2D || this.url.match('.png'); return !!this.context2D || this.url.match('.png');
}, },
/** /**

View File

@ -1448,8 +1448,17 @@ function drawTiles( tiledImage, lastDrawn ) {
tiledImage._drawer.getCanvasSize(true)); tiledImage._drawer.getCanvasSize(true));
} }
if ( useSketch ) { var bounds;
tiledImage._drawer._clear( true ); if (useSketch) {
if (!sketchScale) {
// Except when edge smoothing, we only clean the part of the
// sketch canvas we are going to use for performance reasons.
bounds = tiledImage.viewport.viewportToViewerElementRectangle(
tiledImage.getClippedBounds(true))
.getIntegerBoundingBox()
.times($.pixelDensityRatio);
}
tiledImage._drawer._clear(true, bounds);
} }
// When scaling, we must rotate only when blending the sketch canvas to avoid // When scaling, we must rotate only when blending the sketch canvas to avoid
@ -1532,7 +1541,13 @@ function drawTiles( tiledImage, lastDrawn ) {
if (offsetForRotation) { if (offsetForRotation) {
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false); tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
} }
tiledImage._drawer.blendSketch(tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation); tiledImage._drawer.blendSketch({
opacity: tiledImage.opacity,
scale: sketchScale,
translate: sketchTranslate,
compositeOperation: tiledImage.compositeOperation,
bounds: bounds
});
if (offsetForRotation) { if (offsetForRotation) {
tiledImage._drawer._restoreRotationChanges(false); tiledImage._drawer._restoreRotationChanges(false);
} }