From 947109718cb9c6c317da1dfa16ed5550584230de Mon Sep 17 00:00:00 2001 From: Aiosa Date: Sat, 28 Jan 2023 08:42:07 +0100 Subject: [PATCH 1/5] Ensure tile-loaded event completionCallback is called only once. Check when context2D used after cache creation. --- src/tiledimage.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 928e135d..8a062c92 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1655,17 +1655,17 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @param {XMLHttpRequest|undefined} tileRequest */ _setTileLoaded: function(tile, data, cutoff, tileRequest) { - var increment = 0, + var stopper = 1, + cached = false, _this = this; function getCompletionCallback() { - increment++; return completionCallback; } function completionCallback() { - increment--; - if (increment === 0) { + stopper--; + if (stopper > 0) { tile.loading = false; tile.loaded = true; tile.hasTransparency = _this.source.hasTransparency( @@ -1678,8 +1678,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag cutoff: cutoff, tiledImage: _this }); + cached = true; } _this._needsDraw = true; + } else if (tile.context2D && cached) { + $.console.error("The tile has been cached, yet tile.context2D was set afterwards." + + " The cache is orphaned now. To avoid this, increase the priority of 'tile-loaded' event " + + "attaching context2D property."); } } From 81d86570da2c184a528f3f9d5862c560885a57f8 Mon Sep 17 00:00:00 2001 From: Aiosa Date: Sat, 28 Jan 2023 14:08:00 +0100 Subject: [PATCH 2/5] Typo in the stopping comparison condition. --- src/tiledimage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 8a062c92..a98127b1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1665,7 +1665,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag function completionCallback() { stopper--; - if (stopper > 0) { + if (stopper >= 0) { tile.loading = false; tile.loaded = true; tile.hasTransparency = _this.source.hasTransparency( From 55e7d2439a2891e8fddca9bf3bd2ecf3f17bc2e1 Mon Sep 17 00:00:00 2001 From: Aiosa Date: Tue, 31 Jan 2023 08:05:02 +0100 Subject: [PATCH 3/5] Change completionCallback with 'tile-loaded' event to support original scenario of async completion notification with additional guarding flags. --- src/tiledimage.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index a98127b1..272b6aba 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1655,17 +1655,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @param {XMLHttpRequest|undefined} tileRequest */ _setTileLoaded: function(tile, data, cutoff, tileRequest) { - var stopper = 1, - cached = false, + var increment = 0, + completed = false, + eventFinished = false, _this = this; function getCompletionCallback() { + if (eventFinished) { + $.console.error("Event 'tile-loaded' argument getCompletionCallback must not be called asynchronously."); + } + increment++; return completionCallback; } function completionCallback() { - stopper--; - if (stopper >= 0) { + increment--; + if (increment === 0 && !completed) { + completed = true; tile.loading = false; tile.loaded = true; tile.hasTransparency = _this.source.hasTransparency( @@ -1678,13 +1684,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag cutoff: cutoff, tiledImage: _this }); - cached = true; } _this._needsDraw = true; - } else if (tile.context2D && cached) { - $.console.error("The tile has been cached, yet tile.context2D was set afterwards." + - " The cache is orphaned now. To avoid this, increase the priority of 'tile-loaded' event " + - "attaching context2D property."); } } @@ -1717,6 +1718,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag getCompletionCallback: getCompletionCallback }); // In case the completion callback is never called, we at least force it once. + eventFinished = true; getCompletionCallback()(); }, From 57486732b102d68a83f525394f1b27d561b2a42f Mon Sep 17 00:00:00 2001 From: Aiosa Date: Wed, 1 Feb 2023 10:25:10 +0100 Subject: [PATCH 4/5] Prevent early tile completion with call order instead of guard flag. Improve getCompletionCallback docs. --- src/tiledimage.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 272b6aba..1ff579fc 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1656,13 +1656,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag */ _setTileLoaded: function(tile, data, cutoff, tileRequest) { var increment = 0, - completed = false, eventFinished = false, _this = this; function getCompletionCallback() { if (eventFinished) { - $.console.error("Event 'tile-loaded' argument getCompletionCallback must not be called asynchronously."); + $.console.error("Event 'tile-loaded' argument getCompletionCallback must be called synchronously. " + + "Its return value should be called asynchronously."); } increment++; return completionCallback; @@ -1670,8 +1670,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag function completionCallback() { increment--; - if (increment === 0 && !completed) { - completed = true; + if (increment === 0) { tile.loading = false; tile.loaded = true; tile.hasTransparency = _this.source.hasTransparency( @@ -1705,7 +1704,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * when the asynchronous processing of the image is done. The image will be * marked as entirely loaded when the callback has been called once for each * call to getCompletionCallback. + * Note: if you do not process the tile in asynchronous context + * (timeout, Promises, async, callbacks such as image.onload ...), do not use this function. */ + + var fallbackCompletion = getCompletionCallback(); this.viewer.raiseEvent("tile-loaded", { tile: tile, tiledImage: this, @@ -1717,9 +1720,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag data: data, getCompletionCallback: getCompletionCallback }); - // In case the completion callback is never called, we at least force it once. eventFinished = true; - getCompletionCallback()(); + // In case the completion callback is never called, we at least force it once. + fallbackCompletion(); }, /** From 37d4f62ce9ffd773d755d9ade6f42fa972368e64 Mon Sep 17 00:00:00 2001 From: Aiosa Date: Thu, 2 Feb 2023 17:18:12 +0100 Subject: [PATCH 5/5] Remove discouraging note on getCompletionCallback use docs. --- src/tiledimage.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 1ff579fc..467e4733 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1704,8 +1704,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * when the asynchronous processing of the image is done. The image will be * marked as entirely loaded when the callback has been called once for each * call to getCompletionCallback. - * Note: if you do not process the tile in asynchronous context - * (timeout, Promises, async, callbacks such as image.onload ...), do not use this function. */ var fallbackCompletion = getCompletionCallback();