From 684029bc791a8a3bce2bb3e78f7b3f7f093bac1e Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 27 Apr 2016 11:08:44 -0400 Subject: [PATCH] Optimize sketch canvas clearing and blending. --- src/drawer.js | 107 +++++++++++++++++++++++++++++++--------------- src/rectangle.js | 14 ++++++ src/tile.js | 2 +- src/tiledimage.js | 19 ++++++-- 4 files changed, 103 insertions(+), 39 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 1677dc59..661663d1 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -259,13 +259,17 @@ $.Drawer.prototype = { } }, - _clear: function ( useSketch ) { - if ( !this.useCanvas ) { + _clear: function (useSketch, bounds) { + if (!this.useCanvas) { return; } - var context = this._getContext( useSketch ); - var canvas = context.canvas; - context.clearRect( 0, 0, canvas.width, canvas.height ); + var context = this._getContext(useSketch); + if (bounds) { + 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. - * @param {Float} opacity The opacity of the blending. - * @param {Float} [scale=1] The scale at which tiles were drawn on the sketch. Default is 1. - * Use scale to draw at a lower scale and then enlarge onto the main canvas. - * @param {OpenSeadragon.Point} [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. - * @returns {undefined} + * @param {Object} options The options + * @param {Float} options.opacity The opacity of the blending. + * @param {Float} [options.scale=1] The scale at which tiles were drawn on + * the sketch. Default is 1. + * Use scale to draw at a lower scale and then enlarge onto the main canvas. + * @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) { + var options = opacity; + if (!$.isPlainObject(options)) { + options = { + opacity: opacity, + scale: scale, + translate: translate, + compositeOperation: compositeOperation + }; + } if (!this.useCanvas || !this.sketchCanvas) { return; } - scale = scale || 1; - 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); - } + opacity = options.opacity; + compositeOperation = options.compositeOperation; + var bounds = options.bounds; this.context.save(); this.context.globalAlpha = opacity; if (compositeOperation) { this.context.globalCompositeOperation = compositeOperation; } - 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 - ); + if (bounds) { + this.context.drawImage( + this.sketchCanvas, + bounds.x, + bounds.y, + bounds.width, + bounds.height, + bounds.x, + bounds.y, + 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(); }, diff --git a/src/rectangle.js b/src/rectangle.js index afe5da18..6223c12c 100644 --- a/src/rectangle.js +++ b/src/rectangle.js @@ -367,6 +367,20 @@ $.Rect.prototype = { 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 * debugging. diff --git a/src/tile.js b/src/tile.js index d9c391ce..04f3c08a 100644 --- a/src/tile.js +++ b/src/tile.js @@ -195,7 +195,7 @@ $.Tile.prototype = { // private _hasTransparencyChannel: function() { - return this.context2D || this.url.match('.png'); + return !!this.context2D || this.url.match('.png'); }, /** diff --git a/src/tiledimage.js b/src/tiledimage.js index 9d4db782..67c2a7f3 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1448,8 +1448,15 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.getCanvasSize(true)); } - if ( useSketch ) { - tiledImage._drawer._clear( true ); + var bounds; + 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(); + } + tiledImage._drawer._clear(true, bounds); } // When scaling, we must rotate only when blending the sketch canvas to avoid @@ -1532,7 +1539,13 @@ function drawTiles( tiledImage, lastDrawn ) { if (offsetForRotation) { 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) { tiledImage._drawer._restoreRotationChanges(false); }