diff --git a/src/drawer.js b/src/drawer.js index 05f45803..1677dc59 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -250,8 +250,9 @@ $.Drawer.prototype = { this.canvas.width = viewportSize.x; this.canvas.height = viewportSize.y; if ( this.sketchCanvas !== null ) { - this.sketchCanvas.width = this.canvas.width; - this.sketchCanvas.height = this.canvas.height; + var sketchCanvasSize = this._calculateSketchCanvasSize(); + this.sketchCanvas.width = sketchCanvasSize.x; + this.sketchCanvas.height = sketchCanvasSize.y; } } this._clear(); @@ -313,9 +314,23 @@ $.Drawer.prototype = { if ( useSketch ) { if (this.sketchCanvas === null) { this.sketchCanvas = document.createElement( "canvas" ); - this.sketchCanvas.width = this.canvas.width; - this.sketchCanvas.height = this.canvas.height; + var sketchCanvasSize = this._calculateSketchCanvasSize(); + this.sketchCanvas.width = sketchCanvasSize.x; + this.sketchCanvas.height = sketchCanvasSize.y; this.sketchContext = this.sketchCanvas.getContext( "2d" ); + + // If the viewport is not currently rotated, the sketchCanvas + // will have the same size as the main canvas. However, if + // the viewport get rotated later on, we will need to resize it. + if (this.viewport.getRotation() === 0) { + var self = this; + this.viewer.addHandler('rotate', function resizeSketchCanvas() { + self.viewer.removeHandler('rotate', resizeSketchCanvas); + var sketchCanvasSize = self._calculateSketchCanvasSize(); + self.sketchCanvas.width = sketchCanvasSize.x; + self.sketchCanvas.height = sketchCanvasSize.y; + }); + } } context = this.sketchContext; } @@ -383,6 +398,15 @@ $.Drawer.prototype = { 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.save(); this.context.globalAlpha = opacity; if (compositeOperation) { @@ -390,14 +414,14 @@ $.Drawer.prototype = { } this.context.drawImage( this.sketchCanvas, - position.x, - position.y, - this.sketchCanvas.width * scale, - this.sketchCanvas.height * scale, - 0, - 0, - this.canvas.width, - this.canvas.height + 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(); }, @@ -503,6 +527,16 @@ $.Drawer.prototype = { } }, + /** + * Get the canvas size + * @param {Boolean} sketch If set to true return the size of the sketch canvas + * @returns {OpenSeadragon.Point} The size of the canvas + */ + getCanvasSize: function(sketch) { + var canvas = this._getContext(sketch).canvas; + return new $.Point(canvas.width, canvas.height); + }, + // private _offsetForRotation: function(degrees, useSketch) { var cx = this.canvas.width / 2; @@ -530,6 +564,23 @@ $.Drawer.prototype = { x: viewportSize.x * pixelDensityRatio, y: viewportSize.y * pixelDensityRatio }; + }, + + // private + _calculateSketchCanvasSize: function() { + var canvasSize = this._calculateCanvasSize(); + if (this.viewport.getRotation() === 0) { + return canvasSize; + } + // If the viewport is rotated, we need a larger sketch canvas in order + // to support edge smoothing. + var sketchCanvasSize = Math.ceil(Math.sqrt( + canvasSize.x * canvasSize.x + + canvasSize.y * canvasSize.y)); + return { + x: sketchCanvasSize, + y: sketchCanvasSize + }; } }; diff --git a/src/tile.js b/src/tile.js index 30eac238..9abb14b6 100644 --- a/src/tile.js +++ b/src/tile.js @@ -351,11 +351,14 @@ $.Tile.prototype = { * @param {Number} [scale=1] - Scale to be applied to position. * @return {OpenSeadragon.Point} */ - getTranslationForEdgeSmoothing: function(scale) { + getTranslationForEdgeSmoothing: function(scale, canvasSize, sketchCanvasSize) { // The translation vector must have positive values, otherwise the image goes a bit off // the sketch canvas to the top and left and we must use negative coordinates to repaint it - // to the main canvas. And FF does not like it. It crashes the viewer. - return new $.Point(1, 1).minus( + // to the main canvas. In that case, some browsers throw: + // INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value. + var x = Math.max(1, Math.ceil((sketchCanvasSize.x - canvasSize.x) / 2)); + var y = Math.max(1, Math.ceil((sketchCanvasSize.y - canvasSize.y) / 2)); + return new $.Point(x, y).minus( this.position .times($.pixelDensityRatio) .times(scale || 1) diff --git a/src/tiledimage.js b/src/tiledimage.js index dadeb5de..7772c186 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1337,19 +1337,23 @@ function drawTiles( tiledImage, lastDrawn ) { var zoom = tiledImage.viewport.getZoom(true); var imageZoom = tiledImage.viewportToImageZoom(zoom); - if ( imageZoom > tiledImage.smoothTileEdgesMinZoom && tile) { + if (imageZoom > tiledImage.smoothTileEdgesMinZoom && tile) { // When zoomed in a lot (>100%) the tile edges are visible. // So we have to composite them at ~100% and scale them up together. useSketch = true; sketchScale = tile.getScaleForEdgeSmoothing(); - sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale); + sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale, + tiledImage._drawer.getCanvasSize(false), + tiledImage._drawer.getCanvasSize(true)); } if ( useSketch ) { tiledImage._drawer._clear( true ); } - if (tiledImage.viewport.degrees !== 0) { + // When scaling, we must rotate only when blending the sketch canvas to avoid + // interpolation + if (tiledImage.viewport.degrees !== 0 && !sketchScale) { tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, useSketch); } @@ -1418,12 +1422,19 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.restoreContext( useSketch ); } - if (tiledImage.viewport.degrees !== 0) { + if (tiledImage.viewport.degrees !== 0 && !sketchScale) { tiledImage._drawer._restoreRotationChanges(useSketch); } - if ( useSketch ) { - tiledImage._drawer.blendSketch( tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation ); + if (useSketch) { + var offsetForRotation = tiledImage.viewport.degrees !== 0 && sketchScale; + if (offsetForRotation) { + tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false); + } + tiledImage._drawer.blendSketch(tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation); + if (offsetForRotation) { + tiledImage._drawer._restoreRotationChanges(false); + } } drawDebugInfo( tiledImage, lastDrawn ); }