mirror of
https://github.com/openseadragon/openseadragon.git
synced 2025-02-18 07:43:13 +03:00
Design of separated events: drop update data support for tile-loaded, use only invalidated event.
This commit is contained in:
parent
cd60aff5dc
commit
f127014f0f
@ -199,8 +199,9 @@ $.EventSource.prototype = {
|
||||
* calling the handler for each and awaiting async ones.
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to get handlers for.
|
||||
* @param {any} bindTarget - Bound target to return with the promise on finish
|
||||
*/
|
||||
getAwaitingHandler: function ( eventName) {
|
||||
getAwaitingHandler: function ( eventName, bindTarget ) {
|
||||
let events = this.events[ eventName ];
|
||||
if ( !events || !events.length ) {
|
||||
return null;
|
||||
@ -217,7 +218,7 @@ $.EventSource.prototype = {
|
||||
const length = events.length;
|
||||
function loop(index) {
|
||||
if ( index >= length || !events[ index ] ) {
|
||||
resolve("Resolved!");
|
||||
resolve(bindTarget);
|
||||
return null;
|
||||
}
|
||||
args.eventSource = source;
|
||||
@ -259,17 +260,18 @@ $.EventSource.prototype = {
|
||||
* This events awaits every asynchronous or promise-returning function.
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {Object} eventArgs - Event-specific data.
|
||||
* @param {?} [bindTarget = null] - Promise-resolved value on the event finish
|
||||
* @return {OpenSeadragon.Promise|undefined} - Promise resolved upon the event completion.
|
||||
*/
|
||||
raiseEventAwaiting: function ( eventName, eventArgs ) {
|
||||
raiseEventAwaiting: function ( eventName, eventArgs, bindTarget = null ) {
|
||||
//uncomment if you want to get a log of all events
|
||||
//$.console.log( "Awaiting event fired:", eventName );
|
||||
|
||||
const awaitingHandler = this.getAwaitingHandler(eventName);
|
||||
const awaitingHandler = this.getAwaitingHandler(eventName, bindTarget);
|
||||
if (awaitingHandler) {
|
||||
return awaitingHandler(this, eventArgs || {});
|
||||
}
|
||||
return $.Promise.resolve("No handler for this event registered.");
|
||||
return $.Promise.resolve(bindTarget);
|
||||
},
|
||||
|
||||
/**
|
||||
|
21
src/tile.js
21
src/tile.js
@ -567,25 +567,34 @@ $.Tile.prototype = {
|
||||
* @private
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
updateRenderTargetWithDataTransform: function (drawerId, supportedFormats, usePrivateCache) {
|
||||
updateRenderTargetWithDataTransform: function (drawerId, supportedFormats, usePrivateCache, processTimestamp) {
|
||||
// Now, if working cache exists, we set main cache to the working cache --> prepare
|
||||
const cache = this.getCache(this._wcKey);
|
||||
let cache = this.getCache(this._wcKey);
|
||||
if (cache) {
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache, this.processing);
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache).then(c => {
|
||||
if (c && processTimestamp && this.processing === processTimestamp) {
|
||||
this.updateRenderTarget();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If we requested restore, perform now
|
||||
if (this.__restore) {
|
||||
const cache = this.getCache(this.originalCacheKey);
|
||||
cache = this.getCache(this.originalCacheKey);
|
||||
|
||||
this.tiledImage._tileCache.restoreTilesThatShareOriginalCache(
|
||||
this, cache, this.__restoreRequestedFree
|
||||
);
|
||||
this.__restore = false;
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache, this.processing);
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache).then((c) => {
|
||||
if (c && processTimestamp && this.processing === processTimestamp) {
|
||||
this.updateRenderTarget();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $.Promise.resolve();
|
||||
cache = this.getCache();
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -223,32 +223,18 @@
|
||||
* @param drawerId
|
||||
* @param supportedTypes
|
||||
* @param keepInternalCopy
|
||||
* @param _shareTileUpdateStamp private param, updates render target (swap cache memory) for tiles that come
|
||||
* from the same tstamp batch
|
||||
* @return {OpenSeadragon.Promise<OpenSeadragon.SimpleCacheRecord|OpenSeadragon.CacheRecord>}
|
||||
|
||||
* @return {OpenSeadragon.Promise<OpenSeadragon.SimpleCacheRecord|OpenSeadragon.CacheRecord> | null}
|
||||
* reference to the cache processed for drawer rendering requirements, or null on error
|
||||
*/
|
||||
prepareForRendering(drawerId, supportedTypes, keepInternalCopy = true, _shareTileUpdateStamp = null) {
|
||||
|
||||
const fin = () => {
|
||||
// Locked update of render target to the tile that initiated processing
|
||||
if (_shareTileUpdateStamp) {
|
||||
for (let tile of this._tiles) {
|
||||
if (tile.processing === _shareTileUpdateStamp) {
|
||||
tile.updateRenderTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
prepareForRendering(drawerId, supportedTypes, keepInternalCopy = true) {
|
||||
// if not internal copy and we have no data, or we are ready to render, exit
|
||||
if (!this.loaded || supportedTypes.includes(this.type)) {
|
||||
fin();
|
||||
return $.Promise.resolve(this);
|
||||
}
|
||||
|
||||
if (!keepInternalCopy) {
|
||||
return this.transformTo(supportedTypes).then(fin);
|
||||
return this.transformTo(supportedTypes);
|
||||
}
|
||||
|
||||
// we can get here only if we want to render incompatible type
|
||||
@ -260,11 +246,10 @@
|
||||
internalCache = internalCache[drawerId];
|
||||
if (internalCache) {
|
||||
// already done
|
||||
fin();
|
||||
return $.Promise.resolve(this);
|
||||
} else {
|
||||
internalCache = this[DRAWER_INTERNAL_CACHE][drawerId] = new $.SimpleCacheRecord();
|
||||
}
|
||||
|
||||
internalCache = this[DRAWER_INTERNAL_CACHE][drawerId] = new $.SimpleCacheRecord();
|
||||
const conversionPath = $.convertor.getConversionPath(this.type, supportedTypes);
|
||||
if (!conversionPath) {
|
||||
$.console.error(`[getDataForRendering] Conversion ${this.type} ---> ${supportedTypes} cannot be done!`);
|
||||
@ -273,8 +258,8 @@
|
||||
internalCache.withTileReference(this._tRef);
|
||||
const selectedFormat = conversionPath[conversionPath.length - 1].target.value;
|
||||
return $.convertor.convert(this._tRef, this.data, this.type, selectedFormat).then(data => {
|
||||
internalCache.setDataAs(data, selectedFormat); // synchronous, SimpleCacheRecord
|
||||
return fin();
|
||||
internalCache.setDataAs(data, selectedFormat); // synchronous, SimpleCacheRecord call
|
||||
return internalCache;
|
||||
});
|
||||
}
|
||||
|
||||
@ -842,10 +827,6 @@
|
||||
}
|
||||
|
||||
cacheRecord.addTile(theTile, options.data, options.dataType);
|
||||
if (cacheKey === theTile.cacheKey) {
|
||||
theTile.tiledImage._needsDraw = true;
|
||||
}
|
||||
|
||||
this._freeOldRecordRoutine(theTile, options.cutoff || 0);
|
||||
return cacheRecord;
|
||||
}
|
||||
@ -949,10 +930,7 @@
|
||||
// We need to avoid async execution here: replace consumer instead of overwriting the data.
|
||||
const iterateTiles = [...consumer._tiles]; // unloadCacheForTile() will modify the array, use a copy
|
||||
for (let tile of iterateTiles) {
|
||||
if (tile.loaded || tile.loading) {
|
||||
this.unloadCacheForTile(tile, options.consumerKey, true);
|
||||
}
|
||||
|
||||
this.unloadCacheForTile(tile, options.consumerKey, true, false);
|
||||
}
|
||||
}
|
||||
if (this._cachesLoaded[options.consumerKey]) {
|
||||
@ -968,7 +946,7 @@
|
||||
// Only one cache got working item, other caches were idle: update cache: add the new cache
|
||||
// we can add since we removed above with unloadCacheForTile()
|
||||
for (let tile of tiles) {
|
||||
if (tile !== options.tile && (tile.loaded || tile.loading)) {
|
||||
if (tile !== options.tile) {
|
||||
tile.addCache(options.consumerKey, resultCache.data, resultCache.type, true, false);
|
||||
}
|
||||
}
|
||||
@ -985,7 +963,7 @@
|
||||
*/
|
||||
restoreTilesThatShareOriginalCache(tile, originalCache, freeIfUnused) {
|
||||
for (let t of originalCache._tiles) {
|
||||
this.unloadCacheForTile(t, t.cacheKey, freeIfUnused);
|
||||
this.unloadCacheForTile(t, t.cacheKey, freeIfUnused, false);
|
||||
delete t._caches[t.cacheKey];
|
||||
t.cacheKey = t.originalCacheKey;
|
||||
}
|
||||
@ -1016,7 +994,7 @@
|
||||
if ( prevTile.level <= cutoff ||
|
||||
prevTile.beingDrawn ||
|
||||
prevTile.loading ||
|
||||
prevTile.processing ) {
|
||||
prevTile.processing ) { //todo exempt from deletion, or block this routine on data updates
|
||||
continue;
|
||||
}
|
||||
if ( !worstTile ) {
|
||||
@ -1171,7 +1149,7 @@
|
||||
for (let key in tile._caches) {
|
||||
//we are 'ok' to remove tile caches here since we later call destroy on tile, otherwise
|
||||
//tile has count of its cache size --> would be inconsistent
|
||||
this.unloadCacheForTile(tile, key, destroy);
|
||||
this.unloadCacheForTile(tile, key, destroy, false);
|
||||
}
|
||||
//delete also the tile record
|
||||
if (deleteAtIndex !== undefined) {
|
||||
|
@ -2116,45 +2116,68 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
increment = 0,
|
||||
eventFinished = false;
|
||||
const _this = this,
|
||||
finishPromise = new $.Promise(r => {
|
||||
resolver = r;
|
||||
});
|
||||
now = $.now();
|
||||
|
||||
function completionCallback() {
|
||||
increment--;
|
||||
if (increment > 0) {
|
||||
return;
|
||||
}
|
||||
eventFinished = true;
|
||||
|
||||
//do not override true if set (false is default)
|
||||
tile.hasTransparency = tile.hasTransparency || _this.source.hasTransparency(
|
||||
undefined, tile.getUrl(), tile.ajaxHeaders, tile.postData
|
||||
);
|
||||
tile.updateRenderTarget(true);
|
||||
//make sure cache data is ready for drawing, if not, request the desired format
|
||||
const cache = tile.getCache(tile.cacheKey),
|
||||
requiredTypes = _this._drawer.getSupportedDataFormats();
|
||||
if (!cache) {
|
||||
$.console.warn("Tile %s not cached or not loaded at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
||||
resolver(tile);
|
||||
} else if (!requiredTypes.includes(cache.type)) {
|
||||
//initiate conversion as soon as possible if incompatible with the drawer
|
||||
cache.prepareForRendering(_this._drawer.getId(), requiredTypes, _this._drawer.options.usePrivateCache).then(cacheRef => {
|
||||
if (!cacheRef) {
|
||||
return cache.transformTo(requiredTypes);
|
||||
// tile.updateRenderTarget(true);
|
||||
// //make sure cache data is ready for drawing, if not, request the desired format
|
||||
// const cache = tile.getCache(),
|
||||
// requiredTypes = _this._drawer.getSupportedDataFormats();
|
||||
// if (!cache) {
|
||||
// $.console.warn("Tile %s not cached or not loaded at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
||||
// resolver();
|
||||
// } else if (!requiredTypes.includes(cache.type)) {
|
||||
// //initiate conversion as soon as possible if incompatible with the drawer
|
||||
// //either the cache is a new item in the system (do process), or the cache inherits data from elsewhere (no-op),
|
||||
// // or the cache was processed in this call
|
||||
// tile.transforming = now; // block any updates on the tile
|
||||
// cache.prepareForRendering(_this._drawer.getId(), requiredTypes, _this._drawer.options.usePrivateCache).then(cacheRef => {
|
||||
// if (!cacheRef) {
|
||||
// return cache.transformTo(requiredTypes);
|
||||
// }
|
||||
// if (tile.processing === now) {
|
||||
// tile.updateRenderTarget();
|
||||
// }
|
||||
// return cacheRef;
|
||||
// }).then(resolver);
|
||||
// } else {
|
||||
// resolver();
|
||||
// }
|
||||
|
||||
// TODO consider first running this event before we call tile-loaded...
|
||||
if (!tileCacheCreated) {
|
||||
// Tile-loaded not called on each tile, but only on tiles with new data! Verify we share the main cache
|
||||
const origCache = tile.getCache(tile.originalCacheKey);
|
||||
for (let t of origCache._tiles) {
|
||||
// if there exists a tile that has different main cache, inherit it as a main cache
|
||||
if (t.cacheKey !== tile.cacheKey) {
|
||||
// add reference also to the main cache, no matter what the other tile state has
|
||||
// completion of the invaldate event should take care of all such tiles
|
||||
const targetMainCache = t.getCache();
|
||||
tile.addCache(t.cacheKey, () => {
|
||||
$.console.error("Attempt to share main cache with existing tile should not trigger data getter!");
|
||||
return targetMainCache.data;
|
||||
}, targetMainCache.type, true, false);
|
||||
break;
|
||||
}
|
||||
return cacheRef;
|
||||
}).then(_ => {
|
||||
tile.loading = false;
|
||||
tile.loaded = true;
|
||||
tile.lastProcess = 1;
|
||||
resolver(tile);
|
||||
});
|
||||
} else {
|
||||
tile.loading = false;
|
||||
tile.loaded = true;
|
||||
tile.lastProcess = 1;
|
||||
resolver(tile);
|
||||
}
|
||||
|
||||
resolver();
|
||||
return;
|
||||
}
|
||||
// In case we did not succeed in tile restoration, request invalidation todo what about catch
|
||||
const updatePromise = _this.viewer.world.requestTileInvalidateEvent([tile], now, false, true);
|
||||
updatePromise.then(resolver);
|
||||
}
|
||||
|
||||
function getCompletionCallback() {
|
||||
@ -2168,18 +2191,27 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
|
||||
const fallbackCompletion = getCompletionCallback();
|
||||
|
||||
if (!tileCacheCreated) {
|
||||
// Tile-loaded not called on each tile, but only on tiles with new data! Verify we share the main cache
|
||||
const origCache = tile.getCache(tile.originalCacheKey);
|
||||
for (let t of origCache._tiles) {
|
||||
if (!t.processing && t.cacheKey !== tile.cacheKey) {
|
||||
const targetMainCache = t.getCache();
|
||||
tile.addCache(t.cacheKey, targetMainCache.data, targetMainCache.type, true, false);
|
||||
fallbackCompletion();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (!tileCacheCreated) {
|
||||
// // Tile-loaded not called on each tile, but only on tiles with new data! Verify we share the main cache
|
||||
// const origCache = tile.getCache(tile.originalCacheKey);
|
||||
// if (!origCache.__invStamp) {
|
||||
// for (let t of origCache._tiles) {
|
||||
// if (t.cacheKey !== tile.cacheKey) {
|
||||
// const targetMainCache = t.getCache();
|
||||
// tile.addCache(t.cacheKey, targetMainCache.data, targetMainCache.type, true, false);
|
||||
// fallbackCompletion();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // else todo: what if we somehow managed to finish before this tile gets attached? probably impossible if the tile is joined by original cache...
|
||||
// }
|
||||
|
||||
// // TODO ENSURE ONLY THESE TWO EVENTS CAN CALL TILE UPDATES
|
||||
// // prepare for the fact that tile routine can be called here too
|
||||
// tile.lastProcess = false;
|
||||
// tile.processing = now;
|
||||
// tile.transforming = false;
|
||||
|
||||
/**
|
||||
* Triggered when a tile has just been loaded in memory. That means that the
|
||||
@ -2197,13 +2229,24 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
|
||||
* @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
|
||||
* @property {OpenSeadragon.Promise} - Promise resolved when the tile gets fully loaded.
|
||||
* NOTE: do no await the promise in the handler: you will create a deadlock!
|
||||
* @property {function} getCompletionCallback - deprecated
|
||||
*/
|
||||
this.viewer.raiseEventAwaiting("tile-loaded", {
|
||||
tile: tile,
|
||||
tiledImage: this,
|
||||
tileRequest: tileRequest,
|
||||
promise: finishPromise,
|
||||
promise: new $.Promise(resolve => {
|
||||
resolver = () => {
|
||||
tile.loading = false;
|
||||
tile.loaded = true;
|
||||
tile.lastProcess = false;
|
||||
tile.processing = false;
|
||||
tile.transforming = false;
|
||||
this.redraw();
|
||||
resolve(tile);
|
||||
};
|
||||
}),
|
||||
get image() {
|
||||
$.console.error("[tile-loaded] event 'image' has been deprecated. Use 'tile.getData()' instead.");
|
||||
return data;
|
||||
@ -2219,8 +2262,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
},
|
||||
}).catch(() => {
|
||||
$.console.error("[tile-loaded] event finished with failure: there might be a problem with a plugin you are using.");
|
||||
}).then(() => {
|
||||
eventFinished = true;
|
||||
}).then(fallbackCompletion);
|
||||
},
|
||||
|
||||
|
32
src/world.js
32
src/world.js
@ -265,30 +265,34 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
* @param {Number} tStamp timestamp in milliseconds, if active timestamp of the same value is executing,
|
||||
* changes are added to the cycle, else they await next iteration
|
||||
* @param {Boolean} [restoreTiles=true] if true, tile processing starts from the tile original data
|
||||
* @param {Boolean} [_allowTileUnloaded=false] internal flag for calling on tiles that come new to the system
|
||||
* @fires OpenSeadragon.Viewer.event:tile-invalidated
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
requestTileInvalidateEvent: function(tilesToProcess, tStamp, restoreTiles = true) {
|
||||
requestTileInvalidateEvent: function(tilesToProcess, tStamp, restoreTiles = true, _allowTileUnloaded = false) {
|
||||
const tileList = [],
|
||||
markedTiles = [];
|
||||
for (const tile of tilesToProcess) {
|
||||
// We allow re-execution on tiles that are in process but have too low processing timestamp,
|
||||
// which must be solved by ensuring subsequent data calls in the suddenly outdated processing
|
||||
// pipeline take no effect.
|
||||
// TODO: cross writes on tile when processing cause memory errors - either ensure
|
||||
// tile makes NOOP for any execution that comes with older stamp, or prevent update routine
|
||||
// to happen simultanously
|
||||
if (!tile || !tile.loaded || (tile.processing && tile.processing <= tStamp) || tile.transforming) {
|
||||
if (!tile || (!_allowTileUnloaded && !tile.loaded) || tile.transforming) {
|
||||
continue;
|
||||
}
|
||||
// TODO: consider locking on the original cache, which should be read only
|
||||
// or lock the main cache, and compare with tile.processing tstamp
|
||||
const tileCache = tile.getCache();
|
||||
const tileCache = tile.getCache(tile.originalCacheKey);
|
||||
if (tileCache.__invStamp && tileCache.__invStamp >= tStamp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let t of tileCache._tiles) {
|
||||
// Mark all related tiles as processing and cache the references to unmark later on
|
||||
// Mark all related tiles as processing and cache the references to unmark later on,
|
||||
// last processing is set to old processing (null if finished)
|
||||
t.lastProcess = t.processing;
|
||||
t.processing = tStamp;
|
||||
markedTiles.push(t);
|
||||
}
|
||||
|
||||
tileCache.__invStamp = tStamp;
|
||||
tileList.push(tile);
|
||||
}
|
||||
|
||||
@ -309,11 +313,13 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
return eventTarget.raiseEventAwaiting('tile-invalidated', {
|
||||
tile: tile,
|
||||
tiledImage: tile.tiledImage,
|
||||
}).then(() => {
|
||||
if (tile.processing === tStamp) {
|
||||
}, tile.getCache(tile.originalCacheKey)).then(cacheKey => {
|
||||
if (cacheKey.__invStamp === tStamp) {
|
||||
// asynchronous finisher
|
||||
tile.transforming = tStamp;
|
||||
return tile.updateRenderTargetWithDataTransform(drawerId, supportedFormats, keepInternalCacheCopy);
|
||||
return tile.updateRenderTargetWithDataTransform(drawerId, supportedFormats, keepInternalCacheCopy, tStamp).then(() => {
|
||||
cacheKey.__invStamp = null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}).catch(e => {
|
||||
@ -323,7 +329,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
|
||||
return $.Promise.all(jobList).then(() => {
|
||||
for (let tile of markedTiles) {
|
||||
tile.lastProcess = tile.processing;
|
||||
tile.lastProcess = false;
|
||||
tile.processing = false;
|
||||
tile.transforming = false;
|
||||
}
|
||||
|
@ -47,8 +47,6 @@
|
||||
}
|
||||
const self = this;
|
||||
this.viewer = options.viewer;
|
||||
|
||||
this.viewer.addHandler('tile-loaded', applyFilters);
|
||||
this.viewer.addHandler('tile-invalidated', applyFilters);
|
||||
|
||||
// filterIncrement allows to determine whether a tile contains the
|
||||
@ -67,11 +65,19 @@
|
||||
}
|
||||
|
||||
const contextCopy = await tile.getData('context2d');
|
||||
|
||||
if (contextCopy.canvas.width === 0) {
|
||||
debugger;
|
||||
}
|
||||
|
||||
const currentIncrement = self.filterIncrement;
|
||||
for (let i = 0; i < processors.length; i++) {
|
||||
if (self.filterIncrement !== currentIncrement) {
|
||||
break;
|
||||
}
|
||||
if (contextCopy.canvas.width === 0) {
|
||||
debugger;
|
||||
}
|
||||
await processors[i](contextCopy);
|
||||
}
|
||||
|
||||
|
@ -54,18 +54,14 @@
|
||||
}
|
||||
|
||||
if (_pA) {
|
||||
viewer.removeHandler('tile-loaded', _pA);
|
||||
viewer.removeHandler('tile-invalidated', _pA);
|
||||
}
|
||||
if (_pB) {
|
||||
viewer.removeHandler('tile-loaded', _pB);
|
||||
viewer.removeHandler('tile-invalidated', _pB);
|
||||
}
|
||||
_pA = window.pluginA;
|
||||
_pB = window.pluginB;
|
||||
viewer.addHandler('tile-loaded', _pA, null, window.orderPluginA || 0);
|
||||
viewer.addHandler('tile-invalidated', _pA, null), window.orderPluginA || 0;
|
||||
viewer.addHandler('tile-loaded', _pB, null, window.orderPluginB || 0);
|
||||
viewer.addHandler('tile-invalidated', _pA, null, window.orderPluginA || 0);
|
||||
viewer.addHandler('tile-invalidated', _pB, null, window.orderPluginB || 0);
|
||||
viewer.requestInvalidate();
|
||||
} catch (error) {
|
||||
@ -119,9 +115,7 @@ window.orderPluginB = 0;
|
||||
</textarea>
|
||||
<textarea style="height: 120px; background-color: #e5e5e5" disabled>
|
||||
// Application of the plugins done automatically:
|
||||
viewer.addHandler('tile-loaded', window.pluginA, null, window.orderPluginA);
|
||||
viewer.addHandler('tile-invalidated', window.pluginA, null, window.orderPluginA);
|
||||
viewer.addHandler('tile-loaded', window.pluginB, null, window.orderPluginB);
|
||||
viewer.addHandler('tile-invalidated', window.pluginB, null, window.orderPluginB);
|
||||
viewer.requestInvalidate();
|
||||
</textarea>
|
@ -17,8 +17,10 @@
|
||||
}
|
||||
});
|
||||
|
||||
const PROMISE_REF_KEY = Symbol("_private_test_ref");
|
||||
|
||||
OpenSeadragon.getBuiltInDrawersForTest().forEach(testDrawer);
|
||||
// If yuu want to debug a specific drawer, use instead:
|
||||
// If you want to debug a specific drawer, use instead:
|
||||
// ['webgl'].forEach(testDrawer);
|
||||
|
||||
function getPluginCode(overlayColor = "rgba(0,0,255,0.5)") {
|
||||
@ -74,21 +76,31 @@
|
||||
context.finish(ctx, null, "context2d");
|
||||
}
|
||||
});
|
||||
|
||||
// Get promise reference to wait for tile ready
|
||||
viewer.addHandler('tile-loaded', e => {
|
||||
e.tile[PROMISE_REF_KEY] = e.promise;
|
||||
});
|
||||
}
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
// we test middle of the canvas, so that we can test both tiles or the output canvas of canvas drawer :)
|
||||
async function readTileData() {
|
||||
async function readTileData(tileRef = null) {
|
||||
// Get some time for viewer to load data
|
||||
await sleep(50);
|
||||
// make sure at least one tile loaded
|
||||
const tile = tileRef || viewer.world.getItemAt(0).getTilesToDraw()[0];
|
||||
await tile[PROMISE_REF_KEY];
|
||||
// Get some time for viewer to load data
|
||||
await sleep(50);
|
||||
|
||||
if (type === "canvas") {
|
||||
//test with the underlying canvas instead
|
||||
const canvas = viewer.drawer.canvas;
|
||||
return viewer.drawer.canvas.getContext("2d").getImageData(canvas.width/2, canvas.height/2, 1, 1);
|
||||
}
|
||||
|
||||
await sleep(200);
|
||||
|
||||
//else incompatible drawer for data getting
|
||||
const tile = viewer.world.getItemAt(0).getTilesToDraw()[0];
|
||||
const cache = tile.tile.getCache();
|
||||
if (!cache || !cache.loaded) return null;
|
||||
|
||||
@ -103,12 +115,8 @@
|
||||
const fnA = getPluginCode("rgba(0,0,255,1)");
|
||||
const fnB = getPluginCode("rgba(255,0,0,1)");
|
||||
|
||||
const crashTest = () => assert.ok(false, "Tile Invalidated event should not be called");
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', crashTest);
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', crashTest);
|
||||
viewer.addHandler('tile-invalidated', fnA);
|
||||
viewer.addHandler('tile-invalidated', fnB);
|
||||
|
||||
viewer.addHandler('open', async () => {
|
||||
await viewer.waitForFinishedJobsForTest();
|
||||
@ -120,6 +128,7 @@
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
await tile[PROMISE_REF_KEY]; // to be sure all tiles has finished before checking
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 2, `Tile ${getTileDescription(tile)} has only two caches - main & original`);
|
||||
@ -142,9 +151,7 @@
|
||||
const fnA = getPluginCode("rgba(0,0,255,1)");
|
||||
const fnB = getPluginCode("rgba(255,0,0,1)");
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', fnA);
|
||||
viewer.addHandler('tile-loaded', fnB, null, 1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, 1);
|
||||
// const promise = viewer.requestInvalidate();
|
||||
|
||||
@ -158,7 +165,6 @@
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Test swap
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', fnB);
|
||||
await viewer.requestInvalidate();
|
||||
|
||||
@ -170,7 +176,6 @@
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Now B gets applied last! Red
|
||||
viewer.addHandler('tile-loaded', fnB, null, -1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, -1);
|
||||
await viewer.requestInvalidate();
|
||||
// no change
|
||||
@ -182,6 +187,7 @@
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
await tile[PROMISE_REF_KEY]; // to be sure all tiles has finished before checking
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 2, `Tile ${getTileDescription(tile)} has only two caches - main & original`);
|
||||
@ -204,9 +210,7 @@
|
||||
const fnA = getPluginCode("rgba(0,255,0,1)");
|
||||
const fnB = getResetTileDataCode();
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', fnA);
|
||||
viewer.addHandler('tile-loaded', fnB, null, 1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, 1);
|
||||
// const promise = viewer.requestInvalidate();
|
||||
|
||||
@ -220,7 +224,6 @@
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Test swap - suddenly B applied since it was added later
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', fnB);
|
||||
await viewer.requestInvalidate();
|
||||
data = await readTileData();
|
||||
@ -229,7 +232,6 @@
|
||||
assert.equal(data.data[2], 255);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
viewer.addHandler('tile-loaded', fnB, null, -1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, -1);
|
||||
await viewer.requestInvalidate();
|
||||
data = await readTileData();
|
||||
@ -241,6 +243,7 @@
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
await tile[PROMISE_REF_KEY]; // to be sure all tiles has finished before checking
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 1, `Tile ${getTileDescription(tile)} has only single, original cache`);
|
||||
|
Loading…
x
Reference in New Issue
Block a user