diff --git a/changelog.txt b/changelog.txt index 46592f88..df6ea660 100644 --- a/changelog.txt +++ b/changelog.txt @@ -47,8 +47,11 @@ OPENSEADRAGON CHANGELOG * Added viewport-change event for after the viewport changes but before it's drawn * A spring's current value is now updated immediately on reset (#524) * Fixed an error in fitBounds that occurred sometimes with immediately = true +* Added support for HDPI (retina) displays (#583) -1.2.1: (in progress) +1.2.2: (in progress) + +1.2.1: * Added preserveOverlays option (#561) * Fixed: DZI tilesource was broken on IE8/IE9 (#563) diff --git a/package.json b/package.json index 97fb8b25..99e60e8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "OpenSeadragon", - "version": "1.2.0", + "version": "1.2.1", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "devDependencies": { "grunt": "^0.4.5", diff --git a/src/drawer.js b/src/drawer.js index cfd2c71d..77f078ad 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -108,6 +108,13 @@ $.Drawer = function( options ) { // Note that this means overlays you want to be rtl need to be explicitly set to rtl. this.container.dir = 'ltr'; + // check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density + if (this.useCanvas) { + var viewportSize = this._calculateCanvasSize(); + this.canvas.width = viewportSize.x; + this.canvas.height = viewportSize.y; + } + this.canvas.style.width = "100%"; this.canvas.style.height = "100%"; this.canvas.style.position = "absolute"; @@ -215,7 +222,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ clear: function() { this.canvas.innerHTML = ""; if ( this.useCanvas ) { - var viewportSize = this.viewport.getContainerSize(); + var viewportSize = this._calculateCanvasSize(); if( this.canvas.width != viewportSize.x || this.canvas.height != viewportSize.y ) { this.canvas.width = viewportSize.x; @@ -255,22 +262,24 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ drawDebugInfo: function( tile, count, i ){ if ( this.useCanvas ) { this.context.save(); - this.context.lineWidth = 2; - this.context.font = 'small-caps bold 13px ariel'; + this.context.lineWidth = 2 * $.pixelDensityRatio; + this.context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; this.context.strokeStyle = this.debugGridColor; this.context.fillStyle = this.debugGridColor; - this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); + if ( this.viewport.degrees !== 0 ) { + this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); + } this.context.strokeRect( - tile.position.x, - tile.position.y, - tile.size.x, - tile.size.y + tile.position.x * $.pixelDensityRatio, + tile.position.y * $.pixelDensityRatio, + tile.size.x * $.pixelDensityRatio, + tile.size.y * $.pixelDensityRatio ); - var tileCenterX = tile.position.x + (tile.size.x / 2); - var tileCenterY = tile.position.y + (tile.size.y / 2); + var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio; + var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio; // Rotate the text the right way around. this.context.translate( tileCenterX, tileCenterY ); @@ -280,46 +289,49 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ if( tile.x === 0 && tile.y === 0 ){ this.context.fillText( "Zoom: " + this.viewport.getZoom(), - tile.position.x, - tile.position.y - 30 + tile.position.x * $.pixelDensityRatio, + (tile.position.y - 30) * $.pixelDensityRatio ); this.context.fillText( "Pan: " + this.viewport.getBounds().toString(), - tile.position.x, - tile.position.y - 20 + tile.position.x * $.pixelDensityRatio, + (tile.position.y - 20) * $.pixelDensityRatio ); } this.context.fillText( "Level: " + tile.level, - tile.position.x + 10, - tile.position.y + 20 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 20) * $.pixelDensityRatio ); this.context.fillText( "Column: " + tile.x, - tile.position.x + 10, - tile.position.y + 30 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 30) * $.pixelDensityRatio ); this.context.fillText( "Row: " + tile.y, - tile.position.x + 10, - tile.position.y + 40 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 40) * $.pixelDensityRatio ); this.context.fillText( "Order: " + i + " of " + count, - tile.position.x + 10, - tile.position.y + 50 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 50) * $.pixelDensityRatio ); this.context.fillText( "Size: " + tile.size.toString(), - tile.position.x + 10, - tile.position.y + 60 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 60) * $.pixelDensityRatio ); this.context.fillText( "Position: " + tile.position.toString(), - tile.position.x + 10, - tile.position.y + 70 + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 70) * $.pixelDensityRatio ); - this._restoreRotationChanges( tile, this.canvas, this.context ); + + if ( this.viewport.degrees !== 0 ) { + this._restoreRotationChanges( tile, this.canvas, this.context ); + } this.context.restore(); } }, @@ -328,15 +340,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ debugRect: function(rect) { if ( this.useCanvas ) { this.context.save(); - this.context.lineWidth = 2; + this.context.lineWidth = 2 * $.pixelDensityRatio; this.context.strokeStyle = this.debugGridColor; this.context.fillStyle = this.debugGridColor; this.context.strokeRect( - rect.x, - rect.y, - rect.width, - rect.height + rect.x * $.pixelDensityRatio, + rect.y * $.pixelDensityRatio, + rect.width * $.pixelDensityRatio, + rect.height * $.pixelDensityRatio ); this.context.restore(); @@ -369,6 +381,16 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ tile.position.y = py; this.context.restore(); + }, + + // private + _calculateCanvasSize: function() { + var pixelDensityRatio = $.pixelDensityRatio; + var viewportSize = this.viewport.getContainerSize(); + return { + x: viewportSize.x * pixelDensityRatio, + y: viewportSize.y * pixelDensityRatio + }; } }; diff --git a/src/openseadragon.js b/src/openseadragon.js index b91d9b99..702d6976 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -820,6 +820,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ canvasElement.getContext( '2d' ) ); }()); + /** + * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density. Defaults to 1 if canvas isn't supported by the browser. + * @member {Number} pixelDensityRatio + * @memberof OpenSeadragon + */ + $.pixelDensityRatio = (function () { + if ( $.supportsCanvas ) { + var context = document.createElement('canvas').getContext('2d'); + var devicePixelRatio = window.devicePixelRatio || 1; + var backingStoreRatio = context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + return devicePixelRatio / backingStoreRatio; + } else { + return 1; + } + }()); }( OpenSeadragon )); diff --git a/src/tile.js b/src/tile.js index 30082315..756d9d4a 100644 --- a/src/tile.js +++ b/src/tile.js @@ -268,10 +268,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+1, - position.y+1, - size.x-2, - size.y-2 + (position.x * $.pixelDensityRatio)+1, + (position.y * $.pixelDensityRatio)+1, + (size.x * $.pixelDensityRatio)-2, + (size.y * $.pixelDensityRatio)-2 ); } @@ -297,10 +297,10 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ 0, rendered.canvas.width, rendered.canvas.height, - position.x, - position.y, - size.x, - size.y + position.x * $.pixelDensityRatio, + position.y * $.pixelDensityRatio, + size.x * $.pixelDensityRatio, + size.y * $.pixelDensityRatio ); }, diff --git a/src/tiledimage.js b/src/tiledimage.js index db52c66c..ba587d07 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -262,7 +262,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag // private _viewportToImageDelta: function( viewerX, viewerY, current ) { - var scale = current ? this._scaleSpring.current.value : this._scaleSpring.target.value; + var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value); return new $.Point(viewerX * (this.source.dimensions.x / scale), viewerY * ((this.source.dimensions.y * this.contentAspectX) / scale)); }, @@ -294,7 +294,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag // private _imageToViewportDelta: function( imageX, imageY, current ) { - var scale = current ? this._scaleSpring.current.value : this._scaleSpring.target.value; + var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value); return new $.Point((imageX / this.source.dimensions.x) * scale, (imageY / this.source.dimensions.y / this.contentAspectX) * scale); },