diff --git a/changelog.txt b/changelog.txt index e8778d25..f8d2cfec 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ OPENSEADRAGON CHANGELOG 5.0.1: (in progress...) * Improved overlay handling so it plays better with other libraries (#2582 @BeebBenjamin) +* WebGLDrawer now supports the imageSmoothingEnabled option (#2615 @pearcetm) * Fixed: If you switched from WebGL drawer to canvas drawer, it didn't clean up properly (#2570 @pearcetm) * Fixed: TiledImage.setClip would sometimes leave tiles unloaded (#2590 @pearcetm) * Fixed: The WebGL drawer didn't support viewportMargins (#2600, #2606 @pearcetm) diff --git a/src/openseadragon.js b/src/openseadragon.js index b6212f63..981f8ae5 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -214,7 +214,7 @@ * For complete list of modes, please @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation/ globalCompositeOperation} * * @property {Boolean} [imageSmoothingEnabled=true] - * Image smoothing for canvas rendering (only if the canvas drawer is used). Note: Ignored + * Image smoothing for rendering (only if the canvas or webgl drawer is used). Note: Ignored * by some (especially older) browsers which do not support this canvas property. * This property can be changed in {@link Viewer.DrawerBase.setImageSmoothingEnabled}. * diff --git a/src/webgldrawer.js b/src/webgldrawer.js index e92e497f..6d01b6fe 100644 --- a/src/webgldrawer.js +++ b/src/webgldrawer.js @@ -88,6 +88,8 @@ this._renderingCanvas = null; this._backupCanvasDrawer = null; + this._imageSmoothingEnabled = true; // will be updated by setImageSmoothingEnabled + // Reject listening for the tile-drawing and tile-drawn events, which this drawer does not fire this.viewer.rejectEventHandler("tile-drawn", "The WebGLDrawer does not raise the tile-drawn event"); this.viewer.rejectEventHandler("tile-drawing", "The WebGLDrawer does not raise the tile-drawing event"); @@ -98,10 +100,9 @@ this._setupCanvases(); this._setupRenderer(); - // Unique type per drawer: uploads texture to unique webgl context. - this._dataType = `${this.getId()}_TEX_2D`; this._supportedFormats = []; - this._setupTextureHandlers(this._dataType); + this._setupCallCount = 1; + this._setupTextureHandlers(); this.context = this._outputContext; // API required by tests } @@ -469,11 +470,16 @@ // Public API required by all Drawer implementations /** - * Required by DrawerBase, but has no effect on WebGLDrawer. - * @param {Boolean} enabled + * Sets whether image smoothing is enabled or disabled + * @param {Boolean} enabled If true, uses gl.LINEAR as the TEXTURE_MIN_FILTER and TEXTURE_MAX_FILTER, otherwise gl.NEAREST. */ setImageSmoothingEnabled(enabled){ - // noop - this property does not impact WebGLDrawer + if( this._imageSmoothingEnabled !== enabled ){ + this._imageSmoothingEnabled = enabled; + this._setupTextureHandlers(); // re-sets the type to enforce re-initialization + return this.viewer.requestInvalidate(); + } + return $.Promise.resolve(); } /** @@ -583,6 +589,11 @@ } + // private + _textureFilter(){ + return this._imageSmoothingEnabled ? this._gl.LINEAR : this._gl.NEAREST; + } + // private _setupRenderer(){ let gl = this._gl; @@ -599,7 +610,7 @@ gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this._renderToTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this._renderingCanvas.width, this._renderingCanvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this._textureFilter()); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); @@ -798,7 +809,7 @@ gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this._renderToTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this._textureFilter()); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); @@ -851,7 +862,7 @@ this.viewer.addHandler("resize", this._resizeHandler); } - _setupTextureHandlers(thisType) { + _setupTextureHandlers() { const _this = this; const tex2DCompatibleLoader = (tile, data) => { let tiledImage = tile.tiledImage; @@ -904,10 +915,12 @@ } }; + const thisType = `${this.getId()}_${this._setupCallCount++}_TEX_2D`; // Differentiate type also based on type used to upload data: we can support bidirectional conversion. const c2dTexType = thisType + ":context2d", imageTexType = thisType + ":image"; - this._supportedFormats.push(c2dTexType, imageTexType); + // Todo consider removing old type handlers if _supportedFormats had already types defined + this._supportedFormats = [c2dTexType, imageTexType]; // We should be OK uploading any of these types. The complexity is selected to be O(3n), should be // more than linear pass over pixels @@ -945,6 +958,13 @@ } // private +// _unloadTextures(){ +// let canvases = Array.from(this._TextureMap.keys()); +// canvases.forEach(canvas => { +// this._cleanupImageData(canvas); // deletes texture, removes from _TextureMap +// }); +// } + _setClip(){ // no-op: called by _renderToClippingCanvas when tiledImage._clip is truthy // so that tests will pass. diff --git a/test/demo/drawercomparison.js b/test/demo/drawercomparison.js index bb96219b..16acc74e 100644 --- a/test/demo/drawercomparison.js +++ b/test/demo/drawercomparison.js @@ -193,6 +193,11 @@ function updateTiledImage(tiledImage, data, value, item){ tiledImage.setOpacity(Number(value)); } else if (field == 'flipped'){ tiledImage.setFlip($(item).prop('checked')); + } else if (field == 'smoothing'){ + const checked = $(item).prop('checked'); + viewer1.drawer.setImageSmoothingEnabled(checked); + viewer2.drawer.setImageSmoothingEnabled(checked); + $('[data-field=smoothing]').prop('checked', checked); } else if (field == 'cropped'){ if( $(item).prop('checked') ){ let scale = tiledImage.source.width; @@ -355,6 +360,7 @@ function makeImagePickerElement(key, label){ + `.replaceAll('data-image=""', `data-image="${key}"`).replace('__title__', label));