From 44395662d1368e58632686056f0ce580eec2d2d1 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 19 Apr 2015 16:10:37 -0400 Subject: [PATCH 1/5] Add opacity support. --- src/drawer.js | 182 +++++++++++++++++++++++++++------------------- src/tile.js | 2 +- src/tiledimage.js | 34 ++++++--- src/viewer.js | 1 + 4 files changed, 133 insertions(+), 86 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index e14c14e3..2cf6c43b 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -96,6 +96,13 @@ $.Drawer = function( options ) { */ this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null; + /** + * Sketch canvas used to temporarily draw tiles which cannot be drawn directly + * to the main canvas due to opacity. + */ + this.sketchCanvas = this.useCanvas ? document.createElement( "canvas" ) : null; + this.sketchContext = this.useCanvas ? this.sketchCanvas.getContext( "2d" ) : null; + /** * @member {Element} element * @memberof OpenSeadragon.Drawer# @@ -111,8 +118,8 @@ $.Drawer = function( options ) { // 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.width = this.sketchCanvas.width = viewportSize.x; + this.canvas.height = this.sketchCanvas.height = viewportSize.y; } this.canvas.style.width = "100%"; @@ -254,21 +261,23 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @param {OpenSeadragon.Tile} tile - The tile to draw. * @param {Function} drawingHandler - Method for firing the drawing event if using canvas. * 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. */ - drawTile: function( tile, drawingHandler ) { + drawTile: function( tile, drawingHandler, useSketch ) { $.console.assert(tile, '[Drawer.drawTile] tile is required'); $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); if ( this.useCanvas ) { + var context = useSketch ? this.sketchContext : this.context; // 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 ); - tile.drawCanvas( this.context, drawingHandler ); - this._restoreRotationChanges( tile ); + this._offsetForRotation( tile, this.viewport.degrees, useSketch ); + tile.drawCanvas( context, drawingHandler ); + this._restoreRotationChanges( tile, useSketch ); } else { - tile.drawCanvas( this.context, drawingHandler ); + tile.drawCanvas( context, drawingHandler ); } } else { tile.drawHTML( this.canvas ); @@ -276,63 +285,87 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ }, // private - saveContext: function() { + saveContext: function(useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.save(); + }, + + // private + restoreContext: function(useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.restore(); + }, + + // private + setClip: function(rect, useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.beginPath(); + context.rect(rect.x, rect.y, rect.width, rect.height); + context.clip(); + }, + + // private + drawRectangle: function(rect, fillStyle, useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.fillStyle = fillStyle; + context.fillRect(rect.x, rect.y, rect.width, rect.height); + context.restore(); + }, + + /** + * Blends the sketch canvas in the main canvas. + * The sketch canvas is then cleared. + * @param {Float} opacity The opacity of the blending. + * @returns {undefined} + */ + blendSketch: function(opacity) { if (!this.useCanvas) { return; } this.context.save(); - }, - - // private - restoreContext: function() { - if (!this.useCanvas) { - return; - } - + this.context.globalAlpha = opacity; + this.context.drawImage(this.sketchCanvas, 0, 0); this.context.restore(); + this.sketchContext.clearRect(0, 0, + this.sketchCanvas.width, this.sketchCanvas.height); }, // private - setClip: function(rect) { - if (!this.useCanvas) { - return; - } - - this.context.beginPath(); - this.context.rect(rect.x, rect.y, rect.width, rect.height); - this.context.clip(); - }, - - // private - drawRectangle: function(rect, fillStyle) { - if (!this.useCanvas) { - return; - } - - this.context.save(); - this.context.fillStyle = fillStyle; - this.context.fillRect(rect.x, rect.y, rect.width, rect.height); - this.context.restore(); - }, - - // private - drawDebugInfo: function( tile, count, i ){ + drawDebugInfo: function( tile, count, i, useSketch ){ if ( !this.useCanvas ) { return; } - this.context.save(); - 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; + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.lineWidth = 2 * $.pixelDensityRatio; + context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; + context.strokeStyle = this.debugGridColor; + context.fillStyle = this.debugGridColor; if ( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); + this._offsetForRotation( tile, this.viewport.degrees, useSketch ); } - this.context.strokeRect( + context.strokeRect( tile.position.x * $.pixelDensityRatio, tile.position.y * $.pixelDensityRatio, tile.size.x * $.pixelDensityRatio, @@ -343,95 +376,97 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio; // Rotate the text the right way around. - this.context.translate( tileCenterX, tileCenterY ); - this.context.rotate( Math.PI / 180 * -this.viewport.degrees ); - this.context.translate( -tileCenterX, -tileCenterY ); + context.translate( tileCenterX, tileCenterY ); + context.rotate( Math.PI / 180 * -this.viewport.degrees ); + context.translate( -tileCenterX, -tileCenterY ); if( tile.x === 0 && tile.y === 0 ){ - this.context.fillText( + context.fillText( "Zoom: " + this.viewport.getZoom(), tile.position.x * $.pixelDensityRatio, (tile.position.y - 30) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Pan: " + this.viewport.getBounds().toString(), tile.position.x * $.pixelDensityRatio, (tile.position.y - 20) * $.pixelDensityRatio ); } - this.context.fillText( + context.fillText( "Level: " + tile.level, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 20) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Column: " + tile.x, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 30) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Row: " + tile.y, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 40) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Order: " + i + " of " + count, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 50) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Size: " + tile.size.toString(), (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 60) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Position: " + tile.position.toString(), (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 70) * $.pixelDensityRatio ); if ( this.viewport.degrees !== 0 ) { - this._restoreRotationChanges( tile, this.canvas, this.context ); + this._restoreRotationChanges( tile, useSketch ); } - this.context.restore(); + context.restore(); }, // private - debugRect: function(rect) { + debugRect: function(rect, useSketch) { if ( this.useCanvas ) { - this.context.save(); - this.context.lineWidth = 2 * $.pixelDensityRatio; - this.context.strokeStyle = this.debugGridColor; - this.context.fillStyle = this.debugGridColor; + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.lineWidth = 2 * $.pixelDensityRatio; + context.strokeStyle = this.debugGridColor; + context.fillStyle = this.debugGridColor; - this.context.strokeRect( + context.strokeRect( rect.x * $.pixelDensityRatio, rect.y * $.pixelDensityRatio, rect.width * $.pixelDensityRatio, rect.height * $.pixelDensityRatio ); - this.context.restore(); + context.restore(); } }, // private - _offsetForRotation: function( tile, degrees ){ + _offsetForRotation: function( tile, degrees, useSketch ){ var cx = this.canvas.width / 2, cy = this.canvas.height / 2, px = tile.position.x - cx, py = tile.position.y - cy; - this.context.save(); + var context = useSketch ? this.sketchContext : this.context; + context.save(); - this.context.translate(cx, cy); - this.context.rotate( Math.PI / 180 * degrees); + context.translate(cx, cy); + context.rotate( Math.PI / 180 * degrees); tile.position.x = px; tile.position.y = py; }, // private - _restoreRotationChanges: function( tile ){ + _restoreRotationChanges: function( tile, useSketch ){ var cx = this.canvas.width / 2, cy = this.canvas.height / 2, px = tile.position.x + cx, @@ -440,7 +475,8 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ tile.position.x = px; tile.position.y = py; - this.context.restore(); + var context = useSketch ? this.sketchContext : this.context; + context.restore(); }, // private diff --git a/src/tile.js b/src/tile.js index 756d9d4a..3fe642df 100644 --- a/src/tile.js +++ b/src/tile.js @@ -67,7 +67,7 @@ $.Tile = function(level, x, y, bounds, exists, url) { this.y = y; /** * Where this tile fits, in normalized coordinates - * @member {OpenSeadragon.Point} bounds + * @member {OpenSeadragon.Rect} bounds * @memberof OpenSeadragon.Tile# */ this.bounds = bounds; diff --git a/src/tiledimage.js b/src/tiledimage.js index eebfa0af..a494dbd0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -119,6 +119,12 @@ $.TiledImage = function( options ) { delete options.height; } + this.opacity = 1; + if ( options.opacity ) { + this.opacity = options.opacity; + delete options.opacity; + } + $.extend( true, this, { //internal state properties @@ -1152,20 +1158,20 @@ function compareTiles( previousBest, tile ) { function drawTiles( tiledImage, lastDrawn ) { var i, - tile, - tileKey, - viewer, - viewport, - position, - tileSource; + tile; + + if ( tiledImage.opacity <= 0 ) { + return; + } + var useSketch = tiledImage.opacity < 1; var usedClip = false; if ( tiledImage._clip ) { - tiledImage._drawer.saveContext(); + tiledImage._drawer.saveContext(useSketch); var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box); - tiledImage._drawer.setClip(clipRect); + tiledImage._drawer.setClip(clipRect, useSketch); usedClip = true; } @@ -1181,17 +1187,17 @@ function drawTiles( tiledImage, lastDrawn ) { fillStyle = tiledImage.placeholderFillStyle; } - tiledImage._drawer.drawRectangle(placeholderRect, fillStyle); + tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { tile = lastDrawn[ i ]; - tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler ); + tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch ); tile.beingDrawn = true; if( tiledImage.debugMode ) { try { - tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i ); + tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i, useSketch ); } catch(e) { $.console.error(e); } @@ -1217,7 +1223,11 @@ function drawTiles( tiledImage, lastDrawn ) { } if ( usedClip ) { - tiledImage._drawer.restoreContext(); + tiledImage._drawer.restoreContext( useSketch ); + } + + if ( useSketch ) { + tiledImage._drawer.blendSketch( tiledImage.opacity ); } } diff --git a/src/viewer.js b/src/viewer.js index 57ad0f1f..570e60c7 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1290,6 +1290,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, height: queueItem.options.height, clip: queueItem.options.clip, placeholderFillStyle: queueItem.options.placeholderFillStyle, + opacity: queueItem.options.opacity, springStiffness: _this.springStiffness, animationTime: _this.animationTime, minZoomImageRatio: _this.minZoomImageRatio, From 9d053c545bd1fcae8a21f10875533a4a2a194994 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 20 Apr 2015 19:25:36 -0400 Subject: [PATCH 2/5] Fix Ian's comments. --- src/drawer.js | 104 +++++++++++++++++++++++++++---------------- src/openseadragon.js | 2 +- src/tiledimage.js | 51 ++++++++++++++------- src/viewer.js | 5 ++- 4 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2cf6c43b..2d39c373 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -42,11 +42,9 @@ * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer. * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport. * @param {Element} options.element - Parent element. - * @param {Number} [options.opacity=1] - See opacity in {@link OpenSeadragon.Options} for details. * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details. */ $.Drawer = function( options ) { - var _this = this; $.console.assert( options.viewer, "[Drawer] options.viewer is required" ); @@ -72,7 +70,9 @@ $.Drawer = function( options ) { this.viewer = options.viewer; this.viewport = options.viewport; this.debugGridColor = options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor; - this.opacity = options.opacity === undefined ? $.DEFAULT_SETTINGS.opacity : options.opacity; + if (options.opacity) { + $.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" ); + } this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true ); /** @@ -98,10 +98,10 @@ $.Drawer = function( options ) { /** * Sketch canvas used to temporarily draw tiles which cannot be drawn directly - * to the main canvas due to opacity. + * to the main canvas due to opacity. Lazily initialized. */ - this.sketchCanvas = this.useCanvas ? document.createElement( "canvas" ) : null; - this.sketchContext = this.useCanvas ? this.sketchCanvas.getContext( "2d" ) : null; + this.sketchCanvas = null; + this.sketchContext = null; /** * @member {Element} element @@ -118,8 +118,8 @@ $.Drawer = function( options ) { // 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 = this.sketchCanvas.width = viewportSize.x; - this.canvas.height = this.sketchCanvas.height = viewportSize.y; + this.canvas.width = viewportSize.x; + this.canvas.height = viewportSize.y; } this.canvas.style.width = "100%"; @@ -167,8 +167,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @return {OpenSeadragon.Drawer} Chainable. */ setOpacity: function( opacity ) { - this.opacity = opacity; - $.setElementOpacity( this.canvas, this.opacity, true ); + $.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead."); + var world = this.viewer.world; + for (var i = 0; i < world.getItemCount(); i++) { + world.getItemAt( i ).setOpacity( opacity ); + } return this; }, @@ -177,7 +180,16 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @returns {Number} */ getOpacity: function() { - return this.opacity; + $.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead."); + var world = this.viewer.world; + var maxOpacity = 0; + for (var i = 0; i < world.getItemCount(); i++) { + var opacity = world.getItemAt( i ).getOpacity(); + if ( opacity > maxOpacity ) { + maxOpacity = opacity; + } + } + return maxOpacity; }, // deprecated @@ -221,12 +233,14 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ //force unloading of current canvas (1x1 will be gc later, trick not necessarily needed) this.canvas.width = 1; this.canvas.height = 1; + this.sketchCanvas = null; + this.sketchContext = null; }, /** * Clears the Drawer so it's ready to draw another frame. */ - clear: function() { + clear: function( useSketch ) { this.canvas.innerHTML = ""; if ( this.useCanvas ) { var viewportSize = this._calculateCanvasSize(); @@ -234,8 +248,13 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.canvas.height != viewportSize.y ) { 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; + } } - this.context.clearRect( 0, 0, viewportSize.x, viewportSize.y ); + this._getContext( useSketch ).clearRect( + 0, 0, viewportSize.x, viewportSize.y ); } }, @@ -269,7 +288,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); if ( this.useCanvas ) { - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); // 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 ) { @@ -284,24 +303,36 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ } }, - // private - saveContext: function(useSketch) { - if (!this.useCanvas) { - return; + _getContext: function( useSketch ) { + var context = this.context; + if ( useSketch ) { + if (this.sketchCanvas === null) { + this.sketchCanvas = document.createElement( "canvas" ); + this.sketchCanvas.width = this.canvas.width; + this.sketchCanvas.height = this.canvas.height; + this.sketchContext = this.sketchCanvas.getContext( "2d" ); + } + context = this.sketchContext; } - - var context = useSketch ? this.sketchContext : this.context; - context.save(); + return context; }, // private - restoreContext: function(useSketch) { + saveContext: function( useSketch ) { if (!this.useCanvas) { return; } - var context = useSketch ? this.sketchContext : this.context; - context.restore(); + this._getContext( useSketch ).save(); + }, + + // private + restoreContext: function( useSketch ) { + if (!this.useCanvas) { + return; + } + + this._getContext( useSketch ).restore(); }, // private @@ -310,7 +341,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.beginPath(); context.rect(rect.x, rect.y, rect.width, rect.height); context.clip(); @@ -322,7 +353,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.save(); context.fillStyle = fillStyle; context.fillRect(rect.x, rect.y, rect.width, rect.height); @@ -331,12 +362,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Blends the sketch canvas in the main canvas. - * The sketch canvas is then cleared. * @param {Float} opacity The opacity of the blending. * @returns {undefined} */ blendSketch: function(opacity) { - if (!this.useCanvas) { + if (!this.useCanvas || !this.sketchCanvas) { return; } @@ -344,17 +374,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.context.globalAlpha = opacity; this.context.drawImage(this.sketchCanvas, 0, 0); this.context.restore(); - this.sketchContext.clearRect(0, 0, - this.sketchCanvas.width, this.sketchCanvas.height); }, // private - drawDebugInfo: function( tile, count, i, useSketch ){ + drawDebugInfo: function( tile, count, i ){ if ( !this.useCanvas ) { return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this.context; context.save(); context.lineWidth = 2 * $.pixelDensityRatio; context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; @@ -362,7 +390,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ context.fillStyle = this.debugGridColor; if ( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.viewport.degrees, useSketch ); + this._offsetForRotation( tile, this.viewport.degrees ); } context.strokeRect( @@ -424,15 +452,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ ); if ( this.viewport.degrees !== 0 ) { - this._restoreRotationChanges( tile, useSketch ); + this._restoreRotationChanges( tile ); } context.restore(); }, // private - debugRect: function(rect, useSketch) { + debugRect: function(rect) { if ( this.useCanvas ) { - var context = useSketch ? this.sketchContext : this.context; + var context = this.context; context.save(); context.lineWidth = 2 * $.pixelDensityRatio; context.strokeStyle = this.debugGridColor; @@ -456,7 +484,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ px = tile.position.x - cx, py = tile.position.y - cy; - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.save(); context.translate(cx, cy); @@ -475,7 +503,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ tile.position.x = px; tile.position.y = py; - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.restore(); }, diff --git a/src/openseadragon.js b/src/openseadragon.js index 3e3cffbd..461f9d22 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -204,7 +204,7 @@ * If 0, adjusts to fit viewer. * * @property {Number} [opacity=1] - * Opacity of the drawer (1=opaque, 0=transparent) + * Default opacity of the tiled images (1=opaque, 0=transparent) * * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. diff --git a/src/tiledimage.js b/src/tiledimage.js index a494dbd0..80515434 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.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}. * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}. @@ -119,12 +120,6 @@ $.TiledImage = function( options ) { delete options.height; } - this.opacity = 1; - if ( options.opacity ) { - this.opacity = options.opacity; - delete options.opacity; - } - $.extend( true, this, { //internal state properties @@ -148,7 +143,8 @@ $.TiledImage = function( options ) { minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, debugMode: $.DEFAULT_SETTINGS.debugMode, crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy, - placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle + placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle, + opacity: $.DEFAULT_SETTINGS.opacity }, options ); @@ -492,6 +488,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._needsDraw = true; }, + /** + * @returns {Number} The TiledImage's current opacity. + */ + getOpacity: function() { + return this.opacity; + }, + + /** + * @param {Number} opacity Opacity the tiled image should be drawn at. + * @returns {OpenSeadragon.TiledImage} Chainable + */ + setOpacity: function(opacity) { + this.opacity = opacity; + this._needsDraw = true; + return this; + }, + // private _setScale: function(scale, immediately) { var sameTarget = (this._scaleSpring.target.value === scale); @@ -1161,6 +1174,7 @@ function drawTiles( tiledImage, lastDrawn ) { tile; if ( tiledImage.opacity <= 0 ) { + drawDebugInfo( tiledImage, lastDrawn ); return; } var useSketch = tiledImage.opacity < 1; @@ -1195,14 +1209,6 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch ); tile.beingDrawn = true; - if( tiledImage.debugMode ) { - try { - tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i, useSketch ); - } catch(e) { - $.console.error(e); - } - } - if( tiledImage.viewer ){ /** * - Needs documentation - @@ -1228,6 +1234,21 @@ function drawTiles( tiledImage, lastDrawn ) { if ( useSketch ) { tiledImage._drawer.blendSketch( tiledImage.opacity ); + tiledImage._drawer.clear( true ); + } + drawDebugInfo( tiledImage, lastDrawn ); +} + +function drawDebugInfo( tiledImage, lastDrawn ) { + if( tiledImage.debugMode ) { + for ( var i = lastDrawn.length - 1; i >= 0; i-- ) { + var tile = lastDrawn[ i ]; + try { + tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i ); + } catch(e) { + $.console.error(e); + } + } } } diff --git a/src/viewer.js b/src/viewer.js index 570e60c7..2be3786e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -374,7 +374,6 @@ $.Viewer = function( options ) { viewer: this, viewport: this.viewport, element: this.canvas, - opacity: this.opacity, debugGridColor: this.debugGridColor }); @@ -1198,6 +1197,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to * (portions of the image outside of this area will not be visible). Only works on * browsers that support the HTML5 canvas. + * @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default. * @param {Function} [options.success] A function that gets called when the image is * successfully added. It's passed the event object which contains a single property: * "item", the resulting TiledImage. @@ -1221,6 +1221,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if (options.placeholderFillStyle === undefined) { options.placeholderFillStyle = this.placeholderFillStyle; } + if (options.opacity === undefined) { + options.opacity = this.opacity; + } var myQueueItem = { options: options From dd0777141501a462253bcb4bfc698cd77912a49b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 22 Apr 2015 18:30:49 -0400 Subject: [PATCH 3/5] Fix drawer.clear --- src/drawer.js | 14 +++++++++++--- src/tiledimage.js | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2d39c373..939a8cf0 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -240,7 +240,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Clears the Drawer so it's ready to draw another frame. */ - clear: function( useSketch ) { + clear: function() { this.canvas.innerHTML = ""; if ( this.useCanvas ) { var viewportSize = this._calculateCanvasSize(); @@ -253,11 +253,19 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.sketchCanvas.height = this.canvas.height; } } - this._getContext( useSketch ).clearRect( - 0, 0, viewportSize.x, viewportSize.y ); + this._clear(); } }, + _clear: function ( useSketch ) { + if ( !this.useCanvas ) { + return; + } + var context = this._getContext( useSketch ); + var canvas = context.canvas; + context.clearRect( 0, 0, canvas.width, canvas.height ); + }, + /** * Translates from OpenSeadragon viewer rectangle to drawer rectangle. * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system. diff --git a/src/tiledimage.js b/src/tiledimage.js index 80515434..faa49cc9 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -497,12 +497,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * @param {Number} opacity Opacity the tiled image should be drawn at. - * @returns {OpenSeadragon.TiledImage} Chainable */ setOpacity: function(opacity) { this.opacity = opacity; this._needsDraw = true; - return this; }, // private @@ -1178,6 +1176,9 @@ function drawTiles( tiledImage, lastDrawn ) { return; } var useSketch = tiledImage.opacity < 1; + if ( useSketch ) { + tiledImage._drawer._clear( true ); + } var usedClip = false; if ( tiledImage._clip ) { @@ -1234,7 +1235,6 @@ function drawTiles( tiledImage, lastDrawn ) { if ( useSketch ) { tiledImage._drawer.blendSketch( tiledImage.opacity ); - tiledImage._drawer.clear( true ); } drawDebugInfo( tiledImage, lastDrawn ); } From 6bc5f1ff510fca5718773cedbcc279f32c8e11c2 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 22 Apr 2015 20:12:38 -0400 Subject: [PATCH 4/5] Fix drawer tests. --- test/modules/drawer.js | 69 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/test/modules/drawer.js b/test/modules/drawer.js index c91915c3..4016ebfd 100644 --- a/test/modules/drawer.js +++ b/test/modules/drawer.js @@ -33,9 +33,6 @@ createViewer(); ok(viewer.drawer, 'Drawer exists'); equal(viewer.drawer.canRotate(), OpenSeadragon.supportsCanvas, 'we can rotate if we have canvas'); - equal(viewer.drawer.getOpacity(), 1, 'starts with full opacity'); - viewer.drawer.setOpacity(0.4); - equal(viewer.drawer.getOpacity(), 0.4, 'setting opacity works'); start(); }); @@ -67,18 +64,64 @@ }); }); + // ---------- + asyncTest('sketchCanvas', function() { + createViewer({ + tileSources: '/test/data/testpattern.dzi' + }); + var drawer = viewer.drawer; + + viewer.addHandler('tile-drawn', function noOpacityHandler() { + viewer.removeHandler('tile-drawn', noOpacityHandler); + equal(drawer.sketchCanvas, null, + 'The sketch canvas should be null if no decimal opacity is used.'); + equal(drawer.sketchContext, null, + 'The sketch context should be null if no decimal opacity is used.'); + testOpacityDecimal(); + }); + + function testOpacityDecimal() { + var tiledImage; + viewer.addTiledImage({ + tileSource: '/test/data/testpattern.dzi', + opacity: 0.5, + success: function(event) { + tiledImage = event.item; + } + }); + + viewer.addHandler('tile-drawn', function opacityDecimalHandler(event) { + if (tiledImage !== event.tiledImage) { + return; + } + viewer.removeHandler('tile-drawn', opacityDecimalHandler); + notEqual(drawer.sketchCanvas, null, + 'The sketch canvas should not be null once a decimal opacity has been used.'); + notEqual(drawer.sketchContext, null, + 'The sketch context should not be null once a decimal opacity has been used.'); + start(); + }); + } + }); + // ---------- asyncTest('deprecations', function() { - createViewer(); - Util.testDeprecation(viewer.drawer, 'addOverlay', viewer, 'addOverlay'); - Util.testDeprecation(viewer.drawer, 'updateOverlay', viewer, 'updateOverlay'); - Util.testDeprecation(viewer.drawer, 'removeOverlay', viewer, 'removeOverlay'); - Util.testDeprecation(viewer.drawer, 'clearOverlays', viewer, 'clearOverlays'); - Util.testDeprecation(viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw'); - Util.testDeprecation(viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded'); - Util.testDeprecation(viewer.drawer, 'reset', viewer.world, 'resetItems'); - Util.testDeprecation(viewer.drawer, 'update', viewer.world, 'draw'); - start(); + createViewer({ + tileSources: '/test/data/testpattern.dzi' + }); + viewer.world.addHandler('add-item', function() { + Util.testDeprecation(viewer.drawer, 'addOverlay', viewer, 'addOverlay'); + Util.testDeprecation(viewer.drawer, 'updateOverlay', viewer, 'updateOverlay'); + Util.testDeprecation(viewer.drawer, 'removeOverlay', viewer, 'removeOverlay'); + Util.testDeprecation(viewer.drawer, 'clearOverlays', viewer, 'clearOverlays'); + Util.testDeprecation(viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw'); + Util.testDeprecation(viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded'); + Util.testDeprecation(viewer.drawer, 'reset', viewer.world, 'resetItems'); + Util.testDeprecation(viewer.drawer, 'update', viewer.world, 'draw'); + Util.testDeprecation(viewer.drawer, 'setOpacity', viewer.world.getItemAt(0), 'setOpacity'); + Util.testDeprecation(viewer.drawer, 'getOpacity', viewer.world.getItemAt(0), 'getOpacity'); + start(); + }); }); })(); From 39bd3709309616ed8240dacc453f56e2813b221a Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 27 Apr 2015 19:28:18 -0400 Subject: [PATCH 5/5] Add opacity test in tiledimage. --- test/modules/tiledimage.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 704f4773..07f23f55 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -220,4 +220,41 @@ }); }); + // ---------- + asyncTest('opacity', function() { + + function testDefaultOpacity() { + viewer.removeHandler('open', testDefaultOpacity); + var image = viewer.world.getItemAt(0); + strictEqual(image.getOpacity(), 0.5, 'image has default opacity'); + + image.setOpacity(1); + strictEqual(image.getOpacity(), 1, 'opacity is set correctly'); + + viewer.addHandler('open', testTileSourceOpacity); + viewer.open({ + tileSource: '/test/data/testpattern.dzi', + opacity: 0.25 + }); + } + + function testTileSourceOpacity() { + viewer.removeHandler('open', testTileSourceOpacity); + var image = viewer.world.getItemAt(0); + strictEqual(image.getOpacity(), 0.25, 'image has correct opacity'); + + image.setOpacity(0); + strictEqual(image.getOpacity(), 0, 'opacity is set correctly'); + + start(); + } + + viewer.addHandler('open', testDefaultOpacity); + + viewer.opacity = 0.5; + viewer.open({ + tileSource: '/test/data/testpattern.dzi', + }); + }); + })();