Fix issues with tile reference in cache: keep the 'most fresh' ref.

This commit is contained in:
Aiosa 2024-02-11 17:18:03 +01:00
parent d91df0126b
commit 63f0adbc15
3 changed files with 45 additions and 21 deletions

View File

@ -513,12 +513,14 @@ $.Tile.prototype = {
* @return {OpenSeadragon.CacheRecord} * @return {OpenSeadragon.CacheRecord}
*/ */
getCache: function(key = this.cacheKey) { getCache: function(key = this.cacheKey) {
return this._caches[key]; const cache = this._caches[key];
if (cache) {
cache.withTileReference(this);
}
return cache;
}, },
/** /**
* TODO: set cache might be misleading name since we do not update data,
* this should be either changed or method renamed...
* Set tile cache, possibly multiple with custom key * Set tile cache, possibly multiple with custom key
* @param {string} key cache key, must be unique (we recommend re-using this.cacheTile * @param {string} key cache key, must be unique (we recommend re-using this.cacheTile
* value and extend it with some another unique content, by default overrides the existing * value and extend it with some another unique content, by default overrides the existing
@ -600,6 +602,7 @@ $.Tile.prototype = {
/** /**
* Get the ratio between current and original size. * Get the ratio between current and original size.
* @function * @function
* @deprecated
* @returns {number} * @returns {number}
*/ */
getScaleForEdgeSmoothing: function() { getScaleForEdgeSmoothing: function() {

View File

@ -128,14 +128,13 @@
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed * @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
*/ */
getDataAs(type = this._type, copy = true) { getDataAs(type = this._type, copy = true) {
const referenceTile = this._tiles[0];
if (this.loaded) { if (this.loaded) {
if (type === this._type) { if (type === this._type) {
return copy ? $.convertor.copy(referenceTile, this._data, type) : this._promise; return copy ? $.convertor.copy(this._tRef, this._data, type) : this._promise;
} }
return this._getDataAsUnsafe(referenceTile, this._data, type, copy); return this._getDataAsUnsafe(this._tRef, this._data, type, copy);
} }
return this._promise.then(data => this._getDataAsUnsafe(referenceTile, data, type, copy)); return this._promise.then(data => this._getDataAsUnsafe(this._tRef, data, type, copy));
} }
_getDataAsUnsafe(referenceTile, data, type, copy) { _getDataAsUnsafe(referenceTile, data, type, copy) {
@ -161,7 +160,6 @@
* @param {Array<string>} supportedTypes required data (or one of) type(s) * @param {Array<string>} supportedTypes required data (or one of) type(s)
* @param {boolean} keepInternalCopy if true, the cache keeps internally the drawer data * @param {boolean} keepInternalCopy if true, the cache keeps internally the drawer data
* until 'setData' is called * until 'setData' is called
* todo: keep internal copy is not configurable and always enforced -> set as option for osd?
* @returns {any|undefined} desired data if available, undefined if conversion must be done * @returns {any|undefined} desired data if available, undefined if conversion must be done
*/ */
getDataForRendering(supportedTypes, keepInternalCopy = true) { getDataForRendering(supportedTypes, keepInternalCopy = true) {
@ -176,7 +174,7 @@
} }
if (internalCache) { if (internalCache) {
internalCache.withTemporaryTileRef(this._tiles[0]); internalCache.withTileReference(this._tRef);
} else { } else {
internalCache = this; internalCache = this;
} }
@ -201,7 +199,6 @@
* @return {OpenSeadragon.Promise<OpenSeadragon.SimpleCacheRecord|OpenSeadragon.CacheRecord>} * @return {OpenSeadragon.Promise<OpenSeadragon.SimpleCacheRecord|OpenSeadragon.CacheRecord>}
*/ */
prepareForRendering(supportedTypes, keepInternalCopy = true) { prepareForRendering(supportedTypes, keepInternalCopy = true) {
const referenceTile = this._tiles[0];
// if not internal copy and we have no data, bypass rendering // if not internal copy and we have no data, bypass rendering
if (!this.loaded) { if (!this.loaded) {
return $.Promise.resolve(this); return $.Promise.resolve(this);
@ -214,9 +211,9 @@
$.console.error(`[getDataForRendering] Conversion conversion ${this.type} ---> ${supportedTypes} cannot be done!`); $.console.error(`[getDataForRendering] Conversion conversion ${this.type} ---> ${supportedTypes} cannot be done!`);
return $.Promise.resolve(this); return $.Promise.resolve(this);
} }
internalCache.withTemporaryTileRef(referenceTile); internalCache.withTileReference(this._tRef);
const selectedFormat = conversionPath[conversionPath.length - 1].target.value; const selectedFormat = conversionPath[conversionPath.length - 1].target.value;
return $.convertor.convert(referenceTile, this.data, this.type, selectedFormat).then(data => { return $.convertor.convert(this._tRef, this.data, this.type, selectedFormat).then(data => {
internalCache.setDataAs(data, selectedFormat); internalCache.setDataAs(data, selectedFormat);
return internalCache; return internalCache;
}); });
@ -276,6 +273,16 @@
} }
} }
/**
* Conversion requires tile references:
* keep the most 'up to date' ref here. It is called and managed automatically.
* @param {OpenSeadragon.Tile} ref
* @private
*/
withTileReference(ref) {
this._tRef = ref;
}
/** /**
* Set initial state, prepare for usage. * Set initial state, prepare for usage.
* Must not be called on active cache, e.g. first call destroy(). * Must not be called on active cache, e.g. first call destroy().
@ -318,6 +325,7 @@
this._tiles = null; this._tiles = null;
this._data = null; this._data = null;
this._type = null; this._type = null;
this._tRef = null;
this._promise = null; this._promise = null;
} }
@ -359,6 +367,10 @@
for (let i = 0; i < this._tiles.length; i++) { for (let i = 0; i < this._tiles.length; i++) {
if (this._tiles[i] === tile) { if (this._tiles[i] === tile) {
this._tiles.splice(i, 1); this._tiles.splice(i, 1);
if (this._tRef === tile) {
// keep fresh ref
this._tRef = this._tiles[i - 1];
}
return true; return true;
} }
} }
@ -422,7 +434,7 @@
const internal = this[DRAWER_INTERNAL_CACHE]; const internal = this[DRAWER_INTERNAL_CACHE];
if (internal) { if (internal) {
// TODO: if update will be greedy uncomment (see below) // TODO: if update will be greedy uncomment (see below)
//internal.withTemporaryTileRef(this._tiles[0]); //internal.withTileReference(this._tRef);
internal.setDataAs(data, type); internal.setDataAs(data, type);
} }
this._triggerNeedsDraw(); this._triggerNeedsDraw();
@ -436,7 +448,7 @@
const internal = this[DRAWER_INTERNAL_CACHE]; const internal = this[DRAWER_INTERNAL_CACHE];
if (internal) { if (internal) {
// TODO: if update will be greedy uncomment (see below) // TODO: if update will be greedy uncomment (see below)
//internal.withTemporaryTileRef(this._tiles[0]); //internal.withTileReference(this._tRef);
internal.setDataAs(data, type); internal.setDataAs(data, type);
} }
this._triggerNeedsDraw(); this._triggerNeedsDraw();
@ -452,7 +464,6 @@
*/ */
_convert(from, to) { _convert(from, to) {
const convertor = $.convertor, const convertor = $.convertor,
referenceTile = this._tiles[0],
conversionPath = convertor.getConversionPath(from, to); conversionPath = convertor.getConversionPath(from, to);
if (!conversionPath) { if (!conversionPath) {
$.console.error(`[CacheRecord._convert] Conversion conversion ${from} ---> ${to} cannot be done!`); $.console.error(`[CacheRecord._convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
@ -470,7 +481,7 @@
return $.Promise.resolve(x); return $.Promise.resolve(x);
} }
let edge = conversionPath[i]; let edge = conversionPath[i];
let y = edge.transform(referenceTile, x); let y = edge.transform(_this._tRef, x);
if (y === undefined) { if (y === undefined) {
_this.loaded = false; _this.loaded = false;
throw `[CacheRecord._convert] data mid result undefined value (while converting using ${edge}})`; throw `[CacheRecord._convert] data mid result undefined value (while converting using ${edge}})`;
@ -530,7 +541,7 @@
* compatible api with CacheRecord where tile refs are known. * compatible api with CacheRecord where tile refs are known.
* @param {OpenSeadragon.Tile} referenceTile reference tile for conversion * @param {OpenSeadragon.Tile} referenceTile reference tile for conversion
*/ */
withTemporaryTileRef(referenceTile) { withTileReference(referenceTile) {
this._temporaryTileRef = referenceTile; this._temporaryTileRef = referenceTile;
} }
@ -727,9 +738,12 @@
for ( let i = this._tilesLoaded.length - 1; i >= 0; i-- ) { for ( let i = this._tilesLoaded.length - 1; i >= 0; i-- ) {
prevTile = this._tilesLoaded[ i ]; prevTile = this._tilesLoaded[ i ];
if ( prevTile.level <= cutoff || prevTile.beingDrawn ) { if ( prevTile.level <= cutoff ||
prevTile.beingDrawn ||
prevTile.loading ) {
continue; continue;
} else if ( !worstTile ) { }
if ( !worstTile ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
continue; continue;

View File

@ -2045,7 +2045,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/ */
_onTileLoad: function( tile, time, data, errorMsg, tileRequest, dataType ) { _onTileLoad: function( tile, time, data, errorMsg, tileRequest, dataType ) {
//data is set to null on error by image loader, allow custom falsey values (e.g. 0) //data is set to null on error by image loader, allow custom falsey values (e.g. 0)
if ( data === null ) { if ( data === null || data === undefined ) {
$.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.getUrl(), errorMsg ); $.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.getUrl(), errorMsg );
/** /**
* Triggered when a tile fails to load. * Triggered when a tile fails to load.
@ -2095,6 +2095,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @param {?Boolean} [withEvent=true] do not trigger event if true * @param {?Boolean} [withEvent=true] do not trigger event if true
*/ */
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) { _setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) {
const originalDelete = tile.unload;
tile.unload = (function () {
throw `Cannot unload tile while being loaded!`;
});
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
// does nothing if tile.cacheKey already present // does nothing if tile.cacheKey already present
tile.addCache(tile.cacheKey, data, dataType, false); tile.addCache(tile.cacheKey, data, dataType, false);
@ -2124,17 +2129,19 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
resolver(tile); resolver(tile);
} else if (!requiredTypes.includes(cache.type)) { } else if (!requiredTypes.includes(cache.type)) {
//initiate conversion as soon as possible if incompatible with the drawer //initiate conversion as soon as possible if incompatible with the drawer
cache.prepareForRendering(requiredTypes).then(cacheRef => { cache.prepareForRendering(requiredTypes, _this.viewer.drawer.options.detachedCache).then(cacheRef => {
if (!cacheRef) { if (!cacheRef) {
return cache.transformTo(requiredTypes); return cache.transformTo(requiredTypes);
} }
return cacheRef; return cacheRef;
}).then(_ => { }).then(_ => {
tile.unload = originalDelete;
tile.loading = false; tile.loading = false;
tile.loaded = true; tile.loaded = true;
resolver(tile); resolver(tile);
}); });
} else { } else {
tile.unload = originalDelete;
tile.loading = false; tile.loading = false;
tile.loaded = true; tile.loaded = true;
resolver(tile); resolver(tile);