diff --git a/src/drawer.js b/src/drawer.js
index 5666370d..a28606b1 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -290,22 +290,24 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
* drawingHandler({context, tile, rendered})
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
* where rendered
is the context with the pre-drawn image.
- * @param {Float} scale - Apply a scale to tile position and size
+ * @param {Float} scale - Apply a scale to tile position and size. Defaults to 1.
+ * @param {OpenSeadragon.Point} translate Optional. A translation vector to offset tile position
*/
- drawTile: function( tile, drawingHandler, useSketch, scale ) {
+ drawTile: function( tile, drawingHandler, useSketch, scale, translate ) {
$.console.assert(tile, '[Drawer.drawTile] tile is required');
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
if ( this.useCanvas ) {
var context = this._getContext( useSketch );
+ scale = scale || 1;
// TODO do this in a more performant way
// specifically, don't save,rotate,restore every time we draw a tile
if( this.viewport.degrees !== 0 ) {
this._offsetForRotation( tile, this.viewport.degrees, useSketch );
- tile.drawCanvas( context, drawingHandler, scale );
+ tile.drawCanvas( context, drawingHandler, scale, translate );
this._restoreRotationChanges( tile, useSketch );
} else {
- tile.drawCanvas( context, drawingHandler, scale );
+ tile.drawCanvas( context, drawingHandler, scale, translate );
}
} else {
tile.drawHTML( this.canvas );
@@ -372,24 +374,28 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
/**
* Blends the sketch canvas in the main canvas.
* @param {Float} opacity The opacity of the blending.
- * @param {Float} sketchScale The scale at which tiles were drawn on the sketch. Default is 1.
- * Use sketchScale to draw at a lower scale and then enlarge onto the main canvas.
+ * @param {Float} scale 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
* @returns {undefined}
*/
- blendSketch: function(opacity, sketchScale) {
+ blendSketch: function(opacity, scale, translate) {
if (!this.useCanvas || !this.sketchCanvas) {
return;
}
- sketchScale = sketchScale || 1;
+ scale = scale || 1;
+ var position = translate instanceof $.Point ?
+ translate :
+ new $.Point(0, 0);
this.context.save();
this.context.globalAlpha = opacity;
this.context.drawImage(
this.sketchCanvas,
- 0,
- 0,
- this.sketchCanvas.width * sketchScale,
- this.sketchCanvas.height * sketchScale,
+ position.x,
+ position.y,
+ this.sketchCanvas.width * scale,
+ this.sketchCanvas.height * scale,
0,
0,
this.canvas.width,
diff --git a/src/openseadragon.js b/src/openseadragon.js
index df4dd55f..b3b1bd6e 100644
--- a/src/openseadragon.js
+++ b/src/openseadragon.js
@@ -250,9 +250,9 @@
* availble on the viewing device.
*
* @property {Number} [smoothTileEdgesMinZoom=1.1]
- * A zoom percentage ( expressed as a number between 0 and 1 ) of the highest
- * resolution level. When zoomed in beyond this value alternative compositing will
- * be used to smooth out the edges between tiles. This WILL have a performance impact.
+ * A zoom percentage ( where 1 is 100% ) of the highest resolution level.
+ * When zoomed in beyond this value alternative compositing will be used to
+ * smooth out the edges between tiles. This will have a performance impact.
*
* @property {Boolean} [autoResize=true]
* Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
diff --git a/src/tile.js b/src/tile.js
index 9a7fde55..e5d5be50 100644
--- a/src/tile.js
+++ b/src/tile.js
@@ -241,8 +241,9 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
* drawingHandler({context, tile, rendered})
* where rendered
is the context with the pre-drawn image.
* @param {Number} scale - Apply a scale to position and size
+ * @param {OpenSeadragon.Point} translate - A translation vector
*/
- drawCanvas: function( context, drawingHandler, scale ) {
+ drawCanvas: function( context, drawingHandler, scale, translate ) {
var position = this.position.times($.pixelDensityRatio),
size = this.size.times($.pixelDensityRatio),
@@ -294,38 +295,13 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
// draw tile at a different scale
position = position.times(scale);
size = size.times(scale);
-
- if (scale < 1 && $.Browser.vendor == $.BROWSERS.FIREFOX) {
- // In firefox edges are very visible because there seems to be
- // empty space between tiles caused by float coordinates.
- // Adding partial overlap fixes this.
- // These will be covered by the top and left tiles.
- context.drawImage( // duplicate first column to the left
- rendered.canvas,
- 0,
- 0,
- 1,
- rendered.canvas.height,
- Math.floor(position.x),
- position.y,
- 1,
- size.y
- );
- context.drawImage( // duplicate first row up
- rendered.canvas,
- 0,
- 0,
- rendered.canvas.width,
- 1,
- position.x,
- Math.floor(position.y),
- size.x,
- 1
- );
- }
}
- // context.globalCompositeOperation = 'source-out';
+ if (translate instanceof $.Point) {
+ // shift tile position slightly
+ position = position.plus(translate);
+ }
+
context.drawImage(
rendered.canvas,
0,
@@ -341,6 +317,43 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
context.restore();
},
+ /**
+ * Get the ratio between current and original size.
+ * @function
+ * @return {Float}
+ */
+ getScaleForEdgeSmoothing: function() {
+ if (!this.cacheImageRecord) {
+ $.console.warn(
+ '[Tile.drawCanvas] attempting to get tile scale %s when tile\'s not cached',
+ this.toString());
+ return 1;
+ }
+
+ var rendered = this.cacheImageRecord.getRenderedContext();
+ return rendered.canvas.width / this.size.times($.pixelDensityRatio).x;
+ },
+
+ /**
+ * Get a translation vector that when applied to the tile position produces integer coordinates.
+ * Needed to avoid swimming and twitching.
+ * @function
+ * @param {Number} scale - Scale to be applied to position. Defaults to 1.
+ * @return {OpenSeadragon.Point}
+ */
+ getTranslationForEdgeSmoothing: function(scale) {
+ // 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(
+ this.position
+ .times(scale || 1)
+ .apply(function(x) {
+ return x % 1;
+ })
+ );
+ },
+
/**
* Removes tile from its container.
* @function
diff --git a/src/tiledimage.js b/src/tiledimage.js
index 815d052c..d349658e 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -64,6 +64,7 @@
* @param {Number} [options.blendTime] - See {@link OpenSeadragon.Options}.
* @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.
* @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.
+ * @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.
* @param {Number} [options.opacity=1] - Opacity the tiled image should be drawn at.
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
* @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
@@ -1296,24 +1297,24 @@ function compareTiles( previousBest, tile ) {
function drawTiles( tiledImage, lastDrawn ) {
var i,
- tile;
+ tile = lastDrawn[0];
if ( tiledImage.opacity <= 0 ) {
drawDebugInfo( tiledImage, lastDrawn );
return;
}
var useSketch = tiledImage.opacity < 1;
- var sketchScale = 1;
+ var sketchScale;
+ var sketchTranslate;
- var zoom = tiledImage.viewport.getZoom();
+ var zoom = tiledImage.viewport.getZoom(true);
var imageZoom = tiledImage.viewportToImageZoom(zoom);
- if ( imageZoom > tiledImage.smoothTileEdgesMinZoom ) {
+ 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;
- // Compositing at 100% is not precise and causes weird twithing.
- // So we composite at 101% zoom
- sketchScale = 1.01 / imageZoom;
+ sketchScale = tile.getScaleForEdgeSmoothing();
+ sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale);
}
if ( useSketch ) {
@@ -1347,7 +1348,7 @@ function drawTiles( tiledImage, lastDrawn ) {
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
tile = lastDrawn[ i ];
- tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale );
+ tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate );
tile.beingDrawn = true;
if( tiledImage.viewer ){
@@ -1374,7 +1375,7 @@ function drawTiles( tiledImage, lastDrawn ) {
}
if ( useSketch ) {
- tiledImage._drawer.blendSketch( tiledImage.opacity, sketchScale );
+ tiledImage._drawer.blendSketch( tiledImage.opacity, sketchScale, sketchTranslate );
}
drawDebugInfo( tiledImage, lastDrawn );
}
diff --git a/test/modules/basic.js b/test/modules/basic.js
index 7f095777..eae33474 100644
--- a/test/modules/basic.js
+++ b/test/modules/basic.js
@@ -334,6 +334,7 @@
asyncTest( 'CrossOriginPolicyMissing', function () {
viewer.crossOriginPolicy = false;
+ viewer.smoothTileEdgesMinZoom = Infinity;
viewer.open( {
type: 'legacy-image-pyramid',
levels: [ {