support hot-swapping drawers with viewer.setDrawer()

This commit is contained in:
Tom 2024-02-12 09:30:26 -05:00
parent 0a154a3b21
commit 8967e2bb03
3 changed files with 83 additions and 27 deletions

View File

@ -139,6 +139,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
this.canvas.height = 1; this.canvas.height = 1;
this.sketchCanvas = null; this.sketchCanvas = null;
this.sketchContext = null; this.sketchContext = null;
this.container.removeChild(this.canvas);
} }
/** /**

View File

@ -455,35 +455,13 @@ $.Viewer = function( options ) {
this.drawer = null; this.drawer = null;
for (let i = 0; i < drawerCandidates.length; i++) { for (const drawerCandidate of drawerCandidates){
let success = this.setDrawer(drawerCandidate, false);
let drawerCandidate = drawerCandidates[i]; if(success){
let Drawer = null;
//if inherits from a drawer base, use it
if (drawerCandidate && drawerCandidate.prototype instanceof $.DrawerBase) {
Drawer = drawerCandidate;
drawerCandidate = 'custom';
} else if (typeof drawerCandidate === "string") {
Drawer = $.determineDrawer(drawerCandidate);
} else {
$.console.warn('Unsupported drawer! Drawer must be an existing string type, or a class that extends OpenSeadragon.DrawerBase.');
continue;
}
// if the drawer is supported, create it and break the loop
if (Drawer && Drawer.isSupported()) {
this.drawer = new Drawer({
viewer: this,
viewport: this.viewport,
element: this.canvas,
debugGridColor: this.debugGridColor,
options: this.drawerOptions[drawerCandidate],
});
break; break;
} }
} }
if (!this.drawer){ if (!this.drawer){
$.console.error('No drawer could be created!'); $.console.error('No drawer could be created!');
throw('Error with creating the selected drawer(s)'); throw('Error with creating the selected drawer(s)');
@ -950,6 +928,57 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.removeAllHandlers(); this.removeAllHandlers();
}, },
/**
* Set the drawer for this viewer, as a supported string or drawer constructor.
* @param {String | OpenSeadragon.DrawerBase} drawerCandidate The type of drawer to try to construct
* @param { Boolean } [redrawImmediately] Whether to immediately draw a new frame. Default = true.
* @param { Object } [drawerOptions] Options for this drawer. If falsey, defaults to viewer.drawerOptions
* for this viewer type. See {@link OpenSeadragon.Options}.
* @returns {Boolean} whether the drawer was created successfully
*/
setDrawer(drawerCandidate, redrawImmediately = true, drawerOptions = null){
const oldDrawer = this.drawer;
let Drawer = null;
//if inherits from a drawer base, use it
if (drawerCandidate && drawerCandidate.prototype instanceof $.DrawerBase) {
Drawer = drawerCandidate;
drawerCandidate = 'custom';
} else if (typeof drawerCandidate === "string") {
Drawer = $.determineDrawer(drawerCandidate);
}
if(!Drawer){
$.console.warn('Unsupported drawer! Drawer must be an existing string type, or a class that extends OpenSeadragon.DrawerBase.');
}
// if the drawer is supported, create it and return true
if (Drawer && Drawer.isSupported()) {
// first destroy the previous drawer
if(oldDrawer){
oldDrawer.destroy();
}
// create the new drawer
this.drawer = new Drawer({
viewer: this,
viewport: this.viewport,
element: this.canvas,
debugGridColor: this.debugGridColor,
options: drawerOptions || this.drawerOptions[drawerCandidate],
});
if(redrawImmediately){
this.forceRedraw();
}
return true;
}
return false;
},
/** /**
* @function * @function
* @returns {Boolean} * @returns {Boolean}

View File

@ -158,6 +158,16 @@
// set our webgl context reference to null to enable garbage collection // set our webgl context reference to null to enable garbage collection
this._gl = null; this._gl = null;
if(this._backupCanvasDrawer){
this._backupCanvasDrawer.destroy();
this._backupCanvasDrawer = null;
}
this.container.removeChild(this.canvas);
if(this.viewer.drawer === this){
this.viewer.drawer = null;
}
// set our destroyed flag to true // set our destroyed flag to true
this._destroyed = true; this._destroyed = true;
} }
@ -355,6 +365,15 @@
let tileContext = tile.getCanvasContext(); let tileContext = tile.getCanvasContext();
let textureInfo = tileContext ? this._TextureMap.get(tileContext.canvas) : null; let textureInfo = tileContext ? this._TextureMap.get(tileContext.canvas) : null;
if(!textureInfo){
// tile was not processed in the tile-ready event (this can happen
// if this drawer was created after the tile was downloaded)
this._tileReadyHandler({tile: tile, tiledImage: tiledImage});
// retry getting textureInfo
textureInfo = tileContext ? this._TextureMap.get(tileContext.canvas) : null;
}
if(textureInfo){ if(textureInfo){
this._getTileData(tile, tiledImage, textureInfo, overallMatrix, indexInDrawArray, texturePositionArray, textureDataArray, matrixArray, opacityArray); this._getTileData(tile, tiledImage, textureInfo, overallMatrix, indexInDrawArray, texturePositionArray, textureDataArray, matrixArray, opacityArray);
} else { } else {
@ -840,11 +859,18 @@
_tileReadyHandler(event){ _tileReadyHandler(event){
let tile = event.tile; let tile = event.tile;
let tiledImage = event.tiledImage; let tiledImage = event.tiledImage;
// If a tiledImage is already known to be tainted, don't try to upload any
// textures to webgl, because they won't be used even if it succeeds
if(tiledImage.isTainted()){
return;
}
let tileContext = tile.getCanvasContext(); let tileContext = tile.getCanvasContext();
let canvas = tileContext && tileContext.canvas; let canvas = tileContext && tileContext.canvas;
// if the tile doesn't provide a canvas, or is tainted by cross-origin // if the tile doesn't provide a canvas, or is tainted by cross-origin
// data, marked the TiledImage as tainted so the canvas drawer can be // data, marked the TiledImage as tainted so the canvas drawer can be
// used instead, and return immediately - data cannot be uploaded to webgl // used instead, and return immediately - tainted data cannot be uploaded to webgl
if(!canvas || $.isCanvasTainted(canvas)){ if(!canvas || $.isCanvasTainted(canvas)){
const wasTainted = tiledImage.isTainted(); const wasTainted = tiledImage.isTainted();
if(!wasTainted){ if(!wasTainted){