This commit is contained in:
Antoine Vandecreme 2016-01-23 18:49:58 -05:00
commit 33021ac24f
19 changed files with 377 additions and 32 deletions

View File

@ -8,6 +8,11 @@ OPENSEADRAGON CHANGELOG
* Now supporting rotation in the Rect class (#782)
* Drag outside of iframe now works better, as long as both pages are on the same domain (#790)
* Coordinate conversion now takes rotation into account (#796)
* Support tile-less IIIF as per LegacyTileSource (#816)
* You can now give an empty string to the tabIndex option (#805)
* Fixed issue with rotation and clicking in the navigator (#807)
* Broadened the check for mime type in LegacyTileSource URLs to allow query strings (#819)
* Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814)
2.1.0:

View File

@ -371,10 +371,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
* @param {Float} opacity The opacity of the blending.
* @param {Float} [scale=1] The scale at which tiles were drawn on the sketch. Default is 1.
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
* @param OpenSeadragon.Point} [translate] A translation vector that was used to draw the tiles
* @param {OpenSeadragon.Point} [translate] A translation vector that was used to draw the tiles
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
* @returns {undefined}
*/
blendSketch: function(opacity, scale, translate) {
blendSketch: function(opacity, scale, translate, compositeOperation) {
if (!this.useCanvas || !this.sketchCanvas) {
return;
}
@ -394,6 +395,9 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
this.context.save();
this.context.globalAlpha = opacity;
if (compositeOperation) {
this.context.globalCompositeOperation = compositeOperation;
}
this.context.drawImage(
this.sketchCanvas,
position.x - widthExt * scale,

View File

@ -36,8 +36,8 @@
/**
* @class IIIFTileSource
* @classdesc A client implementation of the International Image Interoperability
* Format: Image API 1.0 - 2.0
* @classdesc A client implementation of the International Image Interoperability Framework
* Format: Image API 1.0 - 2.1
*
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource
@ -83,7 +83,7 @@ $.IIIFTileSource = function( options ){
}
}
}
} else {
} else if ( canBeTiled(options.profile) ) {
// use the largest of tileOptions that is smaller than the short dimension
var shortDim = Math.min( this.height, this.width ),
tileOptions = [256,512,1024],
@ -101,9 +101,28 @@ $.IIIFTileSource = function( options ){
// If we're smaller than 256, just use the short side.
options.tileSize = shortDim;
}
} else if (this.sizes && this.sizes.length > 0) {
// This info.json can't be tiled, but we can still construct a legacy pyramid from the sizes array.
// In this mode, IIIFTileSource will call functions from the abstract baseTileSource or the
// LegacyTileSource instead of performing IIIF tiling.
this.emulateLegacyImagePyramid = true;
options.levels = constructLevels( this );
// use the largest available size to define tiles
$.extend( true, options, {
width: options.levels[ options.levels.length - 1 ].width,
height: options.levels[ options.levels.length - 1 ].height,
tileSize: Math.max( options.height, options.width ),
tileOverlap: 0,
minLevel: 0,
maxLevel: options.levels.length - 1
});
this.levels = options.levels;
} else {
$.console.error("Nothing in the info.json to construct image pyramids from");
}
if ( !options.maxLevel ) {
if (!options.maxLevel && !this.emulateLegacyImagePyramid) {
if (!this.scale_factors) {
options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2)));
} else {
@ -192,6 +211,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
* @param {Number} level
*/
getTileWidth: function( level ) {
if(this.emulateLegacyImagePyramid) {
return $.TileSource.prototype.getTileWidth.call(this, level);
}
var scaleFactor = Math.pow(2, this.maxLevel - level);
if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
@ -206,6 +230,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
* @param {Number} level
*/
getTileHeight: function( level ) {
if(this.emulateLegacyImagePyramid) {
return $.TileSource.prototype.getTileHeight.call(this, level);
}
var scaleFactor = Math.pow(2, this.maxLevel - level);
if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
@ -214,9 +243,61 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
return this._tileHeight;
},
/**
* @function
* @param {Number} level
*/
getLevelScale: function ( level ) {
if(this.emulateLegacyImagePyramid) {
var levelScale = NaN;
if (this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel) {
levelScale =
this.levels[level].width /
this.levels[this.maxLevel].width;
}
return levelScale;
}
return $.TileSource.prototype.getLevelScale.call(this, level);
},
/**
* Responsible for retreiving the url which will return an image for the
* @function
* @param {Number} level
*/
getNumTiles: function( level ) {
if(this.emulateLegacyImagePyramid) {
var scale = this.getLevelScale(level);
if (scale) {
return new $.Point(1, 1);
} else {
return new $.Point(0, 0);
}
}
return $.TileSource.prototype.getNumTiles.call(this, level);
},
/**
* @function
* @param {Number} level
* @param {OpenSeadragon.Point} point
*/
getTileAtPoint: function( level, point ) {
if(this.emulateLegacyImagePyramid) {
return new $.Point(0, 0);
}
return $.TileSource.prototype.getTileAtPoint.call(this, level, point);
},
/**
* Responsible for retrieving the url which will return an image for the
* region specified by the given x, y, and level components.
* @function
* @param {Number} level - z index
@ -226,6 +307,14 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
*/
getTileUrl: function( level, x, y ){
if(this.emulateLegacyImagePyramid) {
var url = null;
if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
url = this.levels[ level ].url;
}
return url;
}
//# constants
var IIIF_ROTATION = '0',
//## get the scale (level as a decimal)
@ -280,6 +369,40 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
});
/**
* Determine whether arbitrary tile requests can be made against a service with the given profile
* @function
* @param {object} profile - IIIF profile object
* @throws {Error}
*/
function canBeTiled (profile ) {
var level0Profiles = [
"http://library.stanford.edu/iiif/image-api/compliance.html#level0",
"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0",
"http://iiif.io/api/image/2/level0.json"
];
var isLevel0 = (level0Profiles.indexOf(profile[0]) != -1);
return !isLevel0 || (profile.indexOf("sizeByW") != -1);
}
/**
* Build the legacy pyramid URLs (one tile per level)
* @function
* @param {object} options - infoJson
* @throws {Error}
*/
function constructLevels(options) {
var levels = [];
for(var i=0; i<options.sizes.length; i++) {
levels.push({
url: options['@id'] + '/full/' + options.sizes[i].width + ',/0/default.jpg',
width: options.sizes[i].width,
height: options.sizes[i].height
});
}
return levels.sort(function(a,b){return a.width - b.width;});
}
function configureFromXml10(xmlDoc) {
//parse the xml
@ -330,4 +453,5 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
}
}( OpenSeadragon ));

View File

@ -132,18 +132,7 @@
});
$.addEvent(image, 'error', function () {
/***
* Raised when an error occurs loading a TileSource.
*
* @event open-failed
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference
* to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
// Note: this event is documented elsewhere, in TileSource
_this.raiseEvent('open-failed', {
message: "Error loading image at " + url,
source: url

View File

@ -215,7 +215,7 @@ function filterFiles( files ){
if( file.height &&
file.width &&
file.url && (
file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)$/) || (
file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)(?:\?.*)?$/) || (
file.mimetype &&
file.mimetype.toLowerCase().match(/^.*\/(png|jpg|jpeg|gif)$/)
)

View File

@ -206,6 +206,11 @@
* @property {Number} [opacity=1]
* Default opacity of the tiled images (1=opaque, 0=transparent)
*
* @property {String} [compositeOperation=null]
* Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',
* 'destination-over', 'destination-atop', 'destination-in',
* 'destination-out', 'lighter', 'copy' or 'xor'
*
* @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null]
* Draws a colored rectangle behind the tile if it is not loaded yet.
* You can pass a CSS color value like "#FF8800".
@ -1067,6 +1072,7 @@ if (typeof define === 'function' && define.amd) {
// APPEARANCE
opacity: 1,
compositeOperation: null,
placeholderFillStyle: null,
//REFERENCE STRIP SETTINGS

View File

@ -41,7 +41,7 @@
* @param {Number} options.springStiffness - Spring stiffness. Must be greater than zero.
* The closer to zero, the closer to linear animation.
* @param {Number} options.animationTime - Animation duration per spring, in seconds.
* Must be greater than zero.
* Must be zero or greater.
* @param {Number} [options.initial=0] - Initial value of spring.
* @param {Boolean} [options.exponential=false] - Whether this spring represents
* an exponential scale (such as zoom) and should be animated accordingly. Note that
@ -79,8 +79,8 @@ $.Spring = function( options ) {
$.console.assert(typeof options.springStiffness === "number" && options.springStiffness !== 0,
"[OpenSeadragon.Spring] options.springStiffness must be a non-zero number");
$.console.assert(typeof options.animationTime === "number" && options.animationTime !== 0,
"[OpenSeadragon.Spring] options.animationTime must be a non-zero number");
$.console.assert(typeof options.animationTime === "number" && options.animationTime >= 0,
"[OpenSeadragon.Spring] options.animationTime must be a number greater than or equal to 0");
if (options.exponential) {
this._exponential = true;

View File

@ -66,6 +66,7 @@
* @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.
* @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.
* @param {Number} [options.opacity=1] - Opacity the tiled image should be drawn at.
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
* @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
* @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.
@ -132,7 +133,6 @@ $.TiledImage = function( options ) {
_midDraw: false, // Is the tiledImage currently updating the viewport?
_needsDraw: true, // Does the tiledImage need to update the viewport again?
_hasOpaqueTile: false, // Do we have even one fully opaque tile?
//configurable settings
springStiffness: $.DEFAULT_SETTINGS.springStiffness,
animationTime: $.DEFAULT_SETTINGS.animationTime,
@ -147,7 +147,8 @@ $.TiledImage = function( options ) {
debugMode: $.DEFAULT_SETTINGS.debugMode,
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
opacity: $.DEFAULT_SETTINGS.opacity
opacity: $.DEFAULT_SETTINGS.opacity,
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
}, options );
@ -176,7 +177,7 @@ $.TiledImage = function( options ) {
/**
* This event is fired just before the tile is drawn giving the application a chance to alter the image.
*
* NOTE: This event is only fired when the drawer is using a <canvas>.
* NOTE: This event is only fired when the drawer is using a &lt;canvas&gt;.
*
* @event tile-drawing
* @memberof OpenSeadragon.Viewer
@ -585,6 +586,21 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this._needsDraw = true;
},
/**
* @returns {String} The TiledImage's current compositeOperation.
*/
getCompositeOperation: function() {
return this.compositeOperation;
},
/**
* @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation.
*/
setCompositeOperation: function(compositeOperation) {
this.compositeOperation = compositeOperation;
this._needsDraw = true;
},
// private
_setScale: function(scale, immediately) {
var sameTarget = (this._scaleSpring.target.value === scale);
@ -1313,7 +1329,9 @@ function drawTiles( tiledImage, lastDrawn ) {
drawDebugInfo( tiledImage, lastDrawn );
return;
}
var useSketch = tiledImage.opacity < 1;
var useSketch = tiledImage.opacity < 1 ||
(tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over');
var sketchScale;
var sketchTranslate;
@ -1413,7 +1431,7 @@ function drawTiles( tiledImage, lastDrawn ) {
if (offsetForRotation) {
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
}
tiledImage._drawer.blendSketch(tiledImage.opacity, sketchScale, sketchTranslate);
tiledImage._drawer.blendSketch(tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation);
if (offsetForRotation) {
tiledImage._drawer._restoreRotationChanges(false);
}

View File

@ -234,7 +234,7 @@ $.Viewer = function( options ) {
style.left = "0px";
}(this.canvas.style));
$.setElementTouchActionNone( this.canvas );
this.canvas.tabIndex = options.tabIndex || 0;
this.canvas.tabIndex = (options.tabIndex === undefined ? 0 : options.tabIndex);
//the container is created through applying the ControlDock constructor above
this.container.className = "openseadragon-container";
@ -1207,6 +1207,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* (portions of the image outside of this area will not be visible). Only works on
* browsers that support the HTML5 canvas.
* @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default.
* @param {String} [options.compositeOperation] How the image is composited onto other images.
* @param {Function} [options.success] A function that gets called when the image is
* successfully added. It's passed the event object which contains a single property:
* "item", the resulting TiledImage.
@ -1239,6 +1240,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
if (options.opacity === undefined) {
options.opacity = this.opacity;
}
if (options.compositeOperation === undefined) {
options.compositeOperation = this.compositeOperation;
}
var myQueueItem = {
options: options
@ -1337,6 +1341,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
clip: queueItem.options.clip,
placeholderFillStyle: queueItem.options.placeholderFillStyle,
opacity: queueItem.options.opacity,
compositeOperation: queueItem.options.compositeOperation,
springStiffness: _this.springStiffness,
animationTime: _this.animationTime,
minZoomImageRatio: _this.minZoomImageRatio,

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -0,0 +1,15 @@
{
"@context": "http://iiif.io/api/image/2/context.json",
"@id": "http://localhost:8000/test/data/iiif_2_0_sizes",
"protocol": "http://iiif.io/api/image",
"width": 6976,
"height": 5074,
"profile": ["http://iiif.io/api/image/2/level0.json"],
"sizes" : [
{"width" : 400, "height" : 291},
{"width" : 800, "height" : 582},
{"width" : 1600, "height" : 1164},
{"width" : 3200, "height": 2328},
{"width" : 6976, "height": 5074}
]
}

51
test/demo/iiif-sizes.html Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Demo - IIIF emulation of legacy image pyramid</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: 800px;
height: 600px;
}
</style>
</head>
<body>
<div>
<p>Default OpenSeadragon viewer from IIIF Source</p>
<p>This allows IIIF even if you only have a handful of static image sizes.</p>
</div>
<!--
Notes:
legacy iiif
supports
configure
getTileWidth
getTileHeight
getLevelScale
getNumTiles
getTileAtPoint
getTileUrl getTileUrl
-->
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/iiif_2_0_sizes/info.json",
showNavigator:true
});
</script>
</body>
</html>

34
test/demo/iiif.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Demo - IIIF Tiled</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: 800px;
height: 600px;
}
</style>
</head>
<body>
<div>
<p>Default OpenSeadragon viewer from IIIF Tile Source.</p>
<p>This depends on a remote server providing a IIIF Image API endpoint.</p>
</div>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "http://wellcomelibrary.org/iiif-img/b11768265-0/a6801943-b8b4-4674-908c-7d5b27e70569/info.json",
showNavigator:true
});
</script>
</body>
</html>

56
test/demo/legacy.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Demo - Legacy image pyramid</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: 800px;
height: 600px;
}
</style>
</head>
<body>
<div>
Use an array of full images at different sizes.
</div>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: {
type: 'legacy-image-pyramid',
levels:[{
url: '/test/data/iiif_2_0_sizes/full/400,/0/default.jpg',
height: 291,
width: 400
},{
url: '/test/data/iiif_2_0_sizes/full/800,/0/default.jpg',
height: 582,
width: 800
},{
url: '/test/data/iiif_2_0_sizes/full/1600,/0/default.jpg',
height: 1164,
width: 1600
},{
url: '/test/data/iiif_2_0_sizes/full/3200,/0/default.jpg',
height: 2328,
width: 3200
},{
url: '/test/data/iiif_2_0_sizes/full/6976,/0/default.jpg',
height: 5074,
width: 6976
}]
},
showNavigator:true
});
</script>
</body>
</html>

View File

@ -114,6 +114,11 @@
testOpenUrl('iiif_2_0_tiled/info.json');
});
// ----------
asyncTest('IIIF 2.0 JSON, sizes array only', function() {
testOpenUrl('iiif_2_0_sizes/info.json');
});
// ----------
asyncTest('IIIF 2.0 JSON String', function() {
testOpen(
@ -150,4 +155,37 @@
});
});
// ----------
asyncTest('Legacy Image Pyramid', function() {
// Although it is using image paths that happen to be in IIIF format, this is not a IIIFTileSource.
// The url values are opaque, just image locations.
// When emulating a legacy pyramid, IIIFTileSource calls functions from LegacyTileSource, so this
// adds a test for the legacy functionality too.
testOpen({
type: 'legacy-image-pyramid',
levels:[{
url: '/test/data/iiif_2_0_sizes/full/400,/0/default.jpg',
height: 291,
width: 400
},{
url: '/test/data/iiif_2_0_sizes/full/800,/0/default.jpg',
height: 582,
width: 800
},{
url: '/test/data/iiif_2_0_sizes/full/1600,/0/default.jpg',
height: 1164,
width: 1600
},{
url: '/test/data/iiif_2_0_sizes/full/3200,/0/default.jpg',
height: 2328,
width: 3200
},{
url: '/test/data/iiif_2_0_sizes/full/6976,/0/default.jpg',
height: 5074,
width: 6976
}]
});
});
})();