From 44395662d1368e58632686056f0ce580eec2d2d1 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 19 Apr 2015 16:10:37 -0400 Subject: [PATCH] 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,