diff --git a/src/canvasdrawer.js b/src/canvasdrawer.js index 6f727f2c..cae12a56 100644 --- a/src/canvasdrawer.js +++ b/src/canvasdrawer.js @@ -82,9 +82,8 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr this._prepareNewFrame(); // prepare to draw a new frame tiledImages.forEach(function(tiledImage){ if (tiledImage.opacity !== 0 || tiledImage._preload) { - tiledImage._midDraw = true; - _this._updateViewportWithTiledImage(tiledImage); - tiledImage._midDraw = false; + // _this._updateViewportWithTiledImage(tiledImage); + _this._drawTiles(tiledImage); } else { tiledImage._needsDraw = false; @@ -189,102 +188,102 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr /* Methods from TiledImage */ - /** - * @private - * @inner - * Handles drawing a single TiledImage to the canvas - * - */ - _updateViewportWithTiledImage: function(tiledImage) { - var _this = this; - tiledImage._needsDraw = false; - tiledImage._tilesLoading = 0; - tiledImage.loadingCoverage = {}; + // /** + // * @private + // * @inner + // * Handles drawing a single TiledImage to the canvas + // * + // */ + // _updateViewportWithTiledImage: function(tiledImage) { + // var _this = this; + // tiledImage._needsDraw = false; + // tiledImage._tilesLoading = 0; + // tiledImage.loadingCoverage = {}; - // Reset tile's internal drawn state - while (tiledImage.lastDrawn.length > 0) { - var tile = tiledImage.lastDrawn.pop(); - tile.beingDrawn = false; - } + // // Reset tile's internal drawn state + // while (tiledImage.lastDrawn.length > 0) { + // var tile = tiledImage.lastDrawn.pop(); + // tile.beingDrawn = false; + // } - var drawArea = tiledImage.getDrawArea(); - if(!drawArea){ - return; - } + // var drawArea = tiledImage.getDrawArea(); + // if(!drawArea){ + // return; + // } - function updateTile(info){ - var tile = info.tile; - if(tile && tile.loaded){ - var needsDraw = _this._blendTile( - tiledImage, - tile, - tile.x, - tile.y, - info.level, - info.levelOpacity, - info.currentTime - ); - if(needsDraw){ - tiledImage._needsDraw = true; - } - } - } + // function updateTile(info){ + // var tile = info.tile; + // if(tile && tile.loaded){ + // var needsDraw = _this._blendTile( + // tiledImage, + // tile, + // tile.x, + // tile.y, + // info.level, + // info.levelOpacity, + // info.currentTime + // ); + // if(needsDraw){ + // tiledImage._needsDraw = true; + // } + // } + // } - var infoArray = tiledImage.getTileInfoForDrawing(); - infoArray.forEach(updateTile); + // var infoArray = tiledImage.getTileInfoForDrawing(); + // infoArray.forEach(updateTile); - this._drawTiles(tiledImage); + // this._drawTiles(tiledImage); - }, + // }, - /** - * @private - * @inner - * Updates the opacity of a tile according to the time it has been on screen - * to perform a fade-in. - * Updates coverage once a tile is fully opaque. - * Returns whether the fade-in has completed. - * - * @param {OpenSeadragon.Tile} tile - * @param {Number} x - * @param {Number} y - * @param {Number} level - * @param {Number} levelOpacity - * @param {Number} currentTime - * @returns {Boolean} - */ - _blendTile: function( tiledImage, tile, x, y, level, levelOpacity, currentTime ){ - var blendTimeMillis = 1000 * tiledImage.blendTime, - deltaTime, - opacity; + // /** + // * @private + // * @inner + // * Updates the opacity of a tile according to the time it has been on screen + // * to perform a fade-in. + // * Updates coverage once a tile is fully opaque. + // * Returns whether the fade-in has completed. + // * + // * @param {OpenSeadragon.Tile} tile + // * @param {Number} x + // * @param {Number} y + // * @param {Number} level + // * @param {Number} levelOpacity + // * @param {Number} currentTime + // * @returns {Boolean} + // */ + // _blendTile: function( tiledImage, tile, x, y, level, levelOpacity, currentTime ){ + // var blendTimeMillis = 1000 * tiledImage.blendTime, + // deltaTime, + // opacity; - if ( !tile.blendStart ) { - tile.blendStart = currentTime; - } + // if ( !tile.blendStart ) { + // tile.blendStart = currentTime; + // } - deltaTime = currentTime - tile.blendStart; - opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1; + // deltaTime = currentTime - tile.blendStart; + // opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1; - if ( tiledImage.alwaysBlend ) { - opacity *= levelOpacity; - } + // if ( tiledImage.alwaysBlend ) { + // opacity *= levelOpacity; + // } - tile.opacity = opacity; + // tile.opacity = opacity; - tiledImage.lastDrawn.push( tile ); + // tiledImage.lastDrawn.push( tile ); - if ( opacity === 1 ) { - tiledImage._setCoverage( tiledImage.coverage, level, x, y, true ); - tiledImage._hasOpaqueTile = true; - } else if ( deltaTime < blendTimeMillis ) { - return true; - } + // if ( opacity === 1 ) { + // tiledImage._setCoverage( tiledImage.coverage, level, x, y, true ); + // tiledImage._hasOpaqueTile = true; + // } else if ( deltaTime < blendTimeMillis ) { + // return true; + // } - return false; - }, + // return false; + // }, /** * @private diff --git a/src/htmldrawer.js b/src/htmldrawer.js index 00db0697..fbadf3c4 100644 --- a/src/htmldrawer.js +++ b/src/htmldrawer.js @@ -73,9 +73,8 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag this._prepareNewFrame(); // prepare to draw a new frame tiledImages.forEach(function(tiledImage){ if (tiledImage.opacity !== 0 || tiledImage._preload) { - tiledImage._midDraw = true; - _this._updateViewportWithTiledImage(tiledImage); - tiledImage._midDraw = false; + // _this._updateViewportWithTiledImage(tiledImage); + _this._drawTiles(tiledImage); } else { tiledImage._needsDraw = false; diff --git a/src/tiledimage.js b/src/tiledimage.js index ddb23aff..a8d937b0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -159,7 +159,6 @@ $.TiledImage = function( options ) { loadingCoverage: {}, // A '3d' dictionary [level][x][y] --> Boolean; shows what areas are loaded or are being loaded/blended. lastDrawn: [], // An unordered list of Tiles drawn last frame. lastResetTime: 0, // Last time for which the tiledImage was reset. - _midDraw: false, // Is the tiledImage currently updating the viewport? _needsDraw: true, // Does the tiledImage need to update the viewport again? _hasOpaqueTile: false, // Do we have even one fully opaque tile? _tilesLoading: 0, // The number of pending tile requests. @@ -297,8 +296,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }, /** - * Updates the TiledImage's bounds, animating if needed. - * @returns {Boolean} Whether the TiledImage animated. + * Updates the TiledImage's bounds, animating if needed. Based on the new + * bounds, updates the levels and tiles to be drawn into the viewport. + * @returns {Boolean} Whether the TiledImage needs to be drawn. */ update: function() { var xUpdated = this._xSpring.update(); @@ -306,7 +306,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag var scaleUpdated = this._scaleSpring.update(); var degreesUpdated = this._degreesSpring.update(); - this._updateTilesForViewport(); + this._updateLevelsForViewport(); + this._updateTilesInViewport(); if (xUpdated || yUpdated || scaleUpdated || degreesUpdated) { this._updateForScale(); @@ -318,6 +319,14 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return false; }, + /** + * Mark this TiledImage as having been drawn, so that it will only be drawn + * again if something changes about the image + */ + setDrawn: function(){ + this._needsDraw = false; + }, + /** * Destroy the TiledImage (unload current loaded tiles). */ @@ -903,7 +912,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @returns {Boolean} Whether the TiledImage should be flipped before rendering. */ getFlip: function() { - return !!this.flipped; + return this.flipped; }, /** @@ -911,11 +920,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @fires OpenSeadragon.TiledImage.event:bounds-change */ setFlip: function(flip) { - this.flipped = !!flip; + this.flipped = flip; + }, + + get flipped(){ + return this._flipped; + }, + set flipped(flipped){ + this._flipped = !!flipped; this._needsDraw = true; this._raiseBoundsChange(); }, + get debugMode(){ + return this._debugMode; + }, + set debugMode(debug){ + this._debugMode = !!debug; + this._needsDraw = true; + }, + /** * @returns {Number} The TiledImage's current opacity. */ @@ -928,11 +952,19 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @fires OpenSeadragon.TiledImage.event:opacity-change */ setOpacity: function(opacity) { + this.opacity = opacity; + }, + + get opacity() { + return this._opacity; + }, + + set opacity(opacity) { if (opacity === this.opacity) { return; } - this.opacity = opacity; + this._opacity = opacity; this._needsDraw = true; /** * Raised when the TiledImage's opacity is changed. @@ -1014,6 +1046,14 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return drawArea; }, + /** + * + * @returns {Array} Array of Tiles that make up the current view + */ + getTilesToDraw: function(){ + return this._tilesToDraw; + }, + /** * Get the point around which this tiled image is rotated * @private @@ -1024,24 +1064,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return this.getBoundsNoRotate(current).getCenter(); }, - /** - * @returns {String} The TiledImage's current compositeOperation. - */ - getCompositeOperation: function() { - return this.compositeOperation; + get compositeOperation(){ + return this._compositeOperation; }, - /** - * @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation. - * @fires OpenSeadragon.TiledImage.event:composite-operation-change - */ - setCompositeOperation: function(compositeOperation) { - var _this = this; - if (compositeOperation === this.compositeOperation) { + set compositeOperation(compositeOperation){ + + if (compositeOperation === this._compositeOperation) { return; } - - this.compositeOperation = compositeOperation; + this._compositeOperation = compositeOperation; this._needsDraw = true; /** * Raised when the TiledImage's opacity is changed. @@ -1054,23 +1086,24 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.raiseEvent('composite-operation-change', { - compositeOperation: this.compositeOperation + compositeOperation: this._compositeOperation }); - /** - * Raised when a TiledImage's opacity is changed. - * @event composite-operation-change - * @memberOf OpenSeadragon.TiledImage - * @type {object} - * @property {String} compositeOperation - The new compositeOperation value. - * @property {OpenSeadragon.Viewer} eventSource - A reference to the - * Viewer which raised the event. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.viewer.raiseEvent('composite-operation-change', { - compositeOperation: _this.compositeOperation, - tiledImage: _this - }); + }, + + /** + * @returns {String} The TiledImage's current compositeOperation. + */ + getCompositeOperation: function() { + return this._compositeOperation; + }, + + /** + * @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation. + * @fires OpenSeadragon.TiledImage.event:composite-operation-change + */ + setCompositeOperation: function(compositeOperation) { + this.compositeOperation = compositeOperation; //invokes setter }, /** @@ -1238,15 +1271,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }; }, - /** - * - * @returns {Array} Array of Tiles within the viewport which should be drawn - */ - getTileInfoForDrawing: function(){ - return this._tilesToDraw; - }, - _updateTilesForViewport: function(){ + _updateLevelsForViewport: function(){ var levelsInterval = this._getLevelsInterval(); var lowestLevel = levelsInterval.lowestLevel; var highestLevel = levelsInterval.highestLevel; @@ -1346,6 +1372,99 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag } else { this._setFullyLoaded(this._tilesLoading === 0); } + + // Update + + }, + + /** + * @private + * @inner + * Update all tiles that contribute to the current view + * + */ + _updateTilesInViewport: function() { + var _this = this; + this._tilesLoading = 0; + this.loadingCoverage = {}; + + // Reset tile's internal drawn state + while (this.lastDrawn.length > 0) { + var tile = this.lastDrawn.pop(); + tile.beingDrawn = false; + } + + + var drawArea = this.getDrawArea(); + if(!drawArea){ + return; + } + + function updateTile(info){ + var tile = info.tile; + if(tile && tile.loaded){ + var needsDraw = _this._blendTile( + tile, + tile.x, + tile.y, + info.level, + info.levelOpacity, + info.currentTime + ); + if(needsDraw){ + _this._needsDraw = true; + } + } + } + + this._tilesToDraw.forEach(updateTile); + + }, + + /** + * @private + * @inner + * Updates the opacity of a tile according to the time it has been on screen + * to perform a fade-in. + * Updates coverage once a tile is fully opaque. + * Returns whether the fade-in has completed. + * + * @param {OpenSeadragon.Tile} tile + * @param {Number} x + * @param {Number} y + * @param {Number} level + * @param {Number} levelOpacity + * @param {Number} currentTime + * @returns {Boolean} + */ + _blendTile: function(tile, x, y, level, levelOpacity, currentTime ){ + var blendTimeMillis = 1000 * this.blendTime, + deltaTime, + opacity; + + if ( !tile.blendStart ) { + tile.blendStart = currentTime; + } + + deltaTime = currentTime - tile.blendStart; + opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1; + + if ( this.alwaysBlend ) { + opacity *= levelOpacity; + } + + tile.opacity = opacity; + + this.lastDrawn.push( tile ); + + if ( opacity === 1 ) { + this._setCoverage( this.coverage, level, x, y, true ); + this._hasOpaqueTile = true; + } else if ( deltaTime < blendTimeMillis ) { + return true; + } + + return false; }, /** @@ -1840,14 +1959,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag _this._setTileLoaded(tile, data, cutoff, tileRequest); }; - // Check if we're mid-update; this can happen on IE8 because image load events for - // cached images happen immediately there - if ( !this._midDraw ) { - finish(); - } else { - // Wait until after the update, in case caching unloads any tiles - window.setTimeout( finish, 1); - } + + finish(); }, /** @@ -1889,22 +2002,20 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }); } /** - * Triggered when a tile has just been loaded in memory. That means that the - * image has been downloaded and can be modified before being drawn to the canvas. + * Triggered when a tile is loaded and pre-processing is compelete, + * and the tile is ready to draw. * * @event tile-ready * @memberof OpenSeadragon.Viewer * @type {object} - * @property {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object - * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile. * @property {OpenSeadragon.Tile} tile - The tile which has been loaded. + * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile. * @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable). */ _this.viewer.raiseEvent("tile-ready", { tile: tile, tiledImage: _this, - tileRequest: tileRequest, - data: data + tileRequest: tileRequest }); _this._needsDraw = true; } diff --git a/src/viewer.js b/src/viewer.js index 136a228b..d35156f0 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -434,7 +434,7 @@ $.Viewer = function( options ) { } } else if(this.useCanvas && $.supportsCanvas) { - this.drawer = new $.Drawer({ + this.drawer = new $.CanvasDrawer({ viewer: this, viewport: this.viewport, element: this.canvas, diff --git a/src/world.js b/src/world.js index c355d8e0..a30ed9f9 100644 --- a/src/world.js +++ b/src/world.js @@ -257,6 +257,9 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W */ draw: function() { this.viewer.drawer.draw(this._items); + this._items.forEach(function(item){ + item.setDrawn(); + }); this._needsDraw = false; }, diff --git a/test/demo/threejsdrawer.js b/test/demo/threejsdrawer.js index 6013d212..4fc39e30 100644 --- a/test/demo/threejsdrawer.js +++ b/test/demo/threejsdrawer.js @@ -7,6 +7,8 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ super(options); let _this = this; + this._stats = options.stats; // optional input of stats.js object to enable performance testing + // this.viewer set by parent constructor // this.canvas set by parent constructor, created and appended to the viewer container element this._camera = null; @@ -55,12 +57,16 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ this._setupRenderer(); } renderFrame(){ + // this._stats && this._stats.begin(); if(this._animationFrame) { cancelAnimationFrame(this._animationFrame); } this._animationFrame = requestAnimationFrame(()=>this.render()); + // this._stats && this._stats.end(); } render(){ + // this._stats && this._stats.begin(); + let numItems = this.viewer.world.getItemCount(); this._outputContext.clearRect(0, 0, this._outputCanvas.width, this._outputCanvas.height); //iterate over items to draw @@ -94,6 +100,8 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ if(this._renderingContinuously){ this.renderFrame(); } + + // this._stats && this._stats.end(); // console.log(this._renderer.info.memory, this._renderer.info.render.triangles); } renderContinuously(continuously){ @@ -208,7 +216,7 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ this.viewer.addHandler("viewport-change", () => this._viewportChangeHandler()); this.viewer.addHandler("home", () => this._viewportChangeHandler()); - this.viewer.addHandler("update-viewport", () => this.renderFrame()); + this.viewer.addHandler("update-viewport", () => this.render()); this._viewportChangeHandler(); } @@ -341,6 +349,8 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ _updateTiledImageRendering(tiledImage, tile){ + // this._stats && this._stats.begin(); + let scene = this._tiledImageMap[tiledImage[this._uuid]]; let bounds = this._tileMap[tile.cacheKey].userData._tileBounds @@ -367,6 +377,8 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ } } + // this._stats && this._stats.end(); + this.renderFrame(); } @@ -472,6 +484,7 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ } _viewportChangeHandler(){ + //this._stats && this._stats.begin(); let viewer = this.viewer; let viewerBounds = viewer.viewport.getBoundsNoRotate(true); @@ -493,9 +506,8 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{ let tiledImage = viewer.world.getItemAt(i); this._updateMeshIfNeeded(tiledImage); } - - this.renderFrame(); - + this.render(); + // this.renderFrame(); //this._stats && this._stats.end(); } _renderToClippingCanvas(item){ diff --git a/test/demo/webgl.html b/test/demo/webgl.html index 6d184feb..fe5eabec 100644 --- a/test/demo/webgl.html +++ b/test/demo/webgl.html @@ -6,6 +6,7 @@ +