From e5b608d86a569ac7ec8de4a6fa84d875180a735c Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Mon, 13 Dec 2021 14:20:11 +0900 Subject: [PATCH 1/6] Extends the fix for seams in transparent images to any browsers, making it configurable --- src/drawer.js | 2 +- src/openseadragon.js | 8 +++ src/tile.js | 13 ++++- src/tiledimage.js | 121 ++++++++++++++++++++++++++++++++++++++++++- src/viewer.js | 20 +++++-- 5 files changed, 158 insertions(+), 6 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index fca4b956..1b0c87ac 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -352,7 +352,7 @@ $.Drawer.prototype = { if (this.useCanvas) { var context = this._getContext(useSketch); scale = scale || 1; - tile.drawCanvas(context, drawingHandler, scale, translate); + tile.drawCanvas(context, drawingHandler, scale, translate, this._shouldRoundPositionAndSize); } else { tile.drawHTML( this.canvas ); } diff --git a/src/openseadragon.js b/src/openseadragon.js index 5b0accb0..e507b5eb 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -209,6 +209,13 @@ * You can pass a CSS color value like "#FF8800". * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern. * + * @property {Object} [subPixelRounding=null] + * Determines when subpixel rounding should be applied for tiles rendering. + * This property is a subpixel rounding enum values dictionary [number] --> string. + * The key is a $.BROWSERS value, and the value is one of 'ALWAYS', 'ONLY_AT_REST' or 'NEVER', + * indicating, for a given browser, when to apply subpixel rounding. + * Key '' is the fallback value for any browser not specified in the dictionary. + * * @property {Number} [degrees=0] * Initial rotation. * @@ -1263,6 +1270,7 @@ function OpenSeadragon( options ){ compositeOperation: null, imageSmoothingEnabled: true, placeholderFillStyle: null, + subPixelRounding: null, //REFERENCE STRIP SETTINGS showReferenceStrip: false, diff --git a/src/tile.js b/src/tile.js index 701db750..f003c157 100644 --- a/src/tile.js +++ b/src/tile.js @@ -318,8 +318,11 @@ $.Tile.prototype = { * where rendered is the context with the pre-drawn image. * @param {Number} [scale=1] - Apply a scale to position and size * @param {OpenSeadragon.Point} [translate] - A translation vector + * @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round + * position and size of tiles supporting alpha channel in non-transparency + * context. */ - drawCanvas: function( context, drawingHandler, scale, translate ) { + drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize ) { var position = this.position.times($.pixelDensityRatio), size = this.size.times($.pixelDensityRatio), @@ -363,6 +366,14 @@ $.Tile.prototype = { //an image with an alpha channel, then the only way //to avoid seeing the tile underneath is to clear the rectangle if (context.globalAlpha === 1 && this._hasTransparencyChannel()) { + if (shouldRoundPositionAndSize) { + // Round to the nearest whole pixel so we don't get seams from overlap. + position.x = Math.round(position.x); + position.y = Math.round(position.y); + size.x = Math.round(size.x); + size.y = Math.round(size.y); + } + //clearing only the inside of the rectangle occupied //by the png prevents edge flikering context.clearRect( diff --git a/src/tiledimage.js b/src/tiledimage.js index 95ac4f3c..c913d98e 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -177,7 +177,8 @@ $.TiledImage = function( options ) { placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle, opacity: $.DEFAULT_SETTINGS.opacity, preload: $.DEFAULT_SETTINGS.preload, - compositeOperation: $.DEFAULT_SETTINGS.compositeOperation + compositeOperation: $.DEFAULT_SETTINGS.compositeOperation, + subPixelRounding: $.DEFAULT_SETTINGS.subPixelRounding }, options ); this._preload = this.preload; @@ -1951,6 +1952,111 @@ function compareTiles( previousBest, tile ) { return previousBest; } +/** + * @private + * @inner + * Defines the value for subpixel rounding to fallback to in case of missing or + * invalid value. + */ +var DEFAULT_SUBPIXEL_ROUNDING_RULE = 'NEVER'; + +/** + * @private + * @inner + * Determines whether the subpixel rounding enum value is 'ALWAYS' or not. + * + * @param {string} value - The subpixel rounding enum value to check, case sensitive. + * @returns {Boolean} True if input value is 'ALWAYS', false otherwise. + */ +function isSubPixelRoundingRuleAlways(value) { + return value === 'ALWAYS'; +} + +/** + * @private + * @inner + * Determines whether the subpixel rounding enum value is 'ONLY_AT_REST' or not. + * + * @param {string} value - The subpixel rounding enum value to check, case sensitive. + * @returns {Boolean} True if input value is 'ONLY_AT_REST', false otherwise. + */ + function isSubPixelRoundingRuleOnlyAtRest(value) { + return value === 'ONLY_AT_REST'; +} + +/** + * @private + * @inner + * Determines whether the subpixel rounding enum value is 'NEVER' or not. + * + * @param {string} value - The subpixel rounding enum value to check, case sensitive. + * @returns {Boolean} True if input value is 'NEVER', false otherwise. + */ + function isSubPixelRoundingRuleNever(value) { + return value === DEFAULT_SUBPIXEL_ROUNDING_RULE; +} + +/** + * @private + * @inner + * Checks whether the input value is an invalid subpixel rounding enum value. + * + * @param {string} value - The subpixel rounding enum value to check, case sensitive. + * @returns {Boolean} Returns true if the input value is none of the expected + * 'ALWAYS', 'ONLY_AT_REST' or 'NEVER' value. + * Note that if passed a valid value but with the incorrect casing, the return + * value will be true. If input is 'always', then true is returned, indicating + * it is an unknown value. + */ + function isSubPixelRoundingRuleUnknown(value) { + return !isSubPixelRoundingRuleAlways(value) && + !isSubPixelRoundingRuleOnlyAtRest(value) && + !isSubPixelRoundingRuleNever(value); +} + +/** + * @private + * @inner + * Ensures the returned value is always a valid subpixel rounding enum value, + * defaulting to 'NEVER' if input is missing or invalid. + * + * @param {string} value - The subpixel rounding enum value to normalize, case sensitive. + * @returns {string} Returns a valid subpixel rounding enum value. + * Note that if passed a valid value but with the incorrect casing, the return + * value will be the default 'NEVER'. If input is 'always', then 'NEVER' is + * returned. + */ + function normalizeSubPixelRoundingRule(value) { + if (isSubPixelRoundingRuleUnknown(value)) { + return DEFAULT_SUBPIXEL_ROUNDING_RULE; + } + return value; +} + +/** + * @private + * @inner + * Ensures the returned value is always a valid subpixel rounding enum value, + * defaulting to 'NEVER' if input is missing or invalid. + * + * @param {Object} subPixelRoundingRules - A subpixel rounding enum values dictionary [number] --> string. + * @returns {string} Returns the determined subpixel rounding enum value for the + * current browser. + */ +function determineSubPixelRoundingRule(subPixelRoundingRules) { + if (!subPixelRoundingRules || !$.Browser) { + return DEFAULT_SUBPIXEL_ROUNDING_RULE; + } + + var subPixelRoundingRule = subPixelRoundingRules[$.Browser.vendor]; + + if (!subPixelRoundingRule || isSubPixelRoundingRuleUnknown(subPixelRoundingRule)) { + subPixelRoundingRule = subPixelRoundingRules['']; + } + + return normalizeSubPixelRoundingRule(subPixelRoundingRule); +} + /** * @private * @inner @@ -2099,6 +2205,19 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch); } + var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRounding); + + var shouldRoundPositionAndSize = false; + + if (isSubPixelRoundingRuleAlways(subPixelRoundingRule)) { + shouldRoundPositionAndSize = true; + } else if (isSubPixelRoundingRuleOnlyAtRest(subPixelRoundingRule)) { + var isAnimating = tiledImage.viewer && tiledImage.viewer.isAnimating(); + shouldRoundPositionAndSize = !isAnimating; + } + + tiledImage._drawer._shouldRoundPositionAndSize = shouldRoundPositionAndSize; + for (var i = lastDrawn.length - 1; i >= 0; i--) { tile = lastDrawn[ i ]; tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate ); diff --git a/src/viewer.js b/src/viewer.js index a879a0bd..6ad976dc 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1494,7 +1494,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, ajaxWithCredentials: queueItem.options.ajaxWithCredentials, loadTilesWithAjax: queueItem.options.loadTilesWithAjax, ajaxHeaders: queueItem.options.ajaxHeaders, - debugMode: _this.debugMode + debugMode: _this.debugMode, + subPixelRounding: _this.subPixelRounding }); if (_this.collectionMode) { @@ -2348,6 +2349,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, } this.goToPage( next ); }, + + isAnimating: function () { + return THIS[ this.hash ].animating; + }, }); @@ -3494,7 +3499,9 @@ function updateOnce( viewer ) { animated = viewer.referenceStrip.update( viewer.viewport ) || animated; } - if ( !THIS[ viewer.hash ].animating && animated ) { + var currentAnimating = THIS[ viewer.hash ].animating; + + if ( !currentAnimating && animated ) { /** * Raised when any spring animation starts (zoom, pan, etc.). * @@ -3532,7 +3539,7 @@ function updateOnce( viewer ) { } } - if ( THIS[ viewer.hash ].animating && !animated ) { + if ( currentAnimating && !animated ) { /** * Raised when any spring animation ends (zoom, pan, etc.). * @@ -3551,6 +3558,13 @@ function updateOnce( viewer ) { THIS[ viewer.hash ].animating = animated; + // Intentionally use currentAnimating as the value at the current frame, + // regardless of THIS[ viewer.hash ].animating being updated. + if (currentAnimating && !animated) { + // Ensure a draw occurs once animation is over. + drawWorld( viewer ); + } + //viewer.profiler.endUpdate(); } From 2aebdbd06655725c41ba42c2da16748e1d702e4e Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Tue, 21 Dec 2021 11:38:52 +0900 Subject: [PATCH 2/6] Renamed subPixelRounding to subPixelRoundingForTransparency, changed fallback to '*', introduced SUBPIXEL_ROUNDING_OCCURRENCES numbers, added support for simple mode --- src/openseadragon.js | 40 +++++++++++++------ src/tiledimage.js | 92 ++++++++++++++++++++++---------------------- src/viewer.js | 2 +- 3 files changed, 75 insertions(+), 59 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index e507b5eb..ff889292 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -209,12 +209,16 @@ * You can pass a CSS color value like "#FF8800". * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern. * - * @property {Object} [subPixelRounding=null] - * Determines when subpixel rounding should be applied for tiles rendering. - * This property is a subpixel rounding enum values dictionary [number] --> string. - * The key is a $.BROWSERS value, and the value is one of 'ALWAYS', 'ONLY_AT_REST' or 'NEVER', + * @property {Object} [subPixelRoundingForTransparency=null] + * Determines when subpixel rounding should be applied for tiles when rendering images that support transparency. + * This property is a subpixel rounding enum values dictionary [{@link BROWSERS}] --> {@link SUBPIXEL_ROUNDING_OCCURRENCES}. + * The key is a {@link BROWSERS} value, and the value is one of {@link SUBPIXEL_ROUNDING_OCCURRENCES}, * indicating, for a given browser, when to apply subpixel rounding. - * Key '' is the fallback value for any browser not specified in the dictionary. + * Key '*' is the fallback value for any browser not specified in the dictionary. + * This property has a simple mode, and one can set it directly to + * {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS} + * in order to apply this rule for all browser. The values {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS} would be equivalent to { '*', SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS }. + * The default is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} for all browsers, for backward compatibility reason. * * @property {Number} [degrees=0] * Initial rotation. @@ -1265,12 +1269,12 @@ function OpenSeadragon( options ){ flipped: false, // APPEARANCE - opacity: 1, - preload: false, - compositeOperation: null, - imageSmoothingEnabled: true, - placeholderFillStyle: null, - subPixelRounding: null, + opacity: 1, + preload: false, + compositeOperation: null, + imageSmoothingEnabled: true, + placeholderFillStyle: null, + subPixelRoundingForTransparency: null, //REFERENCE STRIP SETTINGS showReferenceStrip: false, @@ -1411,6 +1415,20 @@ function OpenSeadragon( options ){ CHROMEEDGE: 7 }, + /** + * An enumeration of Browser vendors. + * @static + * @type {Object} + * @property {Number} NEVER Never apply subpixel rounding for transparency. + * @property {Number} ONLY_AT_REST Do not apply subpixel rounding for transparency during animation (panning, zoom, rotation) and apply it once animation is over. + * @property {Number} ALWAYS Apply subpixel rounding for transparency during animation and when animation is over. + */ + SUBPIXEL_ROUNDING_OCCURRENCES: { + NEVER: 0, + ONLY_AT_REST: 1, + ALWAYS: 2 + }, + /** * Keep track of which {@link Viewer}s have been created. * - Key: {@link Element} to which a Viewer is attached. diff --git a/src/tiledimage.js b/src/tiledimage.js index c913d98e..431bd422 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -160,25 +160,25 @@ $.TiledImage = function( options ) { _hasOpaqueTile: false, // Do we have even one fully opaque tile? _tilesLoading: 0, // The number of pending tile requests. //configurable settings - springStiffness: $.DEFAULT_SETTINGS.springStiffness, - animationTime: $.DEFAULT_SETTINGS.animationTime, - minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, - wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, - wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, - immediateRender: $.DEFAULT_SETTINGS.immediateRender, - blendTime: $.DEFAULT_SETTINGS.blendTime, - alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend, - minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, - smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom, - iOSDevice: $.DEFAULT_SETTINGS.iOSDevice, - debugMode: $.DEFAULT_SETTINGS.debugMode, - crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy, - ajaxWithCredentials: $.DEFAULT_SETTINGS.ajaxWithCredentials, - placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle, - opacity: $.DEFAULT_SETTINGS.opacity, - preload: $.DEFAULT_SETTINGS.preload, - compositeOperation: $.DEFAULT_SETTINGS.compositeOperation, - subPixelRounding: $.DEFAULT_SETTINGS.subPixelRounding + springStiffness: $.DEFAULT_SETTINGS.springStiffness, + animationTime: $.DEFAULT_SETTINGS.animationTime, + minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, + wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, + wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, + immediateRender: $.DEFAULT_SETTINGS.immediateRender, + blendTime: $.DEFAULT_SETTINGS.blendTime, + alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend, + minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, + smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom, + iOSDevice: $.DEFAULT_SETTINGS.iOSDevice, + debugMode: $.DEFAULT_SETTINGS.debugMode, + crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy, + ajaxWithCredentials: $.DEFAULT_SETTINGS.ajaxWithCredentials, + placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle, + opacity: $.DEFAULT_SETTINGS.opacity, + preload: $.DEFAULT_SETTINGS.preload, + compositeOperation: $.DEFAULT_SETTINGS.compositeOperation, + subPixelRoundingForTransparency: $.DEFAULT_SETTINGS.subPixelRoundingForTransparency }, options ); this._preload = this.preload; @@ -1958,39 +1958,39 @@ function compareTiles( previousBest, tile ) { * Defines the value for subpixel rounding to fallback to in case of missing or * invalid value. */ -var DEFAULT_SUBPIXEL_ROUNDING_RULE = 'NEVER'; +var DEFAULT_SUBPIXEL_ROUNDING_RULE = $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER; /** * @private * @inner - * Determines whether the subpixel rounding enum value is 'ALWAYS' or not. + * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS} or not. * - * @param {string} value - The subpixel rounding enum value to check, case sensitive. - * @returns {Boolean} True if input value is 'ALWAYS', false otherwise. + * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. + * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, false otherwise. */ function isSubPixelRoundingRuleAlways(value) { - return value === 'ALWAYS'; + return value === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS; } /** * @private * @inner - * Determines whether the subpixel rounding enum value is 'ONLY_AT_REST' or not. + * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or not. * - * @param {string} value - The subpixel rounding enum value to check, case sensitive. - * @returns {Boolean} True if input value is 'ONLY_AT_REST', false otherwise. + * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. + * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST}, false otherwise. */ function isSubPixelRoundingRuleOnlyAtRest(value) { - return value === 'ONLY_AT_REST'; + return value === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST; } /** * @private * @inner - * Determines whether the subpixel rounding enum value is 'NEVER' or not. + * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} or not. * - * @param {string} value - The subpixel rounding enum value to check, case sensitive. - * @returns {Boolean} True if input value is 'NEVER', false otherwise. + * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. + * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER}, false otherwise. */ function isSubPixelRoundingRuleNever(value) { return value === DEFAULT_SUBPIXEL_ROUNDING_RULE; @@ -2001,12 +2001,9 @@ function isSubPixelRoundingRuleAlways(value) { * @inner * Checks whether the input value is an invalid subpixel rounding enum value. * - * @param {string} value - The subpixel rounding enum value to check, case sensitive. + * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. * @returns {Boolean} Returns true if the input value is none of the expected - * 'ALWAYS', 'ONLY_AT_REST' or 'NEVER' value. - * Note that if passed a valid value but with the incorrect casing, the return - * value will be true. If input is 'always', then true is returned, indicating - * it is an unknown value. + * {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} value. */ function isSubPixelRoundingRuleUnknown(value) { return !isSubPixelRoundingRuleAlways(value) && @@ -2018,13 +2015,10 @@ function isSubPixelRoundingRuleAlways(value) { * @private * @inner * Ensures the returned value is always a valid subpixel rounding enum value, - * defaulting to 'NEVER' if input is missing or invalid. + * defaulting to {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} if input is missing or invalid. * - * @param {string} value - The subpixel rounding enum value to normalize, case sensitive. - * @returns {string} Returns a valid subpixel rounding enum value. - * Note that if passed a valid value but with the incorrect casing, the return - * value will be the default 'NEVER'. If input is 'always', then 'NEVER' is - * returned. + * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to normalize. + * @returns {SUBPIXEL_ROUNDING_OCCURRENCES} Returns a valid subpixel rounding enum value. */ function normalizeSubPixelRoundingRule(value) { if (isSubPixelRoundingRuleUnknown(value)) { @@ -2039,19 +2033,23 @@ function isSubPixelRoundingRuleAlways(value) { * Ensures the returned value is always a valid subpixel rounding enum value, * defaulting to 'NEVER' if input is missing or invalid. * - * @param {Object} subPixelRoundingRules - A subpixel rounding enum values dictionary [number] --> string. - * @returns {string} Returns the determined subpixel rounding enum value for the + * @param {Object} subPixelRoundingRules - A subpixel rounding enum values dictionary [{@link BROWSERS}] --> {@link SUBPIXEL_ROUNDING_OCCURRENCES}. + * @returns {SUBPIXEL_ROUNDING_OCCURRENCES} Returns the determined subpixel rounding enum value for the * current browser. */ function determineSubPixelRoundingRule(subPixelRoundingRules) { + if (typeof subPixelRoundingRules === 'number') { + return normalizeSubPixelRoundingRule(subPixelRoundingRules); + } + if (!subPixelRoundingRules || !$.Browser) { return DEFAULT_SUBPIXEL_ROUNDING_RULE; } var subPixelRoundingRule = subPixelRoundingRules[$.Browser.vendor]; - if (!subPixelRoundingRule || isSubPixelRoundingRuleUnknown(subPixelRoundingRule)) { - subPixelRoundingRule = subPixelRoundingRules['']; + if (isSubPixelRoundingRuleUnknown(subPixelRoundingRule)) { + subPixelRoundingRule = subPixelRoundingRules['*']; } return normalizeSubPixelRoundingRule(subPixelRoundingRule); @@ -2205,7 +2203,7 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch); } - var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRounding); + var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRoundingForTransparency); var shouldRoundPositionAndSize = false; diff --git a/src/viewer.js b/src/viewer.js index 6ad976dc..2f23abe9 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1495,7 +1495,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, loadTilesWithAjax: queueItem.options.loadTilesWithAjax, ajaxHeaders: queueItem.options.ajaxHeaders, debugMode: _this.debugMode, - subPixelRounding: _this.subPixelRounding + subPixelRoundingForTransparency: _this.subPixelRoundingForTransparency }); if (_this.collectionMode) { From 3fa67c317b57ed51b046939afbc33ff0275ef252 Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Tue, 21 Dec 2021 11:43:08 +0900 Subject: [PATCH 3/6] Moved _shouldRoundPositionAndSize Drawer class member to drawTile() function argument --- src/drawer.js | 7 +++++-- src/tiledimage.js | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 1b0c87ac..30d7fcf2 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -344,15 +344,18 @@ $.Drawer.prototype = { * where rendered is the context with the pre-drawn image. * @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1. * @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position + * @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round + * position and size of tiles supporting alpha channel in non-transparency + * context. */ - drawTile: function(tile, drawingHandler, useSketch, scale, translate) { + drawTile: function(tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize) { $.console.assert(tile, '[Drawer.drawTile] tile is required'); $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); if (this.useCanvas) { var context = this._getContext(useSketch); scale = scale || 1; - tile.drawCanvas(context, drawingHandler, scale, translate, this._shouldRoundPositionAndSize); + tile.drawCanvas(context, drawingHandler, scale, translate, shouldRoundPositionAndSize); } else { tile.drawHTML( this.canvas ); } diff --git a/src/tiledimage.js b/src/tiledimage.js index 431bd422..a9d2e9e2 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -2214,11 +2214,9 @@ function drawTiles( tiledImage, lastDrawn ) { shouldRoundPositionAndSize = !isAnimating; } - tiledImage._drawer._shouldRoundPositionAndSize = shouldRoundPositionAndSize; - for (var i = lastDrawn.length - 1; i >= 0; i--) { tile = lastDrawn[ i ]; - tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate ); + tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate, shouldRoundPositionAndSize ); tile.beingDrawn = true; if( tiledImage.viewer ){ From bfc0c4608eef100f14dfd13b48048043912d5ca7 Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Wed, 22 Dec 2021 15:42:43 +0900 Subject: [PATCH 4/6] Fixed copy/paste mistake --- src/openseadragon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index ff889292..b01f5c25 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1416,7 +1416,7 @@ function OpenSeadragon( options ){ }, /** - * An enumeration of Browser vendors. + * An enumeration of when subpixel rounding should occur. * @static * @type {Object} * @property {Number} NEVER Never apply subpixel rounding for transparency. From 458a16ce1f900a6be8d2735a9d98304413d50698 Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Wed, 22 Dec 2021 15:44:32 +0900 Subject: [PATCH 5/6] Introduced animation state and testing it to determine rendering when at rest --- src/openseadragon.js | 22 +++++++++++++++++++--- src/tiledimage.js | 6 ++++-- src/viewer.js | 31 +++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index b01f5c25..1c413b30 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1424,9 +1424,25 @@ function OpenSeadragon( options ){ * @property {Number} ALWAYS Apply subpixel rounding for transparency during animation and when animation is over. */ SUBPIXEL_ROUNDING_OCCURRENCES: { - NEVER: 0, - ONLY_AT_REST: 1, - ALWAYS: 2 + NEVER: 0, + ONLY_AT_REST: 1, + ALWAYS: 2 + }, + + /** + * An enumeration of animation states. + * @static + * @type {Object} + * @property {Number} AT_REST Indicates there are no more animations running and the image is at rest. + * @property {Number} ANIMATION_STARTED Indicates the image is in motion and it just started. + * @property {Number} ANIMATING Indicates the image was in motion and is still in motion. + * @property {Number} ANIMATION_FINISHED Indicates the image was in motion and is not in motion anymore. + */ + ANIMATION_STATES: { + AT_REST: 0, + ANIMATION_STARTED: 1, + ANIMATING: 2, + ANIMATION_FINISHED: 3 }, /** diff --git a/src/tiledimage.js b/src/tiledimage.js index a9d2e9e2..6e426e4e 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -2210,8 +2210,10 @@ function drawTiles( tiledImage, lastDrawn ) { if (isSubPixelRoundingRuleAlways(subPixelRoundingRule)) { shouldRoundPositionAndSize = true; } else if (isSubPixelRoundingRuleOnlyAtRest(subPixelRoundingRule)) { - var isAnimating = tiledImage.viewer && tiledImage.viewer.isAnimating(); - shouldRoundPositionAndSize = !isAnimating; + shouldRoundPositionAndSize = tiledImage.viewer && ( + tiledImage.viewer.getAnimationState() === $.ANIMATION_STATES.ANIMATION_FINISHED || + tiledImage.viewer.getAnimationState() === $.ANIMATION_STATES.AT_REST // Testing AT_REST here is for the very first render after loading. + ); } for (var i = lastDrawn.length - 1; i >= 0; i--) { diff --git a/src/viewer.js b/src/viewer.js index 2f23abe9..57b46cad 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -203,6 +203,7 @@ $.Viewer = function( options ) { fsBoundsDelta: new $.Point( 1, 1 ), prevContainerSize: null, animating: false, + animationState: $.ANIMATION_STATES.AT_REST, forceRedraw: false, mouseInside: false, group: null, @@ -713,6 +714,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, } THIS[ this.hash ].animating = false; + THIS[ this.hash ].animationState = $.ANIMATION_STATES.AT_REST; + this.world.removeAll(); this.imageLoader.clear(); @@ -2353,6 +2356,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, isAnimating: function () { return THIS[ this.hash ].animating; }, + + getAnimationState: function () { + return THIS[ this.hash ].animationState; + }, }); @@ -3502,6 +3509,8 @@ function updateOnce( viewer ) { var currentAnimating = THIS[ viewer.hash ].animating; if ( !currentAnimating && animated ) { + THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATION_STARTED; + /** * Raised when any spring animation starts (zoom, pan, etc.). * @@ -3515,7 +3524,18 @@ function updateOnce( viewer ) { abortControlsAutoHide( viewer ); } - if ( animated || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) { + var lastAnimation = false; + + if (currentAnimating) { + if (animated) { + THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATING; + } else { + THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATION_FINISHED; + lastAnimation = true; + } + } + + if ( animated || lastAnimation || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) { drawWorld( viewer ); viewer._drawOverlays(); if( viewer.navigator ){ @@ -3540,6 +3560,8 @@ function updateOnce( viewer ) { } if ( currentAnimating && !animated ) { + THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.AT_REST; + /** * Raised when any spring animation ends (zoom, pan, etc.). * @@ -3558,13 +3580,6 @@ function updateOnce( viewer ) { THIS[ viewer.hash ].animating = animated; - // Intentionally use currentAnimating as the value at the current frame, - // regardless of THIS[ viewer.hash ].animating being updated. - if (currentAnimating && !animated) { - // Ensure a draw occurs once animation is over. - drawWorld( viewer ); - } - //viewer.profiler.endUpdate(); } From 12939d5bb91dbd255151ab79b74bea89793dcbc9 Mon Sep 17 00:00:00 2001 From: Sebastien ROBERT Date: Thu, 23 Dec 2021 12:15:09 +0900 Subject: [PATCH 6/6] Removed the animation state and isSubPixelRoundingRule* functions (except isSubPixelRoundingRuleUnknown), changed timing when animating is set to false --- src/openseadragon.js | 16 -------------- src/tiledimage.js | 52 ++++++-------------------------------------- src/viewer.js | 25 +++++---------------- 3 files changed, 12 insertions(+), 81 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 1c413b30..262b9298 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1429,22 +1429,6 @@ function OpenSeadragon( options ){ ALWAYS: 2 }, - /** - * An enumeration of animation states. - * @static - * @type {Object} - * @property {Number} AT_REST Indicates there are no more animations running and the image is at rest. - * @property {Number} ANIMATION_STARTED Indicates the image is in motion and it just started. - * @property {Number} ANIMATING Indicates the image was in motion and is still in motion. - * @property {Number} ANIMATION_FINISHED Indicates the image was in motion and is not in motion anymore. - */ - ANIMATION_STATES: { - AT_REST: 0, - ANIMATION_STARTED: 1, - ANIMATING: 2, - ANIMATION_FINISHED: 3 - }, - /** * Keep track of which {@link Viewer}s have been created. * - Key: {@link Element} to which a Viewer is attached. diff --git a/src/tiledimage.js b/src/tiledimage.js index 6e426e4e..8a41f050 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1960,42 +1960,6 @@ function compareTiles( previousBest, tile ) { */ var DEFAULT_SUBPIXEL_ROUNDING_RULE = $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER; -/** - * @private - * @inner - * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS} or not. - * - * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. - * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, false otherwise. - */ -function isSubPixelRoundingRuleAlways(value) { - return value === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS; -} - -/** - * @private - * @inner - * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or not. - * - * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. - * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST}, false otherwise. - */ - function isSubPixelRoundingRuleOnlyAtRest(value) { - return value === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST; -} - -/** - * @private - * @inner - * Determines whether the subpixel rounding enum value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} or not. - * - * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check. - * @returns {Boolean} True if input value is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER}, false otherwise. - */ - function isSubPixelRoundingRuleNever(value) { - return value === DEFAULT_SUBPIXEL_ROUNDING_RULE; -} - /** * @private * @inner @@ -2006,9 +1970,9 @@ function isSubPixelRoundingRuleAlways(value) { * {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} value. */ function isSubPixelRoundingRuleUnknown(value) { - return !isSubPixelRoundingRuleAlways(value) && - !isSubPixelRoundingRuleOnlyAtRest(value) && - !isSubPixelRoundingRuleNever(value); + return value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS && + value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST && + value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER; } /** @@ -2207,13 +2171,11 @@ function drawTiles( tiledImage, lastDrawn ) { var shouldRoundPositionAndSize = false; - if (isSubPixelRoundingRuleAlways(subPixelRoundingRule)) { + if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) { shouldRoundPositionAndSize = true; - } else if (isSubPixelRoundingRuleOnlyAtRest(subPixelRoundingRule)) { - shouldRoundPositionAndSize = tiledImage.viewer && ( - tiledImage.viewer.getAnimationState() === $.ANIMATION_STATES.ANIMATION_FINISHED || - tiledImage.viewer.getAnimationState() === $.ANIMATION_STATES.AT_REST // Testing AT_REST here is for the very first render after loading. - ); + } else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) { + var isAnimating = tiledImage.viewer && tiledImage.viewer.isAnimating(); + shouldRoundPositionAndSize = !isAnimating; } for (var i = lastDrawn.length - 1; i >= 0; i--) { diff --git a/src/viewer.js b/src/viewer.js index 57b46cad..86990297 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -203,7 +203,6 @@ $.Viewer = function( options ) { fsBoundsDelta: new $.Point( 1, 1 ), prevContainerSize: null, animating: false, - animationState: $.ANIMATION_STATES.AT_REST, forceRedraw: false, mouseInside: false, group: null, @@ -714,7 +713,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, } THIS[ this.hash ].animating = false; - THIS[ this.hash ].animationState = $.ANIMATION_STATES.AT_REST; this.world.removeAll(); this.imageLoader.clear(); @@ -2356,10 +2354,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, isAnimating: function () { return THIS[ this.hash ].animating; }, - - getAnimationState: function () { - return THIS[ this.hash ].animationState; - }, }); @@ -3509,8 +3503,6 @@ function updateOnce( viewer ) { var currentAnimating = THIS[ viewer.hash ].animating; if ( !currentAnimating && animated ) { - THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATION_STARTED; - /** * Raised when any spring animation starts (zoom, pan, etc.). * @@ -3524,18 +3516,13 @@ function updateOnce( viewer ) { abortControlsAutoHide( viewer ); } - var lastAnimation = false; + var isAnimationFinished = currentAnimating && !animated; - if (currentAnimating) { - if (animated) { - THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATING; - } else { - THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.ANIMATION_FINISHED; - lastAnimation = true; - } + if ( isAnimationFinished ) { + THIS[ viewer.hash ].animating = false; } - if ( animated || lastAnimation || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) { + if ( animated || isAnimationFinished || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) { drawWorld( viewer ); viewer._drawOverlays(); if( viewer.navigator ){ @@ -3559,9 +3546,7 @@ function updateOnce( viewer ) { } } - if ( currentAnimating && !animated ) { - THIS[ viewer.hash ].animationState = $.ANIMATION_STATES.AT_REST; - + if ( isAnimationFinished ) { /** * Raised when any spring animation ends (zoom, pan, etc.). *