Debug fallback to canvas drawer, small bugfixes, add support for drawer cleanup in cache.

This commit is contained in:
Aiosa 2025-01-10 14:46:52 +01:00
parent 426700b1c6
commit cb06a5c0fb
4 changed files with 73 additions and 29 deletions

View file

@ -38,7 +38,14 @@
* @typedef BaseDrawerOptions
* @memberOf OpenSeadragon
* @property {boolean} [usePrivateCache=false] specify whether the drawer should use
* detached (=internal) cache object in case it has to perform type conversion
* detached (=internal) cache object in case it has to perform custom type conversion atop
* what cache performs. In that case, drawer must implement dataCreate() which gets data in one
* of formats the drawer declares as supported. This method must return object to be used during drawing.
* You should probably implement also dataFree() to provide cleanup logics.
*
* @property {boolean} [preloadCache=true]
* When dataCreate is used, it can be applied offline (asynchronously) during data processing = preloading,
* or just in time before rendering (if necessary). Preloading supports
*/
const OpenSeadragon = $; // (re)alias back to OpenSeadragon for JSDoc
@ -86,6 +93,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
this.container.appendChild( this.canvas );
this._checkInterfaceImplementation();
this.setDataNeedsRefresh(); // initializes stamp
}
/**
@ -208,6 +216,14 @@ OpenSeadragon.DrawerBase = class DrawerBase{
$.console.error('Drawer.destroy must be implemented by child class');
}
/**
* Destroy internal cache. Should be called within destroy() when
*
*/
destroyInternalCache() {
this.viewer.tileCache.clearDrawerInternalCache(this);
}
/**
* @param {TiledImage} tiledImage the tiled image that is calling the function
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.

View file

@ -795,8 +795,6 @@ $.Tile.prototype = {
this.tiledImage = null;
this._caches = {};
this._cacheSize = 0;
this.element = null;
this.imgElement = null;
this.loaded = false;
this.loading = false;
this._cKey = this._ocKey;

View file

@ -197,7 +197,7 @@
}
// If we support internal cache
if (keepInternalCopy && !drawer.options.preloadCache) {
if (keepInternalCopy) {
// let sync preparation handle data
if (!drawer.options.preloadCache) {
return this.prepareInternalCacheSync(drawer);
@ -361,15 +361,24 @@
/**
* If cache ceases to be the primary one, free data
* @param {string} drawerId if undefined, all caches are freed, else only target one
* @private
*/
destroyInternalCache() {
destroyInternalCache(drawerId = undefined) {
const internal = this[DRAWER_INTERNAL_CACHE];
if (internal) {
for (let iCache in internal) {
internal[iCache].destroy();
if (drawerId) {
const cache = internal[drawerId];
if (cache) {
cache.destroy();
delete internal[drawerId];
}
} else {
for (let iCache in internal) {
internal[iCache].destroy();
}
delete this[DRAWER_INTERNAL_CACHE];
}
delete this[DRAWER_INTERNAL_CACHE];
}
}
@ -1152,6 +1161,20 @@
this._cachesLoadedCount = 0;
}
/**
* Clean up internal drawer data for a given drawer
* @param {OpenSeadragon.DrawerBase} drawer
*/
clearDrawerInternalCache(drawer) {
const drawerId = drawer.getId();
for (let zombie in this._zombiesLoaded) {
this._zombiesLoaded[zombie].destroyInternalCache(drawerId);
}
for (let cache in this._tilesLoaded) {
this._tilesLoaded[cache].destroyInternalCache(drawerId);
}
}
/**
* Returns reference to all tiles loaded by a particular
* tiled image item

View file

@ -107,7 +107,8 @@
get defaultOptions() {
return {
// use detached cache: our type conversion will not collide (and does not have to preserve CPU data ref)
usePrivateCache: true
usePrivateCache: true,
preloadCache: false,
};
}
@ -168,6 +169,8 @@
this.viewer.drawer = null;
}
this.destroyInternalCache();
// set our destroyed flag to true
this._destroyed = true;
}
@ -238,16 +241,7 @@
this._backupCanvasDrawer = this.viewer.requestDrawer('canvas', {mainDrawer: false});
this._backupCanvasDrawer.canvas.style.setProperty('visibility', 'hidden');
this._backupCanvasDrawer.getSupportedDataFormats = () => this._supportedFormats;
this._backupCanvasDrawer.getDataToDraw = (tile) => {
const cache = tile.getCache(tile.cacheKey);
if (!cache) {
$.console.warn("Attempt to draw tile %s when not cached!", tile);
return undefined;
}
const dataCache = cache.getDataForRendering(this, tile);
// Use CPU Data for the drawer instead
return dataCache && dataCache.cpuData;
};
this._backupCanvasDrawer.getDataToDraw = this.getDataToDraw.bind(this);
}
return this._backupCanvasDrawer;
@ -386,7 +380,7 @@
let numTilesToDraw = indexInDrawArray + 1;
const textureInfo = this.getDataToDraw(tile);
if (textureInfo) {
if (textureInfo && textureInfo.texture) {
this._getTileData(tile, tiledImage, textureInfo, overallMatrix, indexInDrawArray, texturePositionArray, textureDataArray, matrixArray, opacityArray);
} else {
// console.log('No tile info', tile);
@ -824,7 +818,6 @@
//bind the frame buffer to the new texture
gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._renderToTexture, 0);
}
// private
@ -876,13 +869,14 @@
let texture;
let position;
const data = cache.data;
let data = cache.data;
if (!tiledImage.isTainted()) {
if((data instanceof CanvasRenderingContext2D) && $.isCanvasTainted(data.canvas)){
tiledImage.setTainted(true);
$.console.warn('WebGL cannot be used to draw this TiledImage because it has tainted data. Does crossOriginPolicy need to be set?');
this._raiseDrawerErrorEvent(tiledImage, 'Tainted data cannot be used by the WebGLDrawer. Falling back to CanvasDrawer for this TiledImage.');
this.setDataNeedsRefresh();
} else {
let sourceWidthFraction, sourceHeightFraction;
if (tile.sourceBounds) {
@ -925,21 +919,34 @@
// This depends on gl.TEXTURE_2D being bound to the texture
// associated with this canvas before calling this function
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data);
// TextureInfo stored in the cache
return {
texture: texture,
position: position,
};
} catch (e){
// Todo a bit dirty re-use of the tainted flag, but makes the code more stable
tiledImage.setTainted(true);
$.console.error('Error uploading image data to WebGL. Falling back to canvas renderer.', e);
this._raiseDrawerErrorEvent(tiledImage, 'Unknown error when uploading texture. Falling back to CanvasDrawer for this TiledImage.');
this.setDataNeedsRefresh();
}
}
}
// TextureInfo stored in the cache
return {
texture: texture,
position: position,
cpuData: data // Reference to the outer cache data, used to draw if webgl cannot be used
};
if (data instanceof Image) {
const canvas = document.createElement( 'canvas' );
canvas.width = data.width;
canvas.height = data.height;
const context = canvas.getContext('2d', { willReadFrequently: true });
context.drawImage( data, 0, 0 );
data = context;
$.console.log("FONCSCNSO");
}
if (data instanceof CanvasRenderingContext2D) {
return data;
}
$.console.error("Unsupported data used for WebGL Drawer - probably a bug!");
return {};
}
dataFree(data) {