diff --git a/src/drawer.js b/src/drawer.js
index 9b74b981..5666370d 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -290,8 +290,9 @@ $.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
*/
- drawTile: function( tile, drawingHandler, useSketch ) {
+ drawTile: function( tile, drawingHandler, useSketch, scale ) {
$.console.assert(tile, '[Drawer.drawTile] tile is required');
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
@@ -301,10 +302,10 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
// 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 );
+ tile.drawCanvas( context, drawingHandler, scale );
this._restoreRotationChanges( tile, useSketch );
} else {
- tile.drawCanvas( context, drawingHandler );
+ tile.drawCanvas( context, drawingHandler, scale );
}
} else {
tile.drawHTML( this.canvas );
@@ -371,16 +372,29 @@ $.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.
* @returns {undefined}
*/
- blendSketch: function(opacity) {
+ blendSketch: function(opacity, sketchScale) {
if (!this.useCanvas || !this.sketchCanvas) {
return;
}
+ sketchScale = sketchScale || 1;
this.context.save();
this.context.globalAlpha = opacity;
- this.context.drawImage(this.sketchCanvas, 0, 0);
+ this.context.drawImage(
+ this.sketchCanvas,
+ 0,
+ 0,
+ this.sketchCanvas.width * sketchScale,
+ this.sketchCanvas.height * sketchScale,
+ 0,
+ 0,
+ this.canvas.width,
+ this.canvas.height
+ );
this.context.restore();
},
diff --git a/src/openseadragon.js b/src/openseadragon.js
index 6b36327b..df4dd55f 100644
--- a/src/openseadragon.js
+++ b/src/openseadragon.js
@@ -249,6 +249,11 @@
* image though it is less effective visually if the HTML5 Canvas is not
* 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.
+ *
* @property {Boolean} [autoResize=true]
* Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
*
@@ -1000,6 +1005,7 @@ if (typeof define === 'function' && define.amd) {
immediateRender: false,
minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
+ smoothTileEdgesMinZoom: 1.1, //-> higher than maxZoomPixelRatio disables it
pixelsPerWheelLine: 40,
autoResize: true,
preserveImageSizeOnResize: false, // requires autoResize=true
diff --git a/src/tile.js b/src/tile.js
index ad018ba7..9a7fde55 100644
--- a/src/tile.js
+++ b/src/tile.js
@@ -240,11 +240,12 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
* @param {Function} drawingHandler - Method for firing the drawing event.
* drawingHandler({context, tile, rendered})
* where rendered
is the context with the pre-drawn image.
+ * @param {Number} scale - Apply a scale to position and size
*/
- drawCanvas: function( context, drawingHandler ) {
+ drawCanvas: function( context, drawingHandler, scale ) {
- var position = this.position,
- size = this.size,
+ var position = this.position.times($.pixelDensityRatio),
+ size = this.size.times($.pixelDensityRatio),
rendered;
if (!this.cacheImageRecord) {
@@ -277,10 +278,10 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
//clearing only the inside of the rectangle occupied
//by the png prevents edge flikering
context.clearRect(
- (position.x * $.pixelDensityRatio)+1,
- (position.y * $.pixelDensityRatio)+1,
- (size.x * $.pixelDensityRatio)-2,
- (size.y * $.pixelDensityRatio)-2
+ position.x + 1,
+ position.y + 1,
+ size.x - 2,
+ size.y - 2
);
}
@@ -289,16 +290,52 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
// changes as we are rendering the image
drawingHandler({context: context, tile: this, rendered: rendered});
+ if (typeof scale === 'number' && scale !== 1) {
+ // 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';
context.drawImage(
rendered.canvas,
0,
0,
rendered.canvas.width,
rendered.canvas.height,
- position.x * $.pixelDensityRatio,
- position.y * $.pixelDensityRatio,
- size.x * $.pixelDensityRatio,
- size.y * $.pixelDensityRatio
+ position.x,
+ position.y,
+ size.x,
+ size.y
);
context.restore();
diff --git a/src/tiledimage.js b/src/tiledimage.js
index 1731d472..815d052c 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -133,19 +133,20 @@ $.TiledImage = function( options ) {
_hasOpaqueTile: false, // Do we have even one fully opaque tile?
//configurable settings
- springStiffness: $.DEFAULT_SETTINGS.springStiffness,
- animationTime: $.DEFAULT_SETTINGS.animationTime,
- minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
- wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
- wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
- immediateRender: $.DEFAULT_SETTINGS.immediateRender,
- blendTime: $.DEFAULT_SETTINGS.blendTime,
- alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
- minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
- debugMode: $.DEFAULT_SETTINGS.debugMode,
- crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
- placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
- opacity: $.DEFAULT_SETTINGS.opacity
+ springStiffness: $.DEFAULT_SETTINGS.springStiffness,
+ animationTime: $.DEFAULT_SETTINGS.animationTime,
+ minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
+ wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
+ wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
+ immediateRender: $.DEFAULT_SETTINGS.immediateRender,
+ blendTime: $.DEFAULT_SETTINGS.blendTime,
+ alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
+ minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
+ smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,
+ debugMode: $.DEFAULT_SETTINGS.debugMode,
+ crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
+ placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
+ opacity: $.DEFAULT_SETTINGS.opacity
}, options );
@@ -1302,6 +1303,19 @@ function drawTiles( tiledImage, lastDrawn ) {
return;
}
var useSketch = tiledImage.opacity < 1;
+ var sketchScale = 1;
+
+ var zoom = tiledImage.viewport.getZoom();
+ var imageZoom = tiledImage.viewportToImageZoom(zoom);
+ if ( imageZoom > tiledImage.smoothTileEdgesMinZoom ) {
+ // 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;
+ }
+
if ( useSketch ) {
tiledImage._drawer._clear( true );
}
@@ -1333,7 +1347,7 @@ function drawTiles( tiledImage, lastDrawn ) {
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
tile = lastDrawn[ i ];
- tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch );
+ tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale );
tile.beingDrawn = true;
if( tiledImage.viewer ){
@@ -1360,7 +1374,7 @@ function drawTiles( tiledImage, lastDrawn ) {
}
if ( useSketch ) {
- tiledImage._drawer.blendSketch( tiledImage.opacity );
+ tiledImage._drawer.blendSketch( tiledImage.opacity, sketchScale );
}
drawDebugInfo( tiledImage, lastDrawn );
}
diff --git a/src/viewer.js b/src/viewer.js
index c66556f1..0851f0e6 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -604,7 +604,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
var originalSuccess = options.success;
options.success = function(event) {
successes++;
-
+
// TODO: now that options has other things besides tileSource, the overlays
// should probably be at the options level, not the tileSource level.
if (options.tileSource.overlays) {
@@ -1342,6 +1342,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
blendTime: _this.blendTime,
alwaysBlend: _this.alwaysBlend,
minPixelRatio: _this.minPixelRatio,
+ smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
crossOriginPolicy: _this.crossOriginPolicy,
debugMode: _this.debugMode
});