Merge master

This commit is contained in:
Aiosa 2024-06-01 16:37:12 +02:00
commit 0a035afc2d
16 changed files with 309 additions and 155 deletions

39
CITATION.cff Normal file
View File

@ -0,0 +1,39 @@
cff-version: 1.2.0
title: OpenSeadragon
message: "If you use this software, please cite it using the metadata from this file."
type: software
authors:
- given-names: Ian
family-names: Gilman
email: ian@iangilman.com
- given-names: Aseem
family-names: Kishore
- given-names: Chris
family-names: Thatcher
- given-names: Mark
family-names: Salsbery
- given-names: Antoine
family-names: Vandecreme
- given-names: Thomas
family-names: Pearce
identifiers:
- type: url
value: https://openseadragon.github.io/
description: Homepage
- type: url
value: https://github.com/openseadragon/openseadragon
description: Repository
repository-code: https://github.com/openseadragon/openseadragon
url: https://openseadragon.github.io/
abstract: "An open-source, web-based viewer for high-resolution zoomable images, implemented in pure JavaScript, for desktop and mobile."
keywords:
- javascript
- image
- zooming
- viewer
- image-viewer
- high-resolution
- iiif
license: BSD-3-Clause
version: 4.1.1
date-released: 2024-04-01

View File

@ -5,18 +5,27 @@ OPENSEADRAGON CHANGELOG
* BREAKING CHANGE: Dropped support for IE11 (#2300, #2361 @AndrewADev) * BREAKING CHANGE: Dropped support for IE11 (#2300, #2361 @AndrewADev)
* DEPRECATION: The OpenSeadragon.createCallback function is no longer recommended (#2367 @akansjain) * DEPRECATION: The OpenSeadragon.createCallback function is no longer recommended (#2367 @akansjain)
* The viewer now uses WebGL when available (#2310, #2462, #2466, #2468, #2469, #2472, #2478 @pearcetm, @Aiosa, @thec0keman) * The viewer now uses WebGL when available (#2310, #2462, #2466, #2468, #2469, #2472, #2478, #2488, #2492 @pearcetm, @Aiosa, @thec0keman)
* Added webp to supported image formats (#2455 @BeebBenjamin) * Added webp to supported image formats (#2455 @BeebBenjamin)
* Introduced maxTilesPerFrame option to allow loading more tiles simultaneously (#2387 @jetic83) * Introduced maxTilesPerFrame option to allow loading more tiles simultaneously (#2387 @jetic83)
* Now when creating a viewer or navigator, we leave its position style alone if possible (#2393 @VIRAT9358) * Now when creating a viewer or navigator, we leave its position style alone if possible (#2393 @VIRAT9358)
* Added getter & setter for Viewport.maxZoomPixelRatio (#2506 @eug-L)
* Test improvements (#2382 @AndrewADev) * Test improvements (#2382 @AndrewADev)
* MouseTracker options documentation fixes (#2389 @msalsbery) * MouseTracker options documentation fixes (#2389 @msalsbery)
* Improved documentation and error message for Viewport.imageToViewportZoom (#2505 @eug-L)
* Fixed documentation typos (#2507 @frameflare)
* Fixed: Sometimes if the viewport was flipped and the user zoomed in far enough, it would flip back (#2364 @SebDelile) * Fixed: Sometimes if the viewport was flipped and the user zoomed in far enough, it would flip back (#2364 @SebDelile)
* Fixed: Strange behavior if IIIF sizes were not in ascending order (#2416 @lutzhelm)
* Fixed: Two-finger tap on a Mac trackpad would zoom you out (#2431 @cavenel) * Fixed: Two-finger tap on a Mac trackpad would zoom you out (#2431 @cavenel)
* Fixed: dragToPan gesture could not be disabled when flickEnabled was activated (#2464 @jonasengelmann) * Fixed: dragToPan gesture could not be disabled when flickEnabled was activated (#2464 @jonasengelmann)
* Fixed: placeholderFillStyle didn't work properly when the image was rotated (#2469 @pearcetm) * Fixed: placeholderFillStyle didn't work properly when the image was rotated (#2469 @pearcetm)
* Fixed: Sometimes exponential springs wouldn't ever settle (#2469 @pearcetm) * Fixed: Sometimes exponential springs wouldn't ever settle (#2469 @pearcetm)
* Fixed: The navigator wouldn't update its tracking rectangle when the navigator was resized (#2491 @pearcetm)
* Fixed: The drawer would improperly crop when the viewport was flipped and a tiled image was rotated (#2511 @pearcetm, @eug-L)
* Fixed: Flipped viewport caused image to be flipped again when going fullscreen or resizing (#2518 @pearcetm)
4.1.1:
* Fixed: Strange behavior if IIIF sizes were not in ascending order (#2416 @lutzhelm)
4.1.0: 4.1.0:

View File

@ -1,6 +1,6 @@
{ {
"name": "openseadragon", "name": "openseadragon",
"version": "4.1.0", "version": "4.1.1",
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
"keywords": [ "keywords": [
"image", "image",

View File

@ -80,7 +80,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
// Canvas default is "true", so this will only be changed if user specifies "false" in the options or via setImageSmoothinEnabled. // Canvas default is "true", so this will only be changed if user specifies "false" in the options or via setImageSmoothinEnabled.
this._imageSmoothingEnabled = true; this._imageSmoothingEnabled = true;
// Since the tile-drawn and tile-drawing events are fired by this drawer, make sure handlers can be added for them // Since the tile-drawn and tile-drawing events are fired by this drawer, make sure handlers can be added for them
this.viewer.allowEventHandler("tile-drawn"); this.viewer.allowEventHandler("tile-drawn");
this.viewer.allowEventHandler("tile-drawing"); this.viewer.allowEventHandler("tile-drawing");
@ -119,7 +118,9 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
*/ */
draw(tiledImages) { draw(tiledImages) {
this._prepareNewFrame(); // prepare to draw a new frame this._prepareNewFrame(); // prepare to draw a new frame
if(this.viewer.viewport.getFlip() !== this._viewportFlipped){
this._flip();
}
for(const tiledImage of tiledImages){ for(const tiledImage of tiledImages){
if (tiledImage.opacity !== 0) { if (tiledImage.opacity !== 0) {
this._drawTiles(tiledImage); this._drawTiles(tiledImage);
@ -147,9 +148,11 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
} }
/** /**
* @param {TiledImage} tiledImage the tiled image that is calling the function
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams. * @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
* @private
*/ */
minimumOverlapRequired() { minimumOverlapRequired(tiledImage) {
return true; return true;
} }
@ -189,6 +192,14 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
context.restore(); context.restore();
} }
/**
* Test whether the current context is flipped or not
* @private
*/
get _viewportFlipped(){
return this.context.getTransform().a < 0;
}
/** /**
* Fires the tile-drawing event. * Fires the tile-drawing event.
* @private * @private
@ -310,13 +321,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
tiledImage.getClippedBounds(true)) tiledImage.getClippedBounds(true))
.getIntegerBoundingBox(); .getIntegerBoundingBox();
if(this.viewer.viewport.getFlip()) {
if (this.viewport.getRotation(true) % 360 !== 0 ||
tiledImage.getRotation(true) % 360 !== 0) {
bounds.x = this.viewer.container.clientWidth - (bounds.x + bounds.width);
}
}
bounds = bounds.times($.pixelDensityRatio); bounds = bounds.times($.pixelDensityRatio);
} }
this._clear(true, bounds); this._clear(true, bounds);
@ -325,27 +329,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
// When scaling, we must rotate only when blending the sketch canvas to // When scaling, we must rotate only when blending the sketch canvas to
// avoid interpolation // avoid interpolation
if (!sketchScale) { if (!sketchScale) {
if (this.viewport.getRotation(true) % 360 !== 0) { this._setRotations(tiledImage, useSketch);
this._offsetForRotation({
degrees: this.viewport.getRotation(true),
useSketch: useSketch
});
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: this.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true),
useSketch: useSketch
});
}
if (this.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(this.viewer.viewport.getFlip()) {
this._flip();
}
}
} }
let usedClip = false; let usedClip = false;
@ -465,20 +449,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
if (useSketch) { if (useSketch) {
if (sketchScale) { if (sketchScale) {
if (this.viewport.getRotation(true) % 360 !== 0) { this._setRotations(tiledImage);
this._offsetForRotation({
degrees: this.viewport.getRotation(true),
useSketch: false
});
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: this.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true),
useSketch: false
});
}
} }
this.blendSketch({ this.blendSketch({
opacity: tiledImage.opacity, opacity: tiledImage.opacity,
@ -497,15 +468,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
} }
} }
if (!sketchScale) {
if (this.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(this.viewer.viewport.getFlip()) {
this._flip();
}
}
}
this._drawDebugInfo( tiledImage, lastDrawn ); this._drawDebugInfo( tiledImage, lastDrawn );
// Fire tiled-image-drawn event. // Fire tiled-image-drawn event.
@ -587,7 +549,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
size = tile.size.times($.pixelDensityRatio); size = tile.size.times($.pixelDensityRatio);
context.save(); context.save();
// 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) { if (typeof scale === 'number' && scale !== 1) {
// draw tile at a different scale // draw tile at a different scale
@ -835,21 +796,10 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
context.strokeStyle = this.debugGridColor[colorIndex]; context.strokeStyle = this.debugGridColor[colorIndex];
context.fillStyle = this.debugGridColor[colorIndex]; context.fillStyle = this.debugGridColor[colorIndex];
if (this.viewport.getRotation(true) % 360 !== 0 ) { this._setRotations(tiledImage);
this._offsetForRotation({degrees: this.viewport.getRotation(true)});
} if(this._viewportFlipped){
if (tiledImage.getRotation(true) % 360 !== 0) { this._flip({point: tile.position.plus(tile.size.divide(2))});
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: tiledImage.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true)
});
}
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}
} }
context.strokeRect( context.strokeRect(
@ -864,7 +814,8 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
// Rotate the text the right way around. // Rotate the text the right way around.
context.translate( tileCenterX, tileCenterY ); context.translate( tileCenterX, tileCenterY );
context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) ); const angleInDegrees = this.viewport.getRotation(true);
context.rotate( Math.PI / 180 * -angleInDegrees );
context.translate( -tileCenterX, -tileCenterY ); context.translate( -tileCenterX, -tileCenterY );
if( tile.x === 0 && tile.y === 0 ){ if( tile.x === 0 && tile.y === 0 ){
@ -917,13 +868,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
this._restoreRotationChanges(); this._restoreRotationChanges();
} }
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}
}
context.restore(); context.restore();
} }
@ -954,6 +898,33 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
return new $.Point(this.canvas.width / 2, this.canvas.height / 2); return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
} }
/**
* Set rotations for viewport & tiledImage
* @private
* @param {OpenSeadragon.TiledImage} tiledImage
* @param {Boolean} [useSketch=false]
*/
_setRotations(tiledImage, useSketch = false) {
var saveContext = false;
if (this.viewport.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: this.viewport.getRotation(true),
useSketch: useSketch,
saveContext: saveContext
});
saveContext = false;
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: this.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true),
useSketch: useSketch,
saveContext: saveContext
});
}
}
// private // private
_offsetForRotation(options) { _offsetForRotation(options) {
var point = options.point ? var point = options.point ?
@ -964,12 +935,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
context.save(); context.save();
context.translate(point.x, point.y); context.translate(point.x, point.y);
if(this.viewer.viewport.flipped){
context.rotate(Math.PI / 180 * -options.degrees);
context.scale(-1, 1);
} else{
context.rotate(Math.PI / 180 * options.degrees); context.rotate(Math.PI / 180 * options.degrees);
}
context.translate(-point.x, -point.y); context.translate(-point.x, -point.y);
} }

View File

@ -188,10 +188,11 @@ OpenSeadragon.DrawerBase = class DrawerBase{
} }
/** /**
* @param {TiledImage} tiledImage the tiled image that is calling the function
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams. * @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
* @private * @private
*/ */
minimumOverlapRequired() { minimumOverlapRequired(tiledImage) {
return false; return false;
} }

View File

@ -90,9 +90,11 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
} }
/** /**
* @param {TiledImage} tiledImage the tiled image that is calling the function
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams. * @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
* @private
*/ */
minimumOverlapRequired() { minimumOverlapRequired(tiledImage) {
return true; return true;
} }

View File

@ -309,6 +309,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
this.oldContainerSize = containerSize; this.oldContainerSize = containerSize;
this.world.update(); this.world.update();
this.world.draw(); this.world.draw();
this.update(this.viewer.viewport);
} }
} }
}, },
@ -355,7 +356,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
/** /**
* Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs. * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
* @function * @function
* @param {OpenSeadragon.Viewport} The viewport this navigator is tracking. * @param {OpenSeadragon.Viewport} [viewport] The viewport to display. Default: the viewport this navigator is tracking.
*/ */
update: function( viewport ) { update: function( viewport ) {
@ -366,6 +367,10 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
topleft, topleft,
bottomright; bottomright;
if(!viewport){
viewport = this.viewer.viewport;
}
viewerSize = $.getElementSize( this.viewer.element ); viewerSize = $.getElementSize( this.viewer.element );
if ( this._resizeWithViewer && viewerSize.x && viewerSize.y && !viewerSize.equals( this.oldViewerSize ) ) { if ( this._resizeWithViewer && viewerSize.x && viewerSize.y && !viewerSize.equals( this.oldViewerSize ) ) {
this.oldViewerSize = viewerSize; this.oldViewerSize = viewerSize;

View File

@ -1741,7 +1741,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
tileCenter = positionT.plus( sizeT.divide( 2 ) ), tileCenter = positionT.plus( sizeT.divide( 2 ) ),
tileSquaredDistance = viewportCenter.squaredDistanceTo( tileCenter ); tileSquaredDistance = viewportCenter.squaredDistanceTo( tileCenter );
if(this.viewer.drawer.minimumOverlapRequired()){ if(this.viewer.drawer.minimumOverlapRequired(this)){
if ( !overlap ) { if ( !overlap ) {
sizeC = sizeC.plus( new $.Point(1, 1)); sizeC = sizeC.plus( new $.Point(1, 1));
} }

View File

@ -585,13 +585,13 @@ $.TileSource.prototype = {
}, },
/** /**
* Responsible determining if a the particular TileSource supports the * Responsible for determining if the particular TileSource supports the
* data format ( and allowed to apply logic against the url the data was * data format ( and allowed to apply logic against the url the data was
* loaded from, if any ). Overriding implementations are expected to do * loaded from, if any ). Overriding implementations are expected to do
* something smart with data and / or url to determine support. Also * something smart with data and / or url to determine support. Also
* understand that iteration order of TileSources is not guarunteed so * understand that iteration order of TileSources is not guaranteed so
* please make sure your data or url is expressive enough to ensure a simple * please make sure your data or url is expressive enough to ensure a simple
* and sufficient mechanisim for clear determination. * and sufficient mechanism for clear determination.
* @function * @function
* @param {String|Object|Array|Document} data * @param {String|Object|Array|Document} data
* @param {String} url - the url the data was loaded * @param {String} url - the url the data was loaded
@ -828,7 +828,7 @@ $.TileSource.prototype = {
}; };
// Load the tile with an AJAX request if the loadWithAjax option is // Load the tile with an AJAX request if the loadWithAjax option is
// set. Otherwise load the image by setting the source proprety of the image object. // set. Otherwise load the image by setting the source property of the image object.
if (context.loadWithAjax) { if (context.loadWithAjax) {
dataStore.request = $.makeAjaxRequest({ dataStore.request = $.makeAjaxRequest({
url: context.src, url: context.src,

View File

@ -2206,7 +2206,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* viewport which the location coordinates will be treated as relative * viewport which the location coordinates will be treated as relative
* to. * to.
* @param {function} [onDraw] - If supplied the callback is called when the overlay * @param {function} [onDraw] - If supplied the callback is called when the overlay
* needs to be drawn. It it the responsibility of the callback to do any drawing/positioning. * needs to be drawn. It is the responsibility of the callback to do any drawing/positioning.
* It is passed position, size and element. * It is passed position, size and element.
* @returns {OpenSeadragon.Viewer} Chainable. * @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:add-overlay * @fires OpenSeadragon.Viewer.event:add-overlay

View File

@ -1711,7 +1711,7 @@ $.Viewport.prototype = {
* 1 means original image size, 0.5 half size... * 1 means original image size, 0.5 half size...
* Viewport zoom: ratio of the displayed image's width to viewport's width. * Viewport zoom: ratio of the displayed image's width to viewport's width.
* 1 means identical width, 2 means image's width is twice the viewport's width... * 1 means identical width, 2 means image's width is twice the viewport's width...
* Note: not accurate with multi-image. * Note: not accurate with multi-image; use [TiledImage.imageToViewportZoom] for the specific image of interest.
* @function * @function
* @param {Number} imageZoom The image zoom * @param {Number} imageZoom The image zoom
* target zoom. * target zoom.
@ -1723,7 +1723,7 @@ $.Viewport.prototype = {
if (count > 1) { if (count > 1) {
if (!this.silenceMultiImageWarnings) { if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.imageToViewportZoom] is not accurate ' + $.console.error('[Viewport.imageToViewportZoom] is not accurate ' +
'with multi-image.'); 'with multi-image. Instead, use [TiledImage.imageToViewportZoom] for the specific image of interest');
} }
} else if (count === 1) { } else if (count === 1) {
// It is better to use TiledImage.imageToViewportZoom // It is better to use TiledImage.imageToViewportZoom
@ -1789,8 +1789,42 @@ $.Viewport.prototype = {
*/ */
this.viewer.raiseEvent('flip', {flipped: state}); this.viewer.raiseEvent('flip', {flipped: state});
return this; return this;
},
/**
* Gets current max zoom pixel ratio
* @function
* @returns {Number} Max zoom pixel ratio
*/
getMaxZoomPixelRatio: function() {
return this.maxZoomPixelRatio;
},
/**
* Sets max zoom pixel ratio
* @function
* @param {Number} ratio - Max zoom pixel ratio
* @param {Boolean} [applyConstraints=true] - Apply constraints after setting ratio;
* Takes effect only if current zoom is greater than set max zoom pixel ratio
* @param {Boolean} [immediately=false] - Whether to animate to new zoom
*/
setMaxZoomPixelRatio: function(ratio, applyConstraints = true, immediately = false) {
$.console.assert(!isNaN(ratio), "[Viewport.setMaxZoomPixelRatio] ratio must be a number");
if (isNaN(ratio)) {
return;
} }
this.maxZoomPixelRatio = ratio;
if (applyConstraints) {
if (this.getZoom() > this.getMaxZoom()) {
this.applyConstraints(immediately);
}
}
},
}; };
}( OpenSeadragon )); }( OpenSeadragon ));

View File

@ -206,6 +206,16 @@
return 'webgl'; return 'webgl';
} }
/**
* @param {TiledImage} tiledImage the tiled image that is calling the function
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
* @private
*/
minimumOverlapRequired(tiledImage) {
// return true if the tiled image is tainted, since the backup canvas drawer will be used.
return tiledImage.isTainted();
}
/** /**
* create the HTML element (canvas in this case) that the image will be drawn into * create the HTML element (canvas in this case) that the image will be drawn into
* @private * @private
@ -347,7 +357,7 @@
// This can apparently happen on some systems if too many WebGL contexts have been created // This can apparently happen on some systems if too many WebGL contexts have been created
// in which case maxTextures can be null, leading to out of bounds errors with the array. // in which case maxTextures can be null, leading to out of bounds errors with the array.
// For example, when viewers were created and not destroyed in the test suite, this error // For example, when viewers were created and not destroyed in the test suite, this error
// occured in the TravisCI tests, though it did not happen when testing locally either in // occurred in the TravisCI tests, though it did not happen when testing locally either in
// a browser or on the command line via grunt test. // a browser or on the command line via grunt test.
throw(new Error(`WegGL error: bad value for gl parameter MAX_TEXTURE_IMAGE_UNITS (${maxTextures}). This could happen throw(new Error(`WegGL error: bad value for gl parameter MAX_TEXTURE_IMAGE_UNITS (${maxTextures}). This could happen
@ -513,10 +523,14 @@
} }
this._outputContext.restore(); this._outputContext.restore();
if(tiledImage.debugMode){ if(tiledImage.debugMode){
let colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length; const flipped = this.viewer.viewport.getFlip();
let strokeStyle = this.debugGridColor[colorIndex]; if(flipped){
let fillStyle = this.debugGridColor[colorIndex]; this._flip();
this._drawDebugInfo(tilesToDraw, tiledImage, strokeStyle, fillStyle); }
this._drawDebugInfo(tilesToDraw, tiledImage, flipped);
if(flipped){
this._flip();
}
} }
@ -998,32 +1012,64 @@
this._clippingContext.restore(); this._clippingContext.restore();
} }
/**
* Set rotations for viewport & tiledImage
* @private
* @param {OpenSeadragon.TiledImage} tiledImage
*/
_setRotations(tiledImage) {
var saveContext = false;
if (this.viewport.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: this.viewport.getRotation(true),
saveContext: saveContext
});
saveContext = false;
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: this.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true),
saveContext: saveContext
});
}
}
// private // private
_offsetForRotation(options) { _offsetForRotation(options) {
var point = options.point ? var point = options.point ?
options.point.times($.pixelDensityRatio) : options.point.times($.pixelDensityRatio) :
new $.Point(this._outputCanvas.width / 2, this._outputCanvas.height / 2); this._getCanvasCenter();
var context = this._outputContext; var context = this._outputContext;
context.save(); context.save();
context.translate(point.x, point.y); context.translate(point.x, point.y);
if(this.viewport.flipped){
context.rotate(Math.PI / 180 * -options.degrees);
context.scale(-1, 1);
} else{
context.rotate(Math.PI / 180 * options.degrees); context.rotate(Math.PI / 180 * options.degrees);
}
context.translate(-point.x, -point.y); context.translate(-point.x, -point.y);
} }
// private // private
_drawDebugInfo( tilesToDraw, tiledImage, stroke, fill ) { _flip(options) {
options = options || {};
var point = options.point ?
options.point.times($.pixelDensityRatio) :
this._getCanvasCenter();
var context = this._outputContext;
context.translate(point.x, 0);
context.scale(-1, 1);
context.translate(-point.x, 0);
}
// private
_drawDebugInfo( tilesToDraw, tiledImage, flipped ) {
for ( var i = tilesToDraw.length - 1; i >= 0; i-- ) { for ( var i = tilesToDraw.length - 1; i >= 0; i-- ) {
var tile = tilesToDraw[ i ].tile; var tile = tilesToDraw[ i ].tile;
try { try {
this._drawDebugInfoOnTile(tile, tilesToDraw.length, i, tiledImage, stroke, fill); this._drawDebugInfoOnTile(tile, tilesToDraw.length, i, tiledImage, flipped);
} catch(e) { } catch(e) {
$.console.error(e); $.console.error(e);
} }
@ -1031,30 +1077,20 @@
} }
// private // private
_drawDebugInfoOnTile(tile, count, i, tiledImage, stroke, fill) { _drawDebugInfoOnTile(tile, count, i, tiledImage, flipped) {
var context = this._outputContext; var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
var context = this.context;
context.save(); context.save();
context.lineWidth = 2 * $.pixelDensityRatio; context.lineWidth = 2 * $.pixelDensityRatio;
context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
context.strokeStyle = stroke; context.strokeStyle = this.debugGridColor[colorIndex];
context.fillStyle = fill; context.fillStyle = this.debugGridColor[colorIndex];
if (this.viewport.getRotation(true) % 360 !== 0 ) { this._setRotations(tiledImage);
this._offsetForRotation({degrees: this.viewport.getRotation(true)});
} if(flipped){
if (tiledImage.getRotation(true) % 360 !== 0) { this._flip({point: tile.position.plus(tile.size.divide(2))});
this._offsetForRotation({
degrees: tiledImage.getRotation(true),
point: tiledImage.viewport.pixelFromPointNoRotate(
tiledImage._getRotationPoint(true), true)
});
}
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}
} }
context.strokeRect( context.strokeRect(
@ -1069,7 +1105,8 @@
// Rotate the text the right way around. // Rotate the text the right way around.
context.translate( tileCenterX, tileCenterY ); context.translate( tileCenterX, tileCenterY );
context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) ); const angleInDegrees = this.viewport.getRotation(true);
context.rotate( Math.PI / 180 * -angleInDegrees );
context.translate( -tileCenterX, -tileCenterY ); context.translate( -tileCenterX, -tileCenterY );
if( tile.x === 0 && tile.y === 0 ){ if( tile.x === 0 && tile.y === 0 ){
@ -1122,13 +1159,6 @@
this._restoreRotationChanges(); this._restoreRotationChanges();
} }
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}
}
context.restore(); context.restore();
} }
@ -1156,6 +1186,15 @@
} }
/**
* Get the canvas center
* @private
* @returns {OpenSeadragon.Point} The center point of the canvas
*/
_getCanvasCenter() {
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
}
// private // private
_restoreRotationChanges() { _restoreRotationChanges() {
var context = this._outputContext; var context = this._outputContext;

View File

@ -277,11 +277,14 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
* Draws all items. * Draws all items.
*/ */
draw: function() { draw: function() {
return new $.Promise((resolve) => {
this.viewer.drawer.draw(this._items); this.viewer.drawer.draw(this._items);
this._needsDraw = false; this._needsDraw = false;
for (let item of this._items) { for (let item of this._items) {
this._needsDraw = item.setDrawn() || this._needsDraw; this._needsDraw = item.setDrawn() || this._needsDraw;
} }
resolve();
});
}, },
/** /**

35
test/demo/basic2.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon maxTilesPerFrame Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<style type="text/css">
.openseadragon1 {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div>
Simple demo page to show an OpenSeadragon viewer with a higher maxTilesPerFrame.
</div>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "https://openseadragon.github.io/example-images/duomo/duomo.dzi",
showNavigator:true,
debugMode:true,
maxTilesPerFrame:3,
});
</script>
</body>
</html>

View File

@ -243,7 +243,7 @@
/* /*
* FIXME: following https://iiif.io/api/image/3.0/#47-canonical-uri-syntax and * FIXME: following https://iiif.io/api/image/3.0/#47-canonical-uri-syntax and
* https://iiif.io/api/image/2.1/#canonical-uri-syntax, I'd expect 'max' to be required to * https://iiif.io/api/image/2.1/#canonical-uri-syntax, I'd expect 'max' to be required to
* be served by a level 0 compliant service instead of 'w,h', 'full' instead of 'w,' respectivley. * be served by a level 0 compliant service instead of 'w,h', 'full' instead of 'w,' respectively.
*/ */
//assert.equal(levelsVersion3[1].url, 'http://example.com/identifier/full/max/0/default.jpg'); //assert.equal(levelsVersion3[1].url, 'http://example.com/identifier/full/max/0/default.jpg');
}); });

View File

@ -1407,4 +1407,25 @@
viewer.open(DZI_PATH); viewer.open(DZI_PATH);
}); });
QUnit.test('setMaxZoomPixelRatio', function(assert) {
var done = assert.async();
var openHandler = function(event) {
viewer.removeHandler('open', openHandler);
var viewport = viewer.viewport;
for (var i = 0; i < testZoomLevels.length; i++) {
viewport.setMaxZoomPixelRatio(testZoomLevels[i])
assert.equal(viewport.getMaxZoomPixelRatio(), testZoomLevels[i], "Max zoom pixel ratio is set correctly.");
}
viewport.zoomTo(viewport.getMaxZoom())
viewport.setMaxZoomPixelRatio(testZoomLevels[0], true)
assert.equal(viewport.getZoom(), viewport.getMaxZoom(), "Zoom should be adjusted to max zoom level.");
done();
};
viewer.addHandler('open', openHandler);
viewer.open(DZI_PATH);
});
})(); })();