From 7ad748799e8d52d986d5da782e3d87272e32ed99 Mon Sep 17 00:00:00 2001 From: "P. Schueffler" Date: Thu, 20 Jul 2023 14:45:15 +0200 Subject: [PATCH 1/8] Pr for upstream (#3) Added maxTilesPerFrame --- src/openseadragon.js | 7 +++++ src/tiledimage.js | 61 +++++++++++++++++++++++++++----------------- src/viewer.js | 1 + 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 278f61c8..b081365e 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -290,6 +290,12 @@ * @property {Number} [rotationIncrement=90] * The number of degrees to rotate right or left when the rotate buttons or keyboard shortcuts are activated. * + * @property {Number} [maxTilesPerFrame=1] + * The number of tiles loaded per frame. As the frame rate of the client's machine is usually high (e.g., 50 fps), + * one tile per frame should be a good choice. However, for large screens or lower frame rates, the number of + * loaded tiles per frame can be adjusted here. (Note that the actual frame rate is given by the client's + * browser and machine). + * * @property {Number} [pixelsPerWheelLine=40] * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line. * @@ -1288,6 +1294,7 @@ function OpenSeadragon( options ){ preserveImageSizeOnResize: false, // requires autoResize=true minScrollDeltaTime: 50, rotationIncrement: 90, + maxTilesPerFrame: 1, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE diff --git a/src/tiledimage.js b/src/tiledimage.js index 6ad0cf66..59792431 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -182,7 +182,8 @@ $.TiledImage = function( options ) { opacity: $.DEFAULT_SETTINGS.opacity, preload: $.DEFAULT_SETTINGS.preload, compositeOperation: $.DEFAULT_SETTINGS.compositeOperation, - subPixelRoundingForTransparency: $.DEFAULT_SETTINGS.subPixelRoundingForTransparency + subPixelRoundingForTransparency: $.DEFAULT_SETTINGS.subPixelRoundingForTransparency, + maxTilesPerFrame: $.DEFAULT_SETTINGS.maxTilesPerFrame }, options ); this._preload = this.preload; @@ -1208,7 +1209,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag var levelsInterval = this._getLevelsInterval(); var lowestLevel = levelsInterval.lowestLevel; var highestLevel = levelsInterval.highestLevel; - var bestTile = null; + var bestTiles = []; var haveDrawn = false; var currentTime = $.now(); @@ -1253,7 +1254,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag ); // Update the level and keep track of 'best' tile to load - bestTile = this._updateLevel( + bestTiles = this._updateLevel( haveDrawn, drawLevel, level, @@ -1261,7 +1262,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag levelVisibility, drawArea, currentTime, - bestTile + bestTiles ); // Stop the loop if lower-res tiles would all be covered by @@ -1274,9 +1275,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag // Perform the actual drawing this._drawTiles(this.lastDrawn); - // Load the new 'best' tile - if (bestTile && !bestTile.context2D) { - this._loadTile(bestTile, currentTime); + // Load the new 'best' n tiles + if (bestTiles) { + bestTiles.forEach(function (tile) { + if (tile && !tile.context2D) { + this._loadTile(tile, currentTime); + } + }, this); this._needsDraw = true; this._setFullyLoaded(false); } else { @@ -1335,7 +1340,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @param {Number} levelVisibility * @param {OpenSeadragon.Rect} drawArea * @param {Number} currentTime - * @param {OpenSeadragon.Tile} best - The current "best" tile to draw. + * @param {OpenSeadragon.Tile[]} best - The current "best" n tiles to draw. */ _updateLevel: function(haveDrawn, drawLevel, level, levelOpacity, levelVisibility, drawArea, currentTime, best) { @@ -1360,7 +1365,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @property {Object} topleft deprecated, use drawArea instead * @property {Object} bottomright deprecated, use drawArea instead * @property {Object} currenttime - * @property {Object} best + * @property {Object[]} best * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.viewer.raiseEvent('update-level', { @@ -1449,7 +1454,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @param {OpenSeadragon.Point} viewportCenter * @param {Number} numberOfTiles * @param {Number} currentTime - * @param {OpenSeadragon.Tile} best - The current "best" tile to draw. + * @param {OpenSeadragon.Tile[]} best - The current "best" tiles to draw. */ _updateTile: function( haveDrawn, drawLevel, x, y, level, levelOpacity, levelVisibility, viewportCenter, numberOfTiles, currentTime, best){ @@ -1538,7 +1543,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag // the tile is already in the download queue this._tilesLoading++; } else if (!loadingCoverage) { - best = this._compareTiles( best, tile ); + best = this._compareTiles( best, tile, this.maxTilesPerFrame ); } return best; @@ -1912,24 +1917,34 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * @private * @inner - * Determines whether the 'last best' tile for the area is better than the + * Determines the 'best tiles' from the given 'last best' tiles and the * tile in question. * - * @param {OpenSeadragon.Tile} previousBest - * @param {OpenSeadragon.Tile} tile - * @returns {OpenSeadragon.Tile} The new best tile. + * @param {OpenSeadragon.Tile[]} previousBest The best tiles so far. + * @param {OpenSeadragon.Tile} tile The new tile to consider. + * @param {Number} maxNTiles The max number of best tiles. + * @returns {OpenSeadragon.Tile[]} The new best tiles. */ - _compareTiles: function( previousBest, tile ) { + _compareTiles: function( previousBest, tile, maxNTiles ) { if ( !previousBest ) { - return tile; + return [tile]; } - - if ( tile.visibility > previousBest.visibility ) { - return tile; - } else if ( tile.visibility === previousBest.visibility ) { - if ( tile.squaredDistance < previousBest.squaredDistance ) { - return tile; + previousBest.push(tile); + previousBest.sort(function (a, b) { + if (a === null) { + return 1; } + if (b === null) { + return -1; + } + if (a.visibility === b.visibility) { + return (a.squaredDistance - b.squaredDistance); + } else { + return (a.visibility - b.visibility); + } + }); + if (previousBest.length > maxNTiles) { + previousBest.pop(); } return previousBest; }, diff --git a/src/viewer.js b/src/viewer.js index 0a25238c..d82a2d77 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1604,6 +1604,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, minZoomImageRatio: _this.minZoomImageRatio, wrapHorizontal: _this.wrapHorizontal, wrapVertical: _this.wrapVertical, + maxTilesPerFrame: _this.maxTilesPerFrame, immediateRender: _this.immediateRender, blendTime: _this.blendTime, alwaysBlend: _this.alwaysBlend, From 9684a83b8c4c058bfb21420ce9c51482bdae3a04 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 20 Jul 2023 15:17:02 +0200 Subject: [PATCH 2/8] Updated Comment --- src/openseadragon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index b081365e..2cb2ac0d 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -293,8 +293,8 @@ * @property {Number} [maxTilesPerFrame=1] * The number of tiles loaded per frame. As the frame rate of the client's machine is usually high (e.g., 50 fps), * one tile per frame should be a good choice. However, for large screens or lower frame rates, the number of - * loaded tiles per frame can be adjusted here. (Note that the actual frame rate is given by the client's - * browser and machine). + * loaded tiles per frame can be adjusted here. Reasonable values might be 2 or 3 tiles per frame. + * (Note that the actual frame rate is given by the client's browser and machine). * * @property {Number} [pixelsPerWheelLine=40] * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line. From ccb4ae9f8664d38e9e7939194430cdaf611fc703 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 20 Jul 2023 17:27:49 +0200 Subject: [PATCH 3/8] Fixed _updateViewport to come to a fullyLoaded state with n tiles. Improved _compareTiles --- src/tiledimage.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 59792431..e28ed224 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1276,7 +1276,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._drawTiles(this.lastDrawn); // Load the new 'best' n tiles - if (bestTiles) { + if (bestTiles && bestTiles.length > 0) { bestTiles.forEach(function (tile) { if (tile && !tile.context2D) { this._loadTile(tile, currentTime); @@ -1930,20 +1930,20 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return [tile]; } previousBest.push(tile); - previousBest.sort(function (a, b) { - if (a === null) { - return 1; - } - if (b === null) { - return -1; - } - if (a.visibility === b.visibility) { - return (a.squaredDistance - b.squaredDistance); - } else { - return (a.visibility - b.visibility); - } - }); if (previousBest.length > maxNTiles) { + previousBest.sort(function (a, b) { + if (a === null) { + return 1; + } + if (b === null) { + return -1; + } + if (a.visibility === b.visibility) { + return (a.squaredDistance - b.squaredDistance); + } else { + return (a.visibility - b.visibility); + } + }); previousBest.pop(); } return previousBest; From 17873001b8ad7a3f14f470dcebbe6dbb843fa3b0 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 28 Jul 2023 13:15:47 +0200 Subject: [PATCH 4/8] outsourced tile sorting from tile comparing. --- src/tiledimage.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index e28ed224..42319144 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1931,24 +1931,35 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag } previousBest.push(tile); if (previousBest.length > maxNTiles) { - previousBest.sort(function (a, b) { - if (a === null) { - return 1; - } - if (b === null) { - return -1; - } - if (a.visibility === b.visibility) { - return (a.squaredDistance - b.squaredDistance); - } else { - return (a.visibility - b.visibility); - } - }); + this._sortTiles(previousBest); previousBest.pop(); } return previousBest; }, + /** + * @private + * @inner + * Sorts tiles in an array according to distance and visibility. + * + * @param {OpenSeadragon.Tile[]} tiles The tiles. + */ + _sortTiles: function( tiles ) { + tiles.sort(function (a, b) { + if (a === null) { + return 1; + } + if (b === null) { + return -1; + } + if (a.visibility === b.visibility) { + return (a.squaredDistance - b.squaredDistance); + } else { + return (a.visibility - b.visibility); + } + }); + }, + /** * @private * @inner From 13955b0bf680e4c9d819965c2591ec626742a4d2 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 31 Jul 2023 10:14:27 +0200 Subject: [PATCH 5/8] added demo with maxTilesPerFrame = 3 --- test/demo/MaxNTilesPerFrame.html | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/demo/MaxNTilesPerFrame.html diff --git a/test/demo/MaxNTilesPerFrame.html b/test/demo/MaxNTilesPerFrame.html new file mode 100644 index 00000000..f24a8b7f --- /dev/null +++ b/test/demo/MaxNTilesPerFrame.html @@ -0,0 +1,35 @@ + + + + OpenSeadragon Zoomify Demo + + + + + +
+ Simple demo page to show a default OpenSeadragon viewer with a Zoomify tile source. +
+
+ + + From 55a05963a2f126389ea75138f809eb6f3f94e718 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 2 Aug 2023 20:30:30 +0200 Subject: [PATCH 6/8] Sort N tiles always for a better look and feel. --- src/tiledimage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 42319144..0bedb701 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1930,8 +1930,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return [tile]; } previousBest.push(tile); + this._sortTiles(previousBest); if (previousBest.length > maxNTiles) { - this._sortTiles(previousBest); previousBest.pop(); } return previousBest; From f8ad0acfa474ca76afb23b4de78ade610c0b42d2 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 2 Aug 2023 21:04:43 +0200 Subject: [PATCH 7/8] Sort the N tiles only once instead of when adding new indiviual tiles --- src/tiledimage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 0bedb701..869d3633 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1437,6 +1437,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag } } + this._sortTiles(best); return best; }, @@ -1930,8 +1931,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return [tile]; } previousBest.push(tile); - this._sortTiles(previousBest); if (previousBest.length > maxNTiles) { + this._sortTiles(previousBest); previousBest.pop(); } return previousBest; From 0bdd807d9d027fc418aef59b768b3f1c56f27ed9 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 8 Aug 2023 11:05:33 +0200 Subject: [PATCH 8/8] Cleaned code according to comments in PR --- src/tiledimage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 869d3633..0bedb701 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1437,7 +1437,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag } } - this._sortTiles(best); return best; }, @@ -1931,8 +1930,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return [tile]; } previousBest.push(tile); + this._sortTiles(previousBest); if (previousBest.length > maxNTiles) { - this._sortTiles(previousBest); previousBest.pop(); } return previousBest;