deprecate useCanvas option; add option for drawer and drawerOptions

This commit is contained in:
Tom 2023-06-18 19:08:33 -04:00
parent 7bb02d51d0
commit 37b8ef9519
12 changed files with 185 additions and 131 deletions

View File

@ -59,7 +59,7 @@ module.exports = function(grunt) {
"src/overlay.js",
"src/drawerbase.js",
"src/htmldrawer.js",
"src/canvasdrawer.js",
"src/context2ddrawer.js",
"src/viewport.js",
"src/tiledimage.js",
"src/tilecache.js",

View File

@ -35,9 +35,9 @@
(function( $ ){
/**
* @class CanvasDrawer
* @class Context2dDrawer
* @memberof OpenSeadragon
* @classdesc Default implementation of CanvasDrawer for an {@link OpenSeadragon.Viewer}.
* @classdesc Default implementation of Context2dDrawer for an {@link OpenSeadragon.Viewer}.
* @param {Object} options - Options for this Drawer.
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
@ -45,7 +45,7 @@
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
*/
class CanvasDrawer extends $.DrawerBase{
class Context2dDrawer extends $.DrawerBase{
constructor(){
super(...arguments);
@ -77,6 +77,26 @@ class CanvasDrawer extends $.DrawerBase{
this._imageSmoothingEnabled = true;
}
/**
* @returns {Boolean} true if canvas is supported by the browser, otherwise false
*/
isSupported(){
return $.supportsCanvas;
}
/**
* create the HTML element (e.g. canvas, div) that the image will be drawn into
* @returns {Element} the canvas to draw into
*/
createDrawingElement(){
let canvas = $.makeNeutralElement("canvas");
let viewportSize = this._calculateCanvasSize();
canvas.width = viewportSize.x;
canvas.height = viewportSize.y;
return canvas;
}
/**
* Draws the TiledImages
*/
@ -543,7 +563,7 @@ class CanvasDrawer extends $.DrawerBase{
}
context.save();
context.globalAlpha = this.opacity;
// context.globalAlpha = this.options.opacity; // this was deprecated previously and should not be applied as it is set per TiledImage
if (typeof scale === 'number' && scale !== 1) {
// draw tile at a different scale
@ -982,7 +1002,7 @@ class CanvasDrawer extends $.DrawerBase{
};
}
}
$.CanvasDrawer = CanvasDrawer;
$.Context2dDrawer = Context2dDrawer;
/**

View File

@ -71,24 +71,16 @@ $.DrawerBase = class DrawerBase{
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
}
// Object.entries({}).forEach( ([key, value]) => {
// try{
// this[key] = value;
// } catch(e) {
// $.console.error(`This Drawer implementation does not support option '${key}:${value}'`);
// }
// });
this.viewer = options.viewer;
this.viewport = options.viewport;
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
this.options = options.options || {};
// TO DO: This was deprectated previously. Can we get rid of it at this point?
if (options.opacity) {
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
}
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
/**
* The parent element of this Drawer instance, passed in when the Drawer was created.
* The parent of {@link OpenSeadragon.DrawerBase#canvas}.
@ -96,21 +88,6 @@ $.DrawerBase = class DrawerBase{
* @memberof OpenSeadragon.DrawerBase#
*/
this.container = $.getElement( options.element );
/**
* A <canvas> element if the browser supports them, otherwise a <div> element.
* Child element of {@link OpenSeadragon.DrawerBase#container}.
* @member {Element} canvas
* @memberof OpenSeadragon.DrawerBase#
*/
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
/**
* @member {Element} element
* @memberof OpenSeadragon.DrawerBase#
* @deprecated Alias for {@link OpenSeadragon.DrawerBase#container}.
*/
this.element = this.container;
// TO DO: Does this need to be in DrawerBase, or only in Drawer implementations?
// We force our container to ltr because our drawing math doesn't work in rtl.
@ -118,16 +95,11 @@ $.DrawerBase = class DrawerBase{
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
if (this.useCanvas) {
var viewportSize = this._calculateCanvasSize();
this.canvas.width = viewportSize.x;
this.canvas.height = viewportSize.y;
}
// the first time this.canvas is accessed, implementations will create it
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true );
$.setElementOpacity( this.canvas, this.viewer.opacity, true ); // set
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
@ -143,6 +115,32 @@ $.DrawerBase = class DrawerBase{
get isOpenSeadragonDrawer(){
return true;
}
get canvas(){
if(!this._renderingTarget){
this._renderingTarget = this.createDrawingElement();
}
return this._renderingTarget;
}
get element(){
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
return this.container;
}
/**
* @returns {Boolean} whether the drawer implementation is supported by the browser
*/
isSupported() {
$.console.error('Drawer.isSupported must be implemented by child class');
}
/**
* create the HTML element (e.g. canvas, div) that the image will be drawn into
* @returns {Element} the element to draw into
*/
createDrawingElement() {
$.console.error('Drawer.createDrawingElement must be implemented by child class');
return null;
}
/**
* @param tiledImage the TiledImage that is ready to be drawn

View File

@ -56,32 +56,22 @@ class HTMLDrawer extends $.DrawerBase{
*/
this.context = null;
}
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
/**
* Override default element to enforce div for HTMLDrawer
*/
this.canvas.parentNode.removeChild(this.canvas);
this.canvas = $.makeNeutralElement( "div" );
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
/**
* @returns {Boolean} always true
*/
isSupported(){
return true;
}
/**
* create the HTML element (e.g. canvas, div) that the image will be drawn into
* @returns {Element} the div to draw into
*/
createDrawingElement(){
let canvas = $.makeNeutralElement("div");
return canvas;
}
/**

View File

@ -42,8 +42,8 @@
* 1. viewer.open({type: 'image', url: fooUrl});
* 2. viewer.open(new OpenSeadragon.ImageTileSource({url: fooUrl}));
*
* With the first syntax, the crossOriginPolicy, ajaxWithCredentials and
* useCanvas options are inherited from the viewer if they are not
* With the first syntax, the crossOriginPolicy and ajaxWithCredentials
* options are inherited from the viewer if they are not
* specified directly in the options object.
*
* @memberof OpenSeadragon
@ -58,16 +58,13 @@
* domains.
* @param {String|Boolean} [options.ajaxWithCredentials=false] Whether to set
* the withCredentials XHR flag for AJAX requests (when loading tile sources).
* @param {Boolean} [options.useCanvas=true] Set to false to prevent any use
* of the canvas API.
*/
$.ImageTileSource = function (options) {
options = $.extend({
buildPyramid: true,
crossOriginPolicy: false,
ajaxWithCredentials: false,
useCanvas: true
ajaxWithCredentials: false
}, options);
$.TileSource.apply(this, [options]);
@ -214,7 +211,7 @@
height: this._image.naturalHeight
}];
if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
if (!this.buildPyramid || !$.supportsCanvas) {
// We don't need the image anymore. Allows it to be GC.
delete this._image;
return levels;

View File

@ -190,6 +190,16 @@
* Zoom level to use when image is first opened or the home button is clicked.
* If 0, adjusts to fit viewer.
*
* @property {String|DrawerImplementation|Array} [drawer = ['context2d', 'html']]
* Which drawer to use. Valid strings are 'context2d' and 'html'. Valid drawer
* implementations are constructors of classes that extend OpenSeadragon.DrawerBase.
* An array of strings and/or constructors can be used to indicate the priority
* of different implementations, which will be tried in order based on browser support.
*
* @property {Object} [drawerOptions = {}]
* Options to pass to the selected drawer implementation. See documentation
* for Drawer classes that extend DrawerBase for further information.
*
* @property {Number} [opacity=1]
* Default proportional opacity of the tiled images (1=opaque, 0=hidden)
* Hidden images do not draw and only load when preloading is allowed.
@ -502,7 +512,7 @@
* Milliseconds to wait after each tile retry if tileRetryMax is set.
*
* @property {Boolean} [useCanvas=true]
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
* Deprecated. Use drawer option to specify preferred renderer.
*
* @property {Number} [minPixelRatio=0.5]
* The higher the minPixelRatio, the lower the quality of the image that
@ -1332,12 +1342,19 @@ function OpenSeadragon( options ){
flipped: false,
// APPEARANCE
opacity: 1,
preload: false,
compositeOperation: null,
imageSmoothingEnabled: true,
placeholderFillStyle: null,
subPixelRoundingForTransparency: null,
opacity: 1, // to be passed into each TiledImage
compositeOperation: null, // to be passed into each TiledImage
// DRAWER SETTINGS
drawer: ['context2d', 'html'], // prefer using canvas, fallback to html
drawerOptions: {},
useCanvas: true, // deprecated - set drawer and drawerOptions
// TILED IMAGE SETTINGS
preload: false, // to be passed into each TiledImage
imageSmoothingEnabled: true, // to be passed into each TiledImage
placeholderFillStyle: null, // to be passed into each TiledImage
subPixelRoundingForTransparency: null, // to be passed into each TiledImage
//REFERENCE STRIP SETTINGS
showReferenceStrip: false,
@ -1360,7 +1377,6 @@ function OpenSeadragon( options ){
imageLoaderLimit: 0,
maxImageCacheCount: 200,
timeout: 30000,
useCanvas: true, // Use canvas element for drawing if available
tileRetryMax: 0,
tileRetryDelay: 2500,
@ -1430,16 +1446,6 @@ function OpenSeadragon( options ){
},
/**
* TODO: get rid of this. I can't see how it's required at all. Looks
* like an early legacy code artifact.
* @static
* @ignore
*/
SIGNAL: "----seadragon----",
/**
* Returns a function which invokes the method as if it were a method belonging to the object.
* @function

View File

@ -455,7 +455,7 @@ function loadPanels( strip, viewerSize, scroll ) {
animationTime: 0,
loadTilesWithAjax: strip.viewer.loadTilesWithAjax,
ajaxHeaders: strip.viewer.ajaxHeaders,
useCanvas: strip.useCanvas
drawer: strip.viewer.drawerOptions.constructor,
} );
// Allow pointer events to pass through miniViewer's canvas/container
// elements so implicit pointer capture works on touch devices

View File

@ -89,6 +89,21 @@ $.Viewer = function( options ) {
delete options.config;
}
// Move deprecated drawer options from the base options object into a sub-object
// This is an array to make it easy to add additional properties to convert to
// drawer options later if it makes sense to set at the drawer level rather than
// per tiled image (for example, subPixelRoundingForTransparency).
let drawerOptionList = [
'useCanvas', // deprecated
];
options.drawerOptions = Object.assign({},
drawerOptionList.reduce((drawerOptions, option) => {
drawerOptions[option] = options[option];
delete options[option];
return drawerOptions;
}, {}),
options.drawerOptions);
//Public properties
//Allow the options object to override global defaults
$.extend( true, this, {
@ -198,6 +213,7 @@ $.Viewer = function( options ) {
$.console.warn("Hash " + this.hash + " has already been used.");
}
//Private state properties
THIS[ this.hash ] = {
fsBoundsDelta: new $.Point( 1, 1 ),
@ -418,35 +434,48 @@ $.Viewer = function( options ) {
maxImageCacheCount: this.maxImageCacheCount
});
// Create the drawer
if(this.customDrawer){
if(this.customDrawer.prototype.isOpenSeadragonDrawer){
var Drawer = this.customDrawer;
//Create the drawer based on selected options
if (Object.prototype.hasOwnProperty.call(this.drawerOptions, 'useCanvas') ){
$.console.error('useCanvas is deprecated, use the "drawer" option to indicate preferred drawer(s)');
// for backwards compatibility, use HTMLDrawer if useCanvas is defined an is falsey
if (!this.drawerOptions.useCanvas){
this.drawer = $.HTMLDrawer;
}
delete this.drawerOptions.useCanvas;
}
let drawerPriority = Array.isArray(this.drawer) ? this.drawer : [this.drawer];
let drawersToTry = drawerPriority.filter(d => ['context2d', 'html'].includes(d) || (d.prototype && d.prototype.isOpenSeadragonDrawer) );
if(drawerPriority.length !== drawersToTry.length){
$.console.error('An invalid drawer was requested.');
}
if(drawersToTry.length === 0){
drawersToTry = [$.DEFAULT_SETTINGS.drawer].flat(); // ensure it is a list
$.console.warn('No valid drawers were selected. Using the default value.');
}
// extend the drawerOptions object with additional properties to pass to the Drawer implementation
this.drawer = null; // TO DO: how to deal with the possibility that none of the requested drawers are supported?
for(let i = 0; i < drawersToTry.length; i++){
let Drawer = drawersToTry[i];
// replace text-based option with appropriate constructor
if (Drawer === 'context2d'){
Drawer = $.Context2dDrawer;
} else if (Drawer === 'html'){
Drawer = $.HTMLDrawer;
}
// if the drawer is supported, create it and break the loop
if (Drawer.prototype.isSupported()){
this.drawer = new Drawer({
viewer: this,
viewport: this.viewport,
element: this.canvas,
debugGridColor: this.debugGridColor
debugGridColor: this.debugGridColor,
options: this.drawerOptions,
});
} else {
// $.console.error('Viewer option customDrawer must derive from OpenSeadragon.DrawerBase');
throw('Viewer option customDrawer must derive from OpenSeadragon.DrawerBase');
this.drawerOptions.constructor = Drawer;
break;
}
} else if(this.useCanvas && $.supportsCanvas) {
this.drawer = new $.CanvasDrawer({
viewer: this,
viewport: this.viewport,
element: this.canvas,
debugGridColor: this.debugGridColor
});
} else {
this.drawer = new $.HTMLDrawer({
viewer: this,
viewport: this.viewport,
element: this.canvas,
debugGridColor: this.debugGridColor
});
}
@ -455,7 +484,7 @@ $.Viewer = function( options ) {
this.canvas.appendChild( this.overlaysContainer );
// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons
if (!this.drawer.canRotate()) {
if (this.drawer && !this.drawer.canRotate()) {
// Disable/remove the rotate left/right buttons since they aren't supported
if (this.rotateLeft) {
i = this.buttonGroup.buttons.indexOf(this.rotateLeft);
@ -520,11 +549,6 @@ $.Viewer = function( options ) {
beginControlsAutoHide( _this );
} );
// Initial canvas options
if ( this.imageSmoothingEnabled !== undefined && !this.imageSmoothingEnabled){
this.drawer.setImageSmoothingEnabled(this.imageSmoothingEnabled);
}
// Register the viewer
$._viewers.set(this.element, this);
};
@ -2426,7 +2450,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
width: this.referenceStripWidth,
tileSources: this.tileSources,
prefixUrl: this.prefixUrl,
useCanvas: this.useCanvas,
viewer: this
});
@ -2575,7 +2598,6 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
ajaxHeaders: imgOptions.ajaxHeaders ?
imgOptions.ajaxHeaders : viewer.ajaxHeaders,
splitHashDataForPost: viewer.splitHashDataForPost,
useCanvas: viewer.useCanvas,
success: function( event ) {
successCallback( event.tileSource );
}
@ -2593,9 +2615,6 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
if (tileSource.ajaxWithCredentials === undefined) {
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
}
if (tileSource.useCanvas === undefined) {
tileSource.useCanvas = viewer.useCanvas;
}
if ( $.isFunction( tileSource.getTileUrl ) ) {
//Custom tile source

View File

@ -16,7 +16,7 @@
// debugMode: true,
zoomPerScroll: 1.02,
showNavigator: testNavigator,
useCanvas: true,
drawer: ['context2d', 'html'],
// defaultZoomLevel: 2,
// homeFillsViewer: true,
// sequenceMode: true,

View File

@ -145,6 +145,29 @@ export class ThreeJSDrawer extends OpenSeadragon.DrawerBase{
}
// Public API required by all Drawer implementations
/**
* @returns {Boolean} returns true if canvas and webgl are supported and
* three.js has been exposed as a global variable named THREE
*/
isSupported(){
let canvasElement = document.createElement( 'canvas' );
return !!( OpenSeadragon.isFunction( canvasElement.getContext ) &&
canvasElement.getContext( 'webgl' ) ) && THREE;
}
/**
* create the HTML element (e.g. canvas, div) that the image will be drawn into
* @returns {Element} the canvas to draw into
*/
createDrawingElement(){
let canvas = OpenSeadragon.makeNeutralElement("canvas");
let viewportSize = this._calculateCanvasSize();
canvas.width = viewportSize.x;
canvas.height = viewportSize.y;
return canvas;
}
/**
*
* @param {Array} tiledImages Array of TiledImage objects to draw

View File

@ -88,7 +88,7 @@
let viewer = OpenSeadragon({
...
customDrawer: ThreeJSDrawer,
drawer: ThreeJSDrawer,
...
});
</pre>
@ -173,17 +173,17 @@
HTML-based rendering can be selected in two different ways:
</div>
<pre class="example-code">
// via the useCanvas option:
// via the 'html' drawer option:
let viewer = OpenSeadragon({
...
useCanvas: false,
drawer: 'html',
...
});
// or by passing the HTMLDrawer constructor
let viewer = OpenSeadragon({
...
customDrawer:OpenSeadragon.HTMLDrawer,
drawer:OpenSeadragon.HTMLDrawer,
...
});
</pre>

View File

@ -25,7 +25,7 @@ var stats = null;
// document.body.appendChild( stats.dom );
//Double viewer setup for comparison - CanvasDrawer and ThreeJSDrawer
//Double viewer setup for comparison - Context2dDrawer and ThreeJSDrawer
var viewer = window.viewer = OpenSeadragon({
id: "contentDiv",
@ -37,11 +37,11 @@ var viewer = window.viewer = OpenSeadragon({
smoothTileEdgesMinZoom:1.1,
crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false,
useCanvas:true,
drawer:'context2d',
});
// Mirror the interactive viewer with CanvasDrawer onto a separate canvas using ThreeJSDrawer
// Mirror the interactive viewer with Context2dDrawer onto a separate canvas using ThreeJSDrawer
let threeRenderer = window.threeRenderer = new ThreeJSDrawer({viewer, viewport: viewer.viewport, element:viewer.element, stats: stats});
//make the test canvas mirror all changes to the viewer canvas
let viewerCanvas = viewer.drawer.canvas;
@ -61,7 +61,7 @@ var viewer2 = window.viewer2 = OpenSeadragon({
id: "three-viewer",
prefixUrl: "../../build/openseadragon/images/",
minZoomImageRatio:0.01,
customDrawer: ThreeJSDrawer,
drawer: ThreeJSDrawer,
tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']],
sequenceMode: true,
imageSmoothingEnabled: false,
@ -73,6 +73,7 @@ var viewer2 = window.viewer2 = OpenSeadragon({
// Also shows sequence mode
var viewer3 = window.viewer3 = OpenSeadragon({
id: "htmldrawer",
drawer:'html',
prefixUrl: "../../build/openseadragon/images/",
minZoomImageRatio:0.01,
customDrawer: OpenSeadragon.HTMLDrawer,