From 0c358c140dae8bcd5cbc568ef6236a840af84efe Mon Sep 17 00:00:00 2001 From: Ruven Date: Mon, 17 Apr 2023 21:08:18 +0200 Subject: [PATCH] Use resolution level dimensions provided in the info.json "sizes" field to determine tile sizes as well as the number of tiles that exist at a particular resolution. Fall back to calculation using ceil() if no resolution sizes provided. Avoids rounding errors for edge tiles and fixes https://github.com/openseadragon/openseadragon/issues/2321 --- src/iiiftilesource.js | 63 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index 08932b56..8e151cbe 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -2,7 +2,7 @@ * OpenSeadragon - IIIFTileSource * * Copyright (C) 2009 CodePlex Foundation - * Copyright (C) 2010-2022 OpenSeadragon contributors + * Copyright (C) 2010-2023 OpenSeadragon contributors * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -333,7 +333,45 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea } } - return $.TileSource.prototype.getNumTiles.call(this, level); + // Use supplied list of scaled resolution sizes if these exist + var levelSize = this.getLevelSize(level); + if( levelSize ) { + var x = Math.ceil( levelSize.width / this.getTileWidth(level) ), + y = Math.ceil( levelSize.height / this.getTileHeight(level) ); + return new $.Point( x, y ); + } + // Otherwise call default TileSource->getNumTiles() function + else { + return $.TileSource.prototype.getNumTiles.call(this, level); + } + }, + + + /** + * Determine image size at a given resolution level using the info.json "sizes" field + * Returns null if this information is not present + * @function {Number} level + */ + getLevelSize: function( level ) { + + var numLevels = this.maxLevel - this.minLevel; + // Need to take into account that the list may or may not include the full resolution size + if( this.sizes && ((this.sizes.length === numLevels) || + (this.sizes.length === numLevels + 1)) ) { + var levelWidth, levelHeight; + if( this.sizes.length === numLevels ) { + levelWidth = (level === this.sizes.length) ? this.width : this.sizes[level].width; + levelHeight = (level === this.sizes.length) ? this.height : this.sizes[level].height; + } + else { + levelWidth = this.sizes[level].width; + levelHeight = this.sizes[level].height; + } + return { width: levelWidth, height: levelHeight }; + } + else { + return null; + } }, @@ -375,10 +413,9 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea var IIIF_ROTATION = '0', //## get the scale (level as a decimal) scale = Math.pow( 0.5, this.maxLevel - level ), - //# image dimensions at this level - levelWidth = Math.round( this.width * scale ), - levelHeight = Math.round( this.height * scale ), + levelWidth, + levelHeight, //## iiif region tileWidth, @@ -396,6 +433,18 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality, uri; + // Use supplied list of scaled resolution sizes if these exist + var levelSize = this.getLevelSize( level ); + if( levelSize ) { + levelWidth = levelSize.width; + levelHeight = levelSize.height; + } + // Otherwise calculate the sizes ourselves + else { + levelWidth = Math.ceil( this.width * scale ); + levelHeight = Math.ceil( this.height * scale ); + } + tileWidth = this.getTileWidth(level); tileHeight = this.getTileHeight(level); iiifTileSizeWidth = Math.round( tileWidth / scale ); @@ -426,8 +475,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea } else { iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' ); } - iiifSizeW = Math.round( iiifTileW * scale ); - iiifSizeH = Math.round( iiifTileH * scale ); + iiifSizeW = Math.min( tileWidth, levelWidth - (x * tileWidth) ); + iiifSizeH = Math.min( tileHeight, levelHeight - (y * tileHeight) ); if ( this.version === 2 && iiifSizeW === this.width ) { iiifSize = "full"; } else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) {