Merge pull request #825 from avandecreme/rotation

Fix edge smoothing with rotation.
This commit is contained in:
Ian Gilman 2016-01-28 11:27:31 -08:00
commit a244d7ef86
3 changed files with 86 additions and 21 deletions

View File

@ -250,8 +250,9 @@ $.Drawer.prototype = {
this.canvas.width = viewportSize.x; this.canvas.width = viewportSize.x;
this.canvas.height = viewportSize.y; this.canvas.height = viewportSize.y;
if ( this.sketchCanvas !== null ) { if ( this.sketchCanvas !== null ) {
this.sketchCanvas.width = this.canvas.width; var sketchCanvasSize = this._calculateSketchCanvasSize();
this.sketchCanvas.height = this.canvas.height; this.sketchCanvas.width = sketchCanvasSize.x;
this.sketchCanvas.height = sketchCanvasSize.y;
} }
} }
this._clear(); this._clear();
@ -313,9 +314,23 @@ $.Drawer.prototype = {
if ( useSketch ) { if ( useSketch ) {
if (this.sketchCanvas === null) { if (this.sketchCanvas === null) {
this.sketchCanvas = document.createElement( "canvas" ); this.sketchCanvas = document.createElement( "canvas" );
this.sketchCanvas.width = this.canvas.width; var sketchCanvasSize = this._calculateSketchCanvasSize();
this.sketchCanvas.height = this.canvas.height; this.sketchCanvas.width = sketchCanvasSize.x;
this.sketchCanvas.height = sketchCanvasSize.y;
this.sketchContext = this.sketchCanvas.getContext( "2d" ); 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; context = this.sketchContext;
} }
@ -383,6 +398,15 @@ $.Drawer.prototype = {
translate : translate :
new $.Point(0, 0); 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) {
@ -390,14 +414,14 @@ $.Drawer.prototype = {
} }
this.context.drawImage( this.context.drawImage(
this.sketchCanvas, this.sketchCanvas,
position.x, position.x - widthExt * scale,
position.y, position.y - heightExt * scale,
this.sketchCanvas.width * scale, (this.canvas.width + 2 * widthExt) * scale,
this.sketchCanvas.height * scale, (this.canvas.height + 2 * heightExt) * scale,
0, -widthExt,
0, -heightExt,
this.canvas.width, this.canvas.width + 2 * widthExt,
this.canvas.height this.canvas.height + 2 * heightExt
); );
this.context.restore(); 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 // private
_offsetForRotation: function(degrees, useSketch) { _offsetForRotation: function(degrees, useSketch) {
var cx = this.canvas.width / 2; var cx = this.canvas.width / 2;
@ -530,6 +564,23 @@ $.Drawer.prototype = {
x: viewportSize.x * pixelDensityRatio, x: viewportSize.x * pixelDensityRatio,
y: viewportSize.y * 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
};
} }
}; };

View File

@ -351,11 +351,14 @@ $.Tile.prototype = {
* @param {Number} [scale=1] - Scale to be applied to position. * @param {Number} [scale=1] - Scale to be applied to position.
* @return {OpenSeadragon.Point} * @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 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 // 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. // to the main canvas. In that case, some browsers throw:
return new $.Point(1, 1).minus( // 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 this.position
.times($.pixelDensityRatio) .times($.pixelDensityRatio)
.times(scale || 1) .times(scale || 1)

View File

@ -1337,19 +1337,23 @@ function drawTiles( tiledImage, lastDrawn ) {
var zoom = tiledImage.viewport.getZoom(true); var zoom = tiledImage.viewport.getZoom(true);
var imageZoom = tiledImage.viewportToImageZoom(zoom); 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. // When zoomed in a lot (>100%) the tile edges are visible.
// So we have to composite them at ~100% and scale them up together. // So we have to composite them at ~100% and scale them up together.
useSketch = true; useSketch = true;
sketchScale = tile.getScaleForEdgeSmoothing(); sketchScale = tile.getScaleForEdgeSmoothing();
sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale); sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
tiledImage._drawer.getCanvasSize(false),
tiledImage._drawer.getCanvasSize(true));
} }
if ( useSketch ) { if ( useSketch ) {
tiledImage._drawer._clear( true ); 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); tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, useSketch);
} }
@ -1418,12 +1422,19 @@ function drawTiles( tiledImage, lastDrawn ) {
tiledImage._drawer.restoreContext( useSketch ); tiledImage._drawer.restoreContext( useSketch );
} }
if (tiledImage.viewport.degrees !== 0) { if (tiledImage.viewport.degrees !== 0 && !sketchScale) {
tiledImage._drawer._restoreRotationChanges(useSketch); tiledImage._drawer._restoreRotationChanges(useSketch);
} }
if ( useSketch ) { if (useSketch) {
tiledImage._drawer.blendSketch( tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation ); 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 ); drawDebugInfo( tiledImage, lastDrawn );
} }