mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-21 20:56:09 +03:00
Merge pull request #2310 from pearcetm/rotation-seams
Refactor drawing code, add WebGL drawer, and enable plugin renderers
This commit is contained in:
commit
52fc10ffa2
@ -28,6 +28,7 @@ module.exports = function(grunt) {
|
||||
coverageDir = 'coverage/' + dateFormat(new Date(), 'yyyymmdd-HHMMss'),
|
||||
sources = [
|
||||
"src/openseadragon.js",
|
||||
"src/matrix3.js",
|
||||
"src/fullscreen.js",
|
||||
"src/eventsource.js",
|
||||
"src/mousetracker.js",
|
||||
@ -57,11 +58,14 @@ module.exports = function(grunt) {
|
||||
"src/imageloader.js",
|
||||
"src/tile.js",
|
||||
"src/overlay.js",
|
||||
"src/drawer.js",
|
||||
"src/drawerbase.js",
|
||||
"src/htmldrawer.js",
|
||||
"src/canvasdrawer.js",
|
||||
"src/webgldrawer.js",
|
||||
"src/viewport.js",
|
||||
"src/tiledimage.js",
|
||||
"src/tilecache.js",
|
||||
"src/world.js"
|
||||
"src/world.js",
|
||||
];
|
||||
|
||||
var banner = "//! <%= pkg.name %> <%= pkg.version %>\n" +
|
||||
|
@ -1,5 +1,5 @@
|
||||
Copyright (C) 2009 CodePlex Foundation
|
||||
Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Button
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - ButtonGroup
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
1090
src/canvasdrawer.js
Normal file
1090
src/canvasdrawer.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Control
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - ControlDock
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - DisplayRect
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
767
src/drawer.js
767
src/drawer.js
@ -1,767 +0,0 @@
|
||||
/*
|
||||
* OpenSeadragon - Drawer
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* 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
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class Drawer
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {Element} options.element - Parent element.
|
||||
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
$.Drawer = function( options ) {
|
||||
|
||||
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
|
||||
|
||||
//backward compatibility for positional args while preferring more
|
||||
//idiomatic javascript options object as the only argument
|
||||
var args = arguments;
|
||||
|
||||
if( !$.isPlainObject( options ) ){
|
||||
options = {
|
||||
source: args[ 0 ], // Reference to Viewer tile source.
|
||||
viewport: args[ 1 ], // Reference to Viewer viewport.
|
||||
element: args[ 2 ] // Parent element.
|
||||
};
|
||||
}
|
||||
|
||||
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
|
||||
$.console.assert( options.element, "[Drawer] options.element is required" );
|
||||
|
||||
if ( options.source ) {
|
||||
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
|
||||
}
|
||||
|
||||
this.viewer = options.viewer;
|
||||
this.viewport = options.viewport;
|
||||
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
||||
if (options.opacity) {
|
||||
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
|
||||
}
|
||||
|
||||
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
|
||||
/**
|
||||
* The parent element of this Drawer instance, passed in when the Drawer was created.
|
||||
* The parent of {@link OpenSeadragon.Drawer#canvas}.
|
||||
* @member {Element} container
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.container = $.getElement( options.element );
|
||||
/**
|
||||
* A <canvas> element if the browser supports them, otherwise a <div> element.
|
||||
* Child element of {@link OpenSeadragon.Drawer#container}.
|
||||
* @member {Element} canvas
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
|
||||
/**
|
||||
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
|
||||
* @member {Object} context
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
|
||||
|
||||
/**
|
||||
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
||||
* to the main canvas due to opacity. Lazily initialized.
|
||||
*/
|
||||
this.sketchCanvas = null;
|
||||
this.sketchContext = null;
|
||||
|
||||
/**
|
||||
* @member {Element} element
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
* @deprecated Alias for {@link OpenSeadragon.Drawer#container}.
|
||||
*/
|
||||
this.element = this.container;
|
||||
|
||||
// We force our container to ltr because our drawing math doesn't work in rtl.
|
||||
// This issue only affects our canvas renderer, but we do it always for consistency.
|
||||
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
|
||||
this.container.dir = 'ltr';
|
||||
|
||||
// check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density
|
||||
if (this.useCanvas) {
|
||||
var viewportSize = this._calculateCanvasSize();
|
||||
this.canvas.width = viewportSize.x;
|
||||
this.canvas.height = viewportSize.y;
|
||||
}
|
||||
|
||||
this.canvas.style.width = "100%";
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.position = "absolute";
|
||||
$.setElementOpacity( this.canvas, this.opacity, true );
|
||||
// Allow pointer events to pass through the canvas element so implicit
|
||||
// pointer capture works on touch devices
|
||||
$.setElementPointerEventsNone( this.canvas );
|
||||
$.setElementTouchActionNone( this.canvas );
|
||||
|
||||
// explicit left-align
|
||||
this.container.style.textAlign = "left";
|
||||
this.container.appendChild( this.canvas );
|
||||
|
||||
// Image smoothing for canvas rendering (only if canvas is used).
|
||||
// Canvas default is "true", so this will only be changed if user specified "false".
|
||||
this._imageSmoothingEnabled = true;
|
||||
};
|
||||
|
||||
/** @lends OpenSeadragon.Drawer.prototype */
|
||||
$.Drawer.prototype = {
|
||||
// deprecated
|
||||
addOverlay: function( element, location, placement, onDraw ) {
|
||||
$.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
|
||||
this.viewer.addOverlay( element, location, placement, onDraw );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
updateOverlay: function( element, location, placement ) {
|
||||
$.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
|
||||
this.viewer.updateOverlay( element, location, placement );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
removeOverlay: function( element ) {
|
||||
$.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
|
||||
this.viewer.removeOverlay( element );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
clearOverlays: function() {
|
||||
$.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
|
||||
this.viewer.clearOverlays();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function converts the given point from to the drawer coordinate by
|
||||
* multiplying it with the pixel density.
|
||||
* This function does not take rotation into account, thus assuming provided
|
||||
* point is at 0 degree.
|
||||
* @param {OpenSeadragon.Point} point - the pixel point to convert
|
||||
* @returns {OpenSeadragon.Point} Point in drawer coordinate system.
|
||||
*/
|
||||
viewportCoordToDrawerCoord: function(point) {
|
||||
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
|
||||
return new $.Point(
|
||||
vpPoint.x * $.pixelDensityRatio,
|
||||
vpPoint.y * $.pixelDensityRatio
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* This function will create multiple polygon paths on the drawing context by provided polygons,
|
||||
* then clip the context to the paths.
|
||||
* @param {OpenSeadragon.Point[][]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point
|
||||
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
||||
*/
|
||||
clipWithPolygons: function (polygons, useSketch) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
var context = this._getContext(useSketch);
|
||||
context.beginPath();
|
||||
polygons.forEach(function (polygon) {
|
||||
polygon.forEach(function (coord, i) {
|
||||
context[i === 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y);
|
||||
});
|
||||
});
|
||||
context.clip();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the opacity of the drawer.
|
||||
* @param {Number} opacity
|
||||
* @returns {OpenSeadragon.Drawer} Chainable.
|
||||
*/
|
||||
setOpacity: function( opacity ) {
|
||||
$.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
|
||||
var world = this.viewer.world;
|
||||
for (var i = 0; i < world.getItemCount(); i++) {
|
||||
world.getItemAt( i ).setOpacity( opacity );
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the opacity of the drawer.
|
||||
* @returns {Number}
|
||||
*/
|
||||
getOpacity: function() {
|
||||
$.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead.");
|
||||
var world = this.viewer.world;
|
||||
var maxOpacity = 0;
|
||||
for (var i = 0; i < world.getItemCount(); i++) {
|
||||
var opacity = world.getItemAt( i ).getOpacity();
|
||||
if ( opacity > maxOpacity ) {
|
||||
maxOpacity = opacity;
|
||||
}
|
||||
}
|
||||
return maxOpacity;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
needsUpdate: function() {
|
||||
$.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
|
||||
return this.viewer.world.needsDraw();
|
||||
},
|
||||
|
||||
// deprecated
|
||||
numTilesLoaded: function() {
|
||||
$.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
|
||||
return this.viewer.tileCache.numTilesLoaded();
|
||||
},
|
||||
|
||||
// deprecated
|
||||
reset: function() {
|
||||
$.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
|
||||
this.viewer.world.resetItems();
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
update: function() {
|
||||
$.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
|
||||
this.clear();
|
||||
this.viewer.world.draw();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Boolean} True if rotation is supported.
|
||||
*/
|
||||
canRotate: function() {
|
||||
return this.useCanvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the drawer (unload current loaded tiles)
|
||||
*/
|
||||
destroy: function() {
|
||||
//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
|
||||
this.canvas.width = 1;
|
||||
this.canvas.height = 1;
|
||||
this.sketchCanvas = null;
|
||||
this.sketchContext = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the Drawer so it's ready to draw another frame.
|
||||
*/
|
||||
clear: function() {
|
||||
this.canvas.innerHTML = "";
|
||||
if ( this.useCanvas ) {
|
||||
var viewportSize = this._calculateCanvasSize();
|
||||
if( this.canvas.width !== viewportSize.x ||
|
||||
this.canvas.height !== viewportSize.y ) {
|
||||
this.canvas.width = viewportSize.x;
|
||||
this.canvas.height = viewportSize.y;
|
||||
this._updateImageSmoothingEnabled(this.context);
|
||||
if ( this.sketchCanvas !== null ) {
|
||||
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
||||
this.sketchCanvas.width = sketchCanvasSize.x;
|
||||
this.sketchCanvas.height = sketchCanvasSize.y;
|
||||
this._updateImageSmoothingEnabled(this.sketchContext);
|
||||
}
|
||||
}
|
||||
this._clear();
|
||||
}
|
||||
},
|
||||
|
||||
_clear: function (useSketch, bounds) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
var context = this._getContext(useSketch);
|
||||
if (bounds) {
|
||||
context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
} else {
|
||||
var canvas = context.canvas;
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale from OpenSeadragon viewer rectangle to drawer rectangle
|
||||
* (ignoring rotation)
|
||||
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
|
||||
* @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
|
||||
*/
|
||||
viewportToDrawerRectangle: function(rectangle) {
|
||||
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
|
||||
var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
|
||||
|
||||
return new $.Rect(
|
||||
topLeft.x * $.pixelDensityRatio,
|
||||
topLeft.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the given tile.
|
||||
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
||||
* where <code>rendered</code> 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.
|
||||
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
|
||||
*/
|
||||
drawTile: function( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
|
||||
$.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, shouldRoundPositionAndSize, source);
|
||||
} else {
|
||||
tile.drawHTML( this.canvas );
|
||||
}
|
||||
},
|
||||
|
||||
_getContext: function( useSketch ) {
|
||||
var context = this.context;
|
||||
if ( useSketch ) {
|
||||
if (this.sketchCanvas === null) {
|
||||
this.sketchCanvas = document.createElement( "canvas" );
|
||||
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
||||
this.sketchCanvas.width = sketchCanvasSize.x;
|
||||
this.sketchCanvas.height = sketchCanvasSize.y;
|
||||
this.sketchContext = this.sketchCanvas.getContext( "2d" );
|
||||
|
||||
// If the viewport is not currently rotated, the sketchCanvas
|
||||
// will have the same size as the main canvas. However, if
|
||||
// the viewport get rotated later on, we will need to resize it.
|
||||
if (this.viewport.getRotation() === 0) {
|
||||
var self = this;
|
||||
this.viewer.addHandler('rotate', function resizeSketchCanvas() {
|
||||
if (self.viewport.getRotation() === 0) {
|
||||
return;
|
||||
}
|
||||
self.viewer.removeHandler('rotate', resizeSketchCanvas);
|
||||
var sketchCanvasSize = self._calculateSketchCanvasSize();
|
||||
self.sketchCanvas.width = sketchCanvasSize.x;
|
||||
self.sketchCanvas.height = sketchCanvasSize.y;
|
||||
});
|
||||
}
|
||||
this._updateImageSmoothingEnabled(this.sketchContext);
|
||||
}
|
||||
context = this.sketchContext;
|
||||
}
|
||||
return context;
|
||||
},
|
||||
|
||||
// private
|
||||
saveContext: function( useSketch ) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getContext( useSketch ).save();
|
||||
},
|
||||
|
||||
// private
|
||||
restoreContext: function( useSketch ) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getContext( useSketch ).restore();
|
||||
},
|
||||
|
||||
// private
|
||||
setClip: function(rect, useSketch) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this._getContext( useSketch );
|
||||
context.beginPath();
|
||||
context.rect(rect.x, rect.y, rect.width, rect.height);
|
||||
context.clip();
|
||||
},
|
||||
|
||||
// private
|
||||
drawRectangle: function(rect, fillStyle, useSketch) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this._getContext( useSketch );
|
||||
context.save();
|
||||
context.fillStyle = fillStyle;
|
||||
context.fillRect(rect.x, rect.y, rect.width, rect.height);
|
||||
context.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Blends the sketch canvas in the main canvas.
|
||||
* @param {Object} options The options
|
||||
* @param {Float} options.opacity The opacity of the blending.
|
||||
* @param {Float} [options.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} [options.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.
|
||||
* @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
|
||||
* canvas to blend in the main canvas. If specified, options.scale and
|
||||
* options.translate get ignored.
|
||||
*/
|
||||
blendSketch: function(opacity, scale, translate, compositeOperation) {
|
||||
var options = opacity;
|
||||
if (!$.isPlainObject(options)) {
|
||||
options = {
|
||||
opacity: opacity,
|
||||
scale: scale,
|
||||
translate: translate,
|
||||
compositeOperation: compositeOperation
|
||||
};
|
||||
}
|
||||
if (!this.useCanvas || !this.sketchCanvas) {
|
||||
return;
|
||||
}
|
||||
opacity = options.opacity;
|
||||
compositeOperation = options.compositeOperation;
|
||||
var bounds = options.bounds;
|
||||
|
||||
this.context.save();
|
||||
this.context.globalAlpha = opacity;
|
||||
if (compositeOperation) {
|
||||
this.context.globalCompositeOperation = compositeOperation;
|
||||
}
|
||||
if (bounds) {
|
||||
// Internet Explorer, Microsoft Edge, and Safari have problems
|
||||
// when you call context.drawImage with negative x or y
|
||||
// or x + width or y + height greater than the canvas width or height respectively.
|
||||
if (bounds.x < 0) {
|
||||
bounds.width += bounds.x;
|
||||
bounds.x = 0;
|
||||
}
|
||||
if (bounds.x + bounds.width > this.canvas.width) {
|
||||
bounds.width = this.canvas.width - bounds.x;
|
||||
}
|
||||
if (bounds.y < 0) {
|
||||
bounds.height += bounds.y;
|
||||
bounds.y = 0;
|
||||
}
|
||||
if (bounds.y + bounds.height > this.canvas.height) {
|
||||
bounds.height = this.canvas.height - bounds.y;
|
||||
}
|
||||
|
||||
this.context.drawImage(
|
||||
this.sketchCanvas,
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height
|
||||
);
|
||||
} else {
|
||||
scale = options.scale || 1;
|
||||
translate = options.translate;
|
||||
var position = translate instanceof $.Point ?
|
||||
translate : new $.Point(0, 0);
|
||||
|
||||
var widthExt = 0;
|
||||
var heightExt = 0;
|
||||
if (translate) {
|
||||
var widthDiff = this.sketchCanvas.width - this.canvas.width;
|
||||
var heightDiff = this.sketchCanvas.height - this.canvas.height;
|
||||
widthExt = Math.round(widthDiff / 2);
|
||||
heightExt = Math.round(heightDiff / 2);
|
||||
}
|
||||
this.context.drawImage(
|
||||
this.sketchCanvas,
|
||||
position.x - widthExt * scale,
|
||||
position.y - heightExt * scale,
|
||||
(this.canvas.width + 2 * widthExt) * scale,
|
||||
(this.canvas.height + 2 * heightExt) * scale,
|
||||
-widthExt,
|
||||
-heightExt,
|
||||
this.canvas.width + 2 * widthExt,
|
||||
this.canvas.height + 2 * heightExt
|
||||
);
|
||||
}
|
||||
this.context.restore();
|
||||
},
|
||||
|
||||
// private
|
||||
drawDebugInfo: function(tile, count, i, tiledImage) {
|
||||
if ( !this.useCanvas ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
|
||||
var context = this.context;
|
||||
context.save();
|
||||
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||
context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
|
||||
context.strokeStyle = this.debugGridColor[colorIndex];
|
||||
context.fillStyle = this.debugGridColor[colorIndex];
|
||||
|
||||
if (this.viewport.getRotation(true) % 360 !== 0 ) {
|
||||
this._offsetForRotation({degrees: this.viewport.getRotation(true)});
|
||||
}
|
||||
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||
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(
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
tile.position.y * $.pixelDensityRatio,
|
||||
tile.size.x * $.pixelDensityRatio,
|
||||
tile.size.y * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio;
|
||||
var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio;
|
||||
|
||||
// Rotate the text the right way around.
|
||||
context.translate( tileCenterX, tileCenterY );
|
||||
context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) );
|
||||
context.translate( -tileCenterX, -tileCenterY );
|
||||
|
||||
if( tile.x === 0 && tile.y === 0 ){
|
||||
context.fillText(
|
||||
"Zoom: " + this.viewport.getZoom(),
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
(tile.position.y - 30) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Pan: " + this.viewport.getBounds().toString(),
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
(tile.position.y - 20) * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
context.fillText(
|
||||
"Level: " + tile.level,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 20) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Column: " + tile.x,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 30) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Row: " + tile.y,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 40) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Order: " + i + " of " + count,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 50) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Size: " + tile.size.toString(),
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 60) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Position: " + tile.position.toString(),
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 70) * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
if (this.viewport.getRotation(true) % 360 !== 0 ) {
|
||||
this._restoreRotationChanges();
|
||||
}
|
||||
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||
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();
|
||||
},
|
||||
|
||||
// private
|
||||
debugRect: function(rect) {
|
||||
if ( this.useCanvas ) {
|
||||
var context = this.context;
|
||||
context.save();
|
||||
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||
context.strokeStyle = this.debugGridColor[0];
|
||||
context.fillStyle = this.debugGridColor[0];
|
||||
|
||||
context.strokeRect(
|
||||
rect.x * $.pixelDensityRatio,
|
||||
rect.y * $.pixelDensityRatio,
|
||||
rect.width * $.pixelDensityRatio,
|
||||
rect.height * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
|
||||
*
|
||||
* @function
|
||||
* @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
|
||||
* drawn smoothly on the canvas; see imageSmoothingEnabled in
|
||||
* {@link OpenSeadragon.Options} for more explanation.
|
||||
*/
|
||||
setImageSmoothingEnabled: function(imageSmoothingEnabled){
|
||||
if ( this.useCanvas ) {
|
||||
this._imageSmoothingEnabled = imageSmoothingEnabled;
|
||||
this._updateImageSmoothingEnabled(this.context);
|
||||
this.viewer.forceRedraw();
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
_updateImageSmoothingEnabled: function(context){
|
||||
context.msImageSmoothingEnabled = this._imageSmoothingEnabled;
|
||||
context.imageSmoothingEnabled = this._imageSmoothingEnabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the canvas size
|
||||
* @param {Boolean} sketch If set to true return the size of the sketch canvas
|
||||
* @returns {OpenSeadragon.Point} The size of the canvas
|
||||
*/
|
||||
getCanvasSize: function(sketch) {
|
||||
var canvas = this._getContext(sketch).canvas;
|
||||
return new $.Point(canvas.width, canvas.height);
|
||||
},
|
||||
|
||||
getCanvasCenter: function() {
|
||||
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
|
||||
},
|
||||
|
||||
// private
|
||||
_offsetForRotation: function(options) {
|
||||
var point = options.point ?
|
||||
options.point.times($.pixelDensityRatio) :
|
||||
this.getCanvasCenter();
|
||||
|
||||
var context = this._getContext(options.useSketch);
|
||||
context.save();
|
||||
|
||||
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.translate(-point.x, -point.y);
|
||||
},
|
||||
|
||||
// private
|
||||
_flip: function(options) {
|
||||
options = options || {};
|
||||
var point = options.point ?
|
||||
options.point.times($.pixelDensityRatio) :
|
||||
this.getCanvasCenter();
|
||||
var context = this._getContext(options.useSketch);
|
||||
|
||||
context.translate(point.x, 0);
|
||||
context.scale(-1, 1);
|
||||
context.translate(-point.x, 0);
|
||||
},
|
||||
|
||||
// private
|
||||
_restoreRotationChanges: function(useSketch) {
|
||||
var context = this._getContext(useSketch);
|
||||
context.restore();
|
||||
},
|
||||
|
||||
// private
|
||||
_calculateCanvasSize: function() {
|
||||
var pixelDensityRatio = $.pixelDensityRatio;
|
||||
var viewportSize = this.viewport.getContainerSize();
|
||||
return {
|
||||
// canvas width and height are integers
|
||||
x: Math.round(viewportSize.x * pixelDensityRatio),
|
||||
y: Math.round(viewportSize.y * pixelDensityRatio)
|
||||
};
|
||||
},
|
||||
|
||||
// private
|
||||
_calculateSketchCanvasSize: function() {
|
||||
var canvasSize = this._calculateCanvasSize();
|
||||
if (this.viewport.getRotation() === 0) {
|
||||
return canvasSize;
|
||||
}
|
||||
// If the viewport is rotated, we need a larger sketch canvas in order
|
||||
// to support edge smoothing.
|
||||
var sketchCanvasSize = Math.ceil(Math.sqrt(
|
||||
canvasSize.x * canvasSize.x +
|
||||
canvasSize.y * canvasSize.y));
|
||||
return {
|
||||
x: sketchCanvasSize,
|
||||
y: sketchCanvasSize
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
285
src/drawerbase.js
Normal file
285
src/drawerbase.js
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* OpenSeadragon - DrawerBase
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class DrawerBase
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Base class for Drawers that handle rendering of tiles for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {Element} options.element - Parent element.
|
||||
*/
|
||||
|
||||
$.DrawerBase = class DrawerBase{
|
||||
constructor(options){
|
||||
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
|
||||
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
|
||||
$.console.assert( options.element, "[Drawer] options.element is required" );
|
||||
|
||||
this.viewer = options.viewer;
|
||||
this.viewport = options.viewport;
|
||||
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
||||
this.options = options.options || {};
|
||||
|
||||
/**
|
||||
* The parent element of this Drawer instance, passed in when the Drawer was created.
|
||||
* The parent of {@link OpenSeadragon.DrawerBase#canvas}.
|
||||
* @member {Element} container
|
||||
* @memberof OpenSeadragon.DrawerBase#
|
||||
*/
|
||||
this.container = $.getElement( options.element );
|
||||
|
||||
this._renderingTarget = this._createDrawingElement();
|
||||
|
||||
|
||||
this.canvas.style.width = "100%";
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.position = "absolute";
|
||||
// set canvas.style.left = 0 so the canvas is positioned properly in ltr and rtl html
|
||||
this.canvas.style.left = "0";
|
||||
$.setElementOpacity( this.canvas, this.viewer.opacity, true );
|
||||
|
||||
// Allow pointer events to pass through the canvas element so implicit
|
||||
// pointer capture works on touch devices
|
||||
$.setElementPointerEventsNone( this.canvas );
|
||||
$.setElementTouchActionNone( this.canvas );
|
||||
|
||||
// explicit left-align
|
||||
this.container.style.textAlign = "left";
|
||||
this.container.appendChild( this.canvas );
|
||||
|
||||
this._checkForAPIOverrides();
|
||||
}
|
||||
|
||||
// protect the canvas member with a getter
|
||||
get canvas(){
|
||||
return this._renderingTarget;
|
||||
}
|
||||
get element(){
|
||||
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
|
||||
return this.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {String|undefined} What type of drawer this is.
|
||||
*/
|
||||
getType(){
|
||||
$.console.error('Drawer.getType must be implemented by child class');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} whether the drawer implementation is supported by the browser
|
||||
*/
|
||||
static isSupported() {
|
||||
$.console.error('Drawer.isSupported must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
||||
* @returns {Element} the element to draw into
|
||||
*/
|
||||
_createDrawingElement() {
|
||||
$.console.error('Drawer._createDrawingElement must be implemented by child class');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} tiledImages - An array of TiledImages that are ready to be drawn
|
||||
*/
|
||||
draw(tiledImages) {
|
||||
$.console.error('Drawer.draw must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} True if rotation is supported.
|
||||
*/
|
||||
canRotate() {
|
||||
$.console.error('Drawer.canRotate must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the drawer (unload current loaded tiles)
|
||||
*/
|
||||
destroy() {
|
||||
$.console.error('Drawer.destroy must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
||||
*/
|
||||
minimumOverlapRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
|
||||
*
|
||||
* @function
|
||||
* @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
|
||||
* drawn smoothly on the canvas; see imageSmoothingEnabled in
|
||||
* {@link OpenSeadragon.Options} for more explanation.
|
||||
*/
|
||||
setImageSmoothingEnabled(imageSmoothingEnabled){
|
||||
$.console.error('Drawer.setImageSmoothingEnabled must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional public API to draw a rectangle (e.g. for debugging purposes)
|
||||
* Child classes can override this method if they wish to support this
|
||||
* @param {OpenSeadragon.Rect} rect
|
||||
*/
|
||||
drawDebuggingRect(rect) {
|
||||
$.console.warn('[drawer].drawDebuggingRect is not implemented by this drawer');
|
||||
}
|
||||
|
||||
// Deprecated functions
|
||||
clear(){
|
||||
$.console.warn('[drawer].clear() is deprecated. The drawer is responsible for clearing itself as needed before drawing tiles.');
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
/**
|
||||
* Ensures that child classes have provided implementations for public API methods
|
||||
* draw, canRotate, destroy, and setImageSmoothinEnabled. Throws an exception if the original
|
||||
* placeholder methods are still in place.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_checkForAPIOverrides(){
|
||||
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
||||
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
||||
}
|
||||
if(this.draw === $.DrawerBase.prototype.draw){
|
||||
throw(new Error("[drawer].draw must be implemented by child class"));
|
||||
}
|
||||
if(this.canRotate === $.DrawerBase.prototype.canRotate){
|
||||
throw(new Error("[drawer].canRotate must be implemented by child class"));
|
||||
}
|
||||
if(this.destroy === $.DrawerBase.prototype.destroy){
|
||||
throw(new Error("[drawer].destroy must be implemented by child class"));
|
||||
}
|
||||
if(this.setImageSmoothingEnabled === $.DrawerBase.prototype.setImageSmoothingEnabled){
|
||||
throw(new Error("[drawer].setImageSmoothingEnabled must be implemented by child class"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Utility functions
|
||||
|
||||
/**
|
||||
* Scale from OpenSeadragon viewer rectangle to drawer rectangle
|
||||
* (ignoring rotation)
|
||||
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
|
||||
* @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
|
||||
*/
|
||||
viewportToDrawerRectangle(rectangle) {
|
||||
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
|
||||
var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
|
||||
|
||||
return new $.Rect(
|
||||
topLeft.x * $.pixelDensityRatio,
|
||||
topLeft.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function converts the given point from to the drawer coordinate by
|
||||
* multiplying it with the pixel density.
|
||||
* This function does not take rotation into account, thus assuming provided
|
||||
* point is at 0 degree.
|
||||
* @param {OpenSeadragon.Point} point - the pixel point to convert
|
||||
* @returns {OpenSeadragon.Point} Point in drawer coordinate system.
|
||||
*/
|
||||
viewportCoordToDrawerCoord(point) {
|
||||
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
|
||||
return new $.Point(
|
||||
vpPoint.x * $.pixelDensityRatio,
|
||||
vpPoint.y * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Internal utility functions
|
||||
|
||||
/**
|
||||
* Calculate width and height of the canvas based on viewport dimensions
|
||||
* and pixelDensityRatio
|
||||
* @private
|
||||
* @returns {OpenSeadragon.Point} {x, y} size of the canvas
|
||||
*/
|
||||
_calculateCanvasSize() {
|
||||
var pixelDensityRatio = $.pixelDensityRatio;
|
||||
var viewportSize = this.viewport.getContainerSize();
|
||||
return new OpenSeadragon.Point( Math.round(viewportSize.x * pixelDensityRatio), Math.round(viewportSize.y * pixelDensityRatio));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by implementations to fire the tiled-image-drawn event (used by tests)
|
||||
* @private
|
||||
*/
|
||||
_raiseTiledImageDrawnEvent(tiledImage, tiles){
|
||||
if(!this.viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raised when a tiled image is drawn to the canvas. Used internally for testing.
|
||||
* The update-viewport event is preferred if you want to know when a frame has been drawn.
|
||||
*
|
||||
* @event tiled-image-drawn
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
|
||||
* @property {Array} tiles - An array of Tile objects that were drawn.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
* @private
|
||||
*/
|
||||
this.viewer.raiseEvent( 'tiled-image-drawn', {
|
||||
tiledImage: tiledImage,
|
||||
tiles: tiles,
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - DziTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - EventSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -51,6 +51,7 @@
|
||||
*/
|
||||
$.EventSource = function() {
|
||||
this.events = {};
|
||||
this._rejectedEventList = {};
|
||||
};
|
||||
|
||||
/** @lends OpenSeadragon.EventSource.prototype */
|
||||
@ -68,6 +69,7 @@ $.EventSource.prototype = {
|
||||
* @param {Number} [times=1] - The number of times to handle the event
|
||||
* before removing it.
|
||||
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
|
||||
* @returns {Boolean} - True if the handler was added, false if it was rejected
|
||||
*/
|
||||
addOnceHandler: function(eventName, handler, userData, times, priority) {
|
||||
var self = this;
|
||||
@ -80,7 +82,7 @@ $.EventSource.prototype = {
|
||||
}
|
||||
return handler(event);
|
||||
};
|
||||
this.addHandler(eventName, onceHandler, userData, priority);
|
||||
return this.addHandler(eventName, onceHandler, userData, priority);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -90,8 +92,15 @@ $.EventSource.prototype = {
|
||||
* @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
|
||||
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler.
|
||||
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
|
||||
* @returns {Boolean} - True if the handler was added, false if it was rejected
|
||||
*/
|
||||
addHandler: function ( eventName, handler, userData, priority ) {
|
||||
|
||||
if(Object.prototype.hasOwnProperty.call(this._rejectedEventList, eventName)){
|
||||
$.console.error(`Error adding handler for ${eventName}. ${this._rejectedEventList[eventName]}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
var events = this.events[ eventName ];
|
||||
if ( !events ) {
|
||||
this.events[ eventName ] = events = [];
|
||||
@ -106,6 +115,7 @@ $.EventSource.prototype = {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -191,17 +201,45 @@ $.EventSource.prototype = {
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {Object} eventArgs - Event-specific data.
|
||||
* @returns {Boolean} True if the event was fired, false if it was rejected because of rejectEventHandler(eventName)
|
||||
*/
|
||||
raiseEvent: function( eventName, eventArgs ) {
|
||||
//uncomment if you want to get a log of all events
|
||||
//$.console.log( eventName );
|
||||
|
||||
if(Object.prototype.hasOwnProperty.call(this._rejectedEventList, eventName)){
|
||||
$.console.error(`Error adding handler for ${eventName}. ${this._rejectedEventList[eventName]}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
var handler = this.getHandler( eventName );
|
||||
if ( handler ) {
|
||||
return handler( this, eventArgs || {} );
|
||||
handler( this, eventArgs || {} );
|
||||
}
|
||||
return undefined;
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set an event name as being disabled, and provide an optional error message
|
||||
* to be printed to the console
|
||||
* @param {String} eventName - Name of the event
|
||||
* @param {String} [errorMessage] - Optional string to print to the console
|
||||
* @private
|
||||
*/
|
||||
rejectEventHandler(eventName, errorMessage = ''){
|
||||
this._rejectedEventList[eventName] = errorMessage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Explicitly allow an event handler to be added for this event type, undoing
|
||||
* the effects of rejectEventHandler
|
||||
* @param {String} eventName - Name of the event
|
||||
* @private
|
||||
*/
|
||||
allowEventHandler(eventName){
|
||||
delete this._rejectedEventList[eventName];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - full-screen support functions
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
242
src/htmldrawer.js
Normal file
242
src/htmldrawer.js
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* OpenSeadragon - HTMLDrawer
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class HTMLDrawer
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc HTML-based implementation of DrawerBase for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {Element} options.element - Parent element.
|
||||
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
|
||||
class HTMLDrawer extends $.DrawerBase{
|
||||
constructor(options){
|
||||
super(options);
|
||||
|
||||
// Reject listening for the tile-drawing event, which this drawer does not fire
|
||||
this.viewer.rejectEventHandler("tile-drawing", "The HTMLDrawer does not raise the tile-drawing event");
|
||||
// Since the tile-drawn event is fired by this drawer, make sure handlers can be added for it
|
||||
this.viewer.allowEventHandler("tile-drawn");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} always true
|
||||
*/
|
||||
static isSupported(){
|
||||
return true;
|
||||
}
|
||||
|
||||
getType(){
|
||||
return 'html';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
||||
*/
|
||||
minimumOverlapRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
||||
* @returns {Element} the div to draw into
|
||||
*/
|
||||
_createDrawingElement(){
|
||||
let canvas = $.makeNeutralElement("div");
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the TiledImages
|
||||
*/
|
||||
draw(tiledImages) {
|
||||
var _this = this;
|
||||
this._prepareNewFrame(); // prepare to draw a new frame
|
||||
tiledImages.forEach(function(tiledImage){
|
||||
if (tiledImage.opacity !== 0 || tiledImage._preload) {
|
||||
_this._drawTiles(tiledImage);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} False - rotation is not supported.
|
||||
*/
|
||||
canRotate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the drawer (unload current loaded tiles)
|
||||
*/
|
||||
destroy() {
|
||||
this.canvas.innerHTML = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns image smoothing on or off for this viewer. Note: Ignored by HTML Drawer
|
||||
*
|
||||
* @function
|
||||
* @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
|
||||
* drawn smoothly on the canvas; see imageSmoothingEnabled in
|
||||
* {@link OpenSeadragon.Options} for more explanation.
|
||||
*/
|
||||
setImageSmoothingEnabled(){
|
||||
// noop - HTML Drawer does not deal with this property
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the Drawer so it's ready to draw another frame.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_prepareNewFrame() {
|
||||
this.canvas.innerHTML = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a TiledImage.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_drawTiles( tiledImage ) {
|
||||
var lastDrawn = tiledImage.getTilesToDraw().map(info => info.tile);
|
||||
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over the tiles to draw, and draw them
|
||||
for (var i = lastDrawn.length - 1; i >= 0; i--) {
|
||||
var tile = lastDrawn[ i ];
|
||||
this._drawTile( tile );
|
||||
|
||||
if( this.viewer ){
|
||||
/**
|
||||
* Raised when a tile is drawn to the canvas. Only valid for
|
||||
* context2d and html drawers.
|
||||
*
|
||||
* @event tile-drawn
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
|
||||
* @property {OpenSeadragon.Tile} tile
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.viewer.raiseEvent( 'tile-drawn', {
|
||||
tiledImage: tiledImage,
|
||||
tile: tile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the given tile.
|
||||
* @private
|
||||
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
*/
|
||||
_drawTile( tile ) {
|
||||
$.console.assert(tile, '[Drawer._drawTile] tile is required');
|
||||
|
||||
let container = this.canvas;
|
||||
|
||||
if (!tile.cacheImageRecord) {
|
||||
$.console.warn(
|
||||
'[Drawer._drawTileToHTML] attempting to draw tile %s when it\'s not cached',
|
||||
tile.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !tile.loaded ) {
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
tile.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//EXPERIMENTAL - trying to figure out how to scale the container
|
||||
// content during animation of the container size.
|
||||
|
||||
if ( !tile.element ) {
|
||||
var image = tile.getImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
tile.element = $.makeNeutralElement( "div" );
|
||||
tile.imgElement = image.cloneNode();
|
||||
tile.imgElement.style.msInterpolationMode = "nearest-neighbor";
|
||||
tile.imgElement.style.width = "100%";
|
||||
tile.imgElement.style.height = "100%";
|
||||
|
||||
tile.style = tile.element.style;
|
||||
tile.style.position = "absolute";
|
||||
}
|
||||
|
||||
if ( tile.element.parentNode !== container ) {
|
||||
container.appendChild( tile.element );
|
||||
}
|
||||
if ( tile.imgElement.parentNode !== tile.element ) {
|
||||
tile.element.appendChild( tile.imgElement );
|
||||
}
|
||||
|
||||
tile.style.top = tile.position.y + "px";
|
||||
tile.style.left = tile.position.x + "px";
|
||||
tile.style.height = tile.size.y + "px";
|
||||
tile.style.width = tile.size.x + "px";
|
||||
|
||||
if (tile.flipped) {
|
||||
tile.style.transform = "scaleX(-1)";
|
||||
}
|
||||
|
||||
$.setElementOpacity( tile.element, tile.opacity );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$.HTMLDrawer = HTMLDrawer;
|
||||
|
||||
|
||||
}( OpenSeadragon ));
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - IIIFTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - ImageLoader
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -37,6 +37,8 @@
|
||||
/**
|
||||
* @class ImageJob
|
||||
* @classdesc Handles downloading of a single image.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @param {Object} options - Options for this ImageJob.
|
||||
* @param {String} [options.src] - URL of image to download.
|
||||
* @param {Tile} [options.tile] - Tile that belongs the data to.
|
||||
@ -87,6 +89,7 @@ $.ImageJob.prototype = {
|
||||
/**
|
||||
* Starts the image job.
|
||||
* @method
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
start: function() {
|
||||
this.tries++;
|
||||
@ -113,6 +116,7 @@ $.ImageJob.prototype = {
|
||||
* @param {*} data data that has been downloaded
|
||||
* @param {XMLHttpRequest} request reference to the request if used
|
||||
* @param {string} errorMessage description upon failure
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
finish: function(data, request, errorMessage ) {
|
||||
this.data = data;
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - ImageTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -42,8 +42,8 @@
|
||||
* 1. viewer.open({type: 'image', url: fooUrl});
|
||||
* 2. viewer.open(new OpenSeadragon.ImageTileSource({url: fooUrl}));
|
||||
*
|
||||
* With the first syntax, the crossOriginPolicy, ajaxWithCredentials and
|
||||
* useCanvas options are inherited from the viewer if they are not
|
||||
* With the first syntax, the crossOriginPolicy and ajaxWithCredentials
|
||||
* options are inherited from the viewer if they are not
|
||||
* specified directly in the options object.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
@ -58,16 +58,13 @@
|
||||
* domains.
|
||||
* @param {String|Boolean} [options.ajaxWithCredentials=false] Whether to set
|
||||
* the withCredentials XHR flag for AJAX requests (when loading tile sources).
|
||||
* @param {Boolean} [options.useCanvas=true] Set to false to prevent any use
|
||||
* of the canvas API.
|
||||
*/
|
||||
$.ImageTileSource = function (options) {
|
||||
|
||||
options = $.extend({
|
||||
buildPyramid: true,
|
||||
crossOriginPolicy: false,
|
||||
ajaxWithCredentials: false,
|
||||
useCanvas: true
|
||||
ajaxWithCredentials: false
|
||||
}, options);
|
||||
$.TileSource.apply(this, [options]);
|
||||
|
||||
@ -198,9 +195,11 @@
|
||||
/**
|
||||
* Destroys ImageTileSource
|
||||
* @function
|
||||
* @param {OpenSeadragon.Viewer} viewer the viewer that is calling
|
||||
* destroy on the ImageTileSource
|
||||
*/
|
||||
destroy: function () {
|
||||
this._freeupCanvasMemory();
|
||||
destroy: function (viewer) {
|
||||
this._freeupCanvasMemory(viewer);
|
||||
},
|
||||
|
||||
// private
|
||||
@ -214,7 +213,7 @@
|
||||
height: this._image.naturalHeight
|
||||
}];
|
||||
|
||||
if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
|
||||
if (!this.buildPyramid || !$.supportsCanvas) {
|
||||
// We don't need the image anymore. Allows it to be GC.
|
||||
delete this._image;
|
||||
return levels;
|
||||
@ -270,11 +269,26 @@
|
||||
* and Safari keeps canvas until its height and width will be set to 0).
|
||||
* @function
|
||||
*/
|
||||
_freeupCanvasMemory: function () {
|
||||
_freeupCanvasMemory: function (viewer) {
|
||||
for (var i = 0; i < this.levels.length; i++) {
|
||||
if(this.levels[i].context2D){
|
||||
this.levels[i].context2D.canvas.height = 0;
|
||||
this.levels[i].context2D.canvas.width = 0;
|
||||
|
||||
if(viewer){
|
||||
/**
|
||||
* Triggered when an image has just been unloaded
|
||||
*
|
||||
* @event image-unloaded
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {CanvasRenderingContext2D} context2D - The context that is being unloaded
|
||||
*/
|
||||
viewer.raiseEvent("image-unloaded", {
|
||||
context2D: this.levels[i].context2D
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - LegacyTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
209
src/matrix3.js
Normal file
209
src/matrix3.js
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* OpenSeadragon - Mat3
|
||||
*
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Portions of this source file are taken from WegGL Fundamentals:
|
||||
*
|
||||
* Copyright 2012, Gregg Tavares.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Gregg Tavares. nor the names of his
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
(function( $ ){
|
||||
|
||||
// Modified from https://webglfundamentals.org/webgl/lessons/webgl-2d-matrices.html
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @class Mat3
|
||||
* @classdesc A left-to-right matrix representation, useful for affine transforms for
|
||||
* positioning tiles for drawing
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
*
|
||||
* @param {Array} [values] - Initial values for the matrix
|
||||
*
|
||||
**/
|
||||
class Mat3{
|
||||
constructor(values){
|
||||
if(!values) {
|
||||
values = [
|
||||
0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0
|
||||
];
|
||||
}
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeIdentity
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @returns {OpenSeadragon.Mat3} an identity matrix
|
||||
*/
|
||||
static makeIdentity(){
|
||||
return new Mat3([
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeTranslation
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} tx The x value of the translation
|
||||
* @param {Number} ty The y value of the translation
|
||||
* @returns {OpenSeadragon.Mat3} A translation matrix
|
||||
*/
|
||||
static makeTranslation(tx, ty) {
|
||||
return new Mat3([
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
tx, ty, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeRotation
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} angleInRadians The desired rotation angle, in radians
|
||||
* @returns {OpenSeadragon.Mat3} A rotation matrix
|
||||
*/
|
||||
static makeRotation(angleInRadians) {
|
||||
var c = Math.cos(angleInRadians);
|
||||
var s = Math.sin(angleInRadians);
|
||||
return new Mat3([
|
||||
c, -s, 0,
|
||||
s, c, 0,
|
||||
0, 0, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeScaling
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} sx The x value of the scaling
|
||||
* @param {Number} sy The y value of the scaling
|
||||
* @returns {OpenSeadragon.Mat3} A scaling matrix
|
||||
*/
|
||||
static makeScaling(sx, sy) {
|
||||
return new Mat3([
|
||||
sx, 0, 0,
|
||||
0, sy, 0,
|
||||
0, 0, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @alias multiply
|
||||
* @memberof! OpenSeadragon.Mat3
|
||||
* @param {OpenSeadragon.Mat3} other the matrix to multiply with
|
||||
* @returns {OpenSeadragon.Mat3} The result of matrix multiplication
|
||||
*/
|
||||
multiply(other) {
|
||||
let a = this.values;
|
||||
let b = other.values;
|
||||
|
||||
var a00 = a[0 * 3 + 0];
|
||||
var a01 = a[0 * 3 + 1];
|
||||
var a02 = a[0 * 3 + 2];
|
||||
var a10 = a[1 * 3 + 0];
|
||||
var a11 = a[1 * 3 + 1];
|
||||
var a12 = a[1 * 3 + 2];
|
||||
var a20 = a[2 * 3 + 0];
|
||||
var a21 = a[2 * 3 + 1];
|
||||
var a22 = a[2 * 3 + 2];
|
||||
var b00 = b[0 * 3 + 0];
|
||||
var b01 = b[0 * 3 + 1];
|
||||
var b02 = b[0 * 3 + 2];
|
||||
var b10 = b[1 * 3 + 0];
|
||||
var b11 = b[1 * 3 + 1];
|
||||
var b12 = b[1 * 3 + 2];
|
||||
var b20 = b[2 * 3 + 0];
|
||||
var b21 = b[2 * 3 + 1];
|
||||
var b22 = b[2 * 3 + 2];
|
||||
return new Mat3([
|
||||
b00 * a00 + b01 * a10 + b02 * a20,
|
||||
b00 * a01 + b01 * a11 + b02 * a21,
|
||||
b00 * a02 + b01 * a12 + b02 * a22,
|
||||
b10 * a00 + b11 * a10 + b12 * a20,
|
||||
b10 * a01 + b11 * a11 + b12 * a21,
|
||||
b10 * a02 + b11 * a12 + b12 * a22,
|
||||
b20 * a00 + b21 * a10 + b22 * a20,
|
||||
b20 * a01 + b21 * a11 + b22 * a21,
|
||||
b20 * a02 + b21 * a12 + b22 * a22,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$.Mat3 = Mat3;
|
||||
|
||||
}( OpenSeadragon ));
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - MouseTracker
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -357,7 +357,7 @@
|
||||
getActivePointersListByType: function ( type ) {
|
||||
var delegate = THIS[ this.hash ],
|
||||
i,
|
||||
len = delegate.activePointersLists.length,
|
||||
len = delegate ? delegate.activePointersLists.length : 0,
|
||||
list;
|
||||
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
@ -367,7 +367,9 @@
|
||||
}
|
||||
|
||||
list = new $.MouseTracker.GesturePointList( type );
|
||||
delegate.activePointersLists.push( list );
|
||||
if(delegate){
|
||||
delegate.activePointersLists.push( list );
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Navigator
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -170,9 +170,6 @@ $.Navigator = function( options ){
|
||||
style.border = borderWidth + 'px solid ' + options.displayRegionColor;
|
||||
style.margin = '0px';
|
||||
style.padding = '0px';
|
||||
//TODO: IE doesn't like this property being set
|
||||
//try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}
|
||||
|
||||
style.background = 'transparent';
|
||||
|
||||
// We use square bracket notation on the statement below, because float is a keyword.
|
||||
@ -310,7 +307,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
this.viewport.resize( containerSize, true );
|
||||
this.viewport.goHome(true);
|
||||
this.oldContainerSize = containerSize;
|
||||
this.drawer.clear();
|
||||
this.world.update();
|
||||
this.world.draw();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -190,6 +190,16 @@
|
||||
* Zoom level to use when image is first opened or the home button is clicked.
|
||||
* If 0, adjusts to fit viewer.
|
||||
*
|
||||
* @property {String|DrawerImplementation|Array} [drawer = ['webgl', 'canvas', 'html']]
|
||||
* Which drawer to use. Valid strings are 'webgl', 'canvas', and 'html'. Valid drawer
|
||||
* implementations are constructors of classes that extend OpenSeadragon.DrawerBase.
|
||||
* An array of strings and/or constructors can be used to indicate the priority
|
||||
* of different implementations, which will be tried in order based on browser support.
|
||||
*
|
||||
* @property {Object} drawerOptions
|
||||
* Options to pass to the selected drawer implementation. For details
|
||||
* please see {@link OpenSeadragon.DrawerOptions}.
|
||||
*
|
||||
* @property {Number} [opacity=1]
|
||||
* Default proportional opacity of the tiled images (1=opaque, 0=hidden)
|
||||
* Hidden images do not draw and only load when preloading is allowed.
|
||||
@ -204,9 +214,9 @@
|
||||
* For complete list of modes, please @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation/ globalCompositeOperation}
|
||||
*
|
||||
* @property {Boolean} [imageSmoothingEnabled=true]
|
||||
* Image smoothing for canvas rendering (only if canvas is used). Note: Ignored
|
||||
* Image smoothing for canvas rendering (only if the canvas drawer is used). Note: Ignored
|
||||
* by some (especially older) browsers which do not support this canvas property.
|
||||
* This property can be changed in {@link Viewer.Drawer.setImageSmoothingEnabled}.
|
||||
* This property can be changed in {@link Viewer.DrawerBase.setImageSmoothingEnabled}.
|
||||
*
|
||||
* @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null]
|
||||
* Draws a colored rectangle behind the tile if it is not loaded yet.
|
||||
@ -508,7 +518,7 @@
|
||||
* Milliseconds to wait after each tile retry if tileRetryMax is set.
|
||||
*
|
||||
* @property {Boolean} [useCanvas=true]
|
||||
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
|
||||
* Deprecated. Use the `drawer` option to specify preferred renderer.
|
||||
*
|
||||
* @property {Number} [minPixelRatio=0.5]
|
||||
* The higher the minPixelRatio, the lower the quality of the image that
|
||||
@ -744,6 +754,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} DrawerOptions
|
||||
* @memberof OpenSeadragon
|
||||
* @property {Object} webgl - options if the WebGLDrawer is used. No options are currently supported.
|
||||
* @property {Object} canvas - options if the CanvasDrawer is used. No options are currently supported.
|
||||
* @property {Object} html - options if the HTMLDrawer is used. No options are currently supported.
|
||||
* @property {Object} custom - options if a custom drawer is used. No options are currently supported.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The names for the image resources used for the image navigation buttons.
|
||||
*
|
||||
@ -1339,12 +1359,32 @@ function OpenSeadragon( options ){
|
||||
flipped: false,
|
||||
|
||||
// APPEARANCE
|
||||
opacity: 1,
|
||||
preload: false,
|
||||
compositeOperation: null,
|
||||
imageSmoothingEnabled: true,
|
||||
placeholderFillStyle: null,
|
||||
subPixelRoundingForTransparency: null,
|
||||
opacity: 1, // to be passed into each TiledImage
|
||||
compositeOperation: null, // to be passed into each TiledImage
|
||||
|
||||
// DRAWER SETTINGS
|
||||
drawer: ['webgl', 'canvas', 'html'], // prefer using webgl, then canvas (i.e. context2d), then fallback to html
|
||||
|
||||
drawerOptions: {
|
||||
webgl: {
|
||||
|
||||
},
|
||||
canvas: {
|
||||
|
||||
},
|
||||
html: {
|
||||
|
||||
},
|
||||
custom: {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
// TILED IMAGE SETTINGS
|
||||
preload: false, // to be passed into each TiledImage
|
||||
imageSmoothingEnabled: true, // to be passed into each TiledImage
|
||||
placeholderFillStyle: null, // to be passed into each TiledImage
|
||||
subPixelRoundingForTransparency: null, // to be passed into each TiledImage
|
||||
|
||||
//REFERENCE STRIP SETTINGS
|
||||
showReferenceStrip: false,
|
||||
@ -1367,7 +1407,6 @@ function OpenSeadragon( options ){
|
||||
imageLoaderLimit: 0,
|
||||
maxImageCacheCount: 200,
|
||||
timeout: 30000,
|
||||
useCanvas: true, // Use canvas element for drawing if available
|
||||
tileRetryMax: 0,
|
||||
tileRetryDelay: 2500,
|
||||
|
||||
@ -1437,16 +1476,6 @@ function OpenSeadragon( options ){
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* TODO: get rid of this. I can't see how it's required at all. Looks
|
||||
* like an early legacy code artifact.
|
||||
* @static
|
||||
* @ignore
|
||||
*/
|
||||
SIGNAL: "----seadragon----",
|
||||
|
||||
|
||||
/**
|
||||
* Returns a function which invokes the method as if it were a method belonging to the object.
|
||||
* @function
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - OsmTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Overlay
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* OpenSeadragon - Placement
|
||||
*
|
||||
* Copyright (C) 2010-2016 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Point
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Profiler
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Rect
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - ReferenceStrip
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -46,7 +46,7 @@ var THIS = {};
|
||||
*
|
||||
* This idea is a reexpression of the idea of dzi collections
|
||||
* which allows a clearer algorithm to reuse the tile sources already
|
||||
* supported by OpenSeadragon, in heterogenious or homogenious
|
||||
* supported by OpenSeadragon, in heterogeneous or homogeneous
|
||||
* sequences just like mixed groups already supported by the viewer
|
||||
* for the purpose of image sequnces.
|
||||
*
|
||||
@ -455,7 +455,7 @@ function loadPanels( strip, viewerSize, scroll ) {
|
||||
animationTime: 0,
|
||||
loadTilesWithAjax: strip.viewer.loadTilesWithAjax,
|
||||
ajaxHeaders: strip.viewer.ajaxHeaders,
|
||||
useCanvas: strip.useCanvas
|
||||
drawer: 'canvas', //always use canvas for the reference strip
|
||||
} );
|
||||
// Allow pointer events to pass through miniViewer's canvas/container
|
||||
// elements so implicit pointer capture works on touch devices
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Spring
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -206,7 +206,8 @@ $.Spring.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns true if the value got updated, false otherwise
|
||||
* @returns true if the spring is still updating its value, false if it is
|
||||
* already at the target value.
|
||||
*/
|
||||
update: function() {
|
||||
this.current.time = $.now();
|
||||
@ -230,14 +231,13 @@ $.Spring.prototype = {
|
||||
( this.target.time - this.start.time )
|
||||
);
|
||||
|
||||
var oldValue = this.current.value;
|
||||
if (this._exponential) {
|
||||
this.current.value = Math.exp(currentValue);
|
||||
} else {
|
||||
this.current.value = currentValue;
|
||||
}
|
||||
|
||||
return oldValue !== this.current.value;
|
||||
return currentValue !== targetValue;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - getString/setString
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
175
src/tile.js
175
src/tile.js
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Tile
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -81,6 +81,12 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.bounds = bounds;
|
||||
/**
|
||||
* Where this tile fits, in normalized coordinates, after positioning
|
||||
* @member {OpenSeadragon.Rect} positionedBounds
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.positionedBounds = new OpenSeadragon.Rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
/**
|
||||
* The portion of the tile to use as the source of the drawing operation, in pixels. Note that
|
||||
* this only works when drawing with canvas; when drawing with HTML the entire tile is always used.
|
||||
@ -274,64 +280,6 @@ $.Tile.prototype = {
|
||||
return !!this.context2D || this.getUrl().match('.png');
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the tile in an html container.
|
||||
* @function
|
||||
* @param {Element} container
|
||||
*/
|
||||
drawHTML: function( container ) {
|
||||
if (!this.cacheImageRecord) {
|
||||
$.console.warn(
|
||||
'[Tile.drawHTML] attempting to draw tile %s when it\'s not cached',
|
||||
this.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !this.loaded ) {
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//EXPERIMENTAL - trying to figure out how to scale the container
|
||||
// content during animation of the container size.
|
||||
|
||||
if ( !this.element ) {
|
||||
var image = this.getImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.element = $.makeNeutralElement( "div" );
|
||||
this.imgElement = image.cloneNode();
|
||||
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
|
||||
this.imgElement.style.width = "100%";
|
||||
this.imgElement.style.height = "100%";
|
||||
|
||||
this.style = this.element.style;
|
||||
this.style.position = "absolute";
|
||||
}
|
||||
if ( this.element.parentNode !== container ) {
|
||||
container.appendChild( this.element );
|
||||
}
|
||||
if ( this.imgElement.parentNode !== this.element ) {
|
||||
this.element.appendChild( this.imgElement );
|
||||
}
|
||||
|
||||
this.style.top = this.position.y + "px";
|
||||
this.style.left = this.position.x + "px";
|
||||
this.style.height = this.size.y + "px";
|
||||
this.style.width = this.size.x + "px";
|
||||
|
||||
if (this.flipped) {
|
||||
this.style.transform = "scaleX(-1)";
|
||||
}
|
||||
|
||||
$.setElementOpacity( this.element, this.opacity );
|
||||
},
|
||||
|
||||
/**
|
||||
* The Image object for this tile.
|
||||
* @member {Object} image
|
||||
@ -382,114 +330,7 @@ $.Tile.prototype = {
|
||||
* @returns {CanvasRenderingContext2D}
|
||||
*/
|
||||
getCanvasContext: function() {
|
||||
return this.context2D || this.cacheImageRecord.getRenderedContext();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the tile in a canvas-based context.
|
||||
* @function
|
||||
* @param {Canvas} context
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
* where <code>rendered</code> 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.
|
||||
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
|
||||
*/
|
||||
drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
|
||||
|
||||
var position = this.position.times($.pixelDensityRatio),
|
||||
size = this.size.times($.pixelDensityRatio),
|
||||
rendered;
|
||||
|
||||
if (!this.context2D && !this.cacheImageRecord) {
|
||||
$.console.warn(
|
||||
'[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached',
|
||||
this.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
rendered = this.getCanvasContext();
|
||||
|
||||
if ( !this.loaded || !rendered ){
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
context.save();
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
if (typeof scale === 'number' && scale !== 1) {
|
||||
// draw tile at a different scale
|
||||
position = position.times(scale);
|
||||
size = size.times(scale);
|
||||
}
|
||||
|
||||
if (translate instanceof $.Point) {
|
||||
// shift tile position slightly
|
||||
position = position.plus(translate);
|
||||
}
|
||||
|
||||
//if we are supposed to be rendering fully opaque rectangle,
|
||||
//ie its done fading or fading is turned off, and if we are drawing
|
||||
//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.hasTransparency) {
|
||||
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(
|
||||
position.x,
|
||||
position.y,
|
||||
size.x,
|
||||
size.y
|
||||
);
|
||||
}
|
||||
|
||||
// This gives the application a chance to make image manipulation
|
||||
// changes as we are rendering the image
|
||||
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||
|
||||
var sourceWidth, sourceHeight;
|
||||
if (this.sourceBounds) {
|
||||
sourceWidth = Math.min(this.sourceBounds.width, rendered.canvas.width);
|
||||
sourceHeight = Math.min(this.sourceBounds.height, rendered.canvas.height);
|
||||
} else {
|
||||
sourceWidth = rendered.canvas.width;
|
||||
sourceHeight = rendered.canvas.height;
|
||||
}
|
||||
|
||||
context.translate(position.x + size.x / 2, 0);
|
||||
if (this.flipped) {
|
||||
context.scale(-1, 1);
|
||||
}
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
0,
|
||||
0,
|
||||
sourceWidth,
|
||||
sourceHeight,
|
||||
-size.x / 2,
|
||||
position.y,
|
||||
size.x,
|
||||
size.y
|
||||
);
|
||||
|
||||
context.restore();
|
||||
return this.context2D || (this.cacheImageRecord && this.cacheImageRecord.getRenderedContext());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - TileCache
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -236,19 +236,51 @@ $.TileCache.prototype = {
|
||||
var tile = tileRecord.tile;
|
||||
var tiledImage = tileRecord.tiledImage;
|
||||
|
||||
// tile.getCanvasContext should always exist in normal usage (with $.Tile)
|
||||
// but the tile cache test passes in a dummy object
|
||||
let context2D = tile.getCanvasContext && tile.getCanvasContext();
|
||||
|
||||
tile.unload();
|
||||
tile.cacheImageRecord = null;
|
||||
|
||||
var imageRecord = this._imagesLoaded[tile.cacheKey];
|
||||
if(!imageRecord){
|
||||
return;
|
||||
}
|
||||
imageRecord.removeTile(tile);
|
||||
if (!imageRecord.getTileCount()) {
|
||||
|
||||
imageRecord.destroy();
|
||||
delete this._imagesLoaded[tile.cacheKey];
|
||||
this._imagesLoadedCount--;
|
||||
|
||||
if(context2D){
|
||||
/**
|
||||
* Free up canvas memory
|
||||
* (iOS 12 or higher on 2GB RAM device has only 224MB canvas memory,
|
||||
* and Safari keeps canvas until its height and width will be set to 0).
|
||||
*/
|
||||
context2D.canvas.width = 0;
|
||||
context2D.canvas.height = 0;
|
||||
|
||||
/**
|
||||
* Triggered when an image has just been unloaded
|
||||
*
|
||||
* @event image-unloaded
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {CanvasRenderingContext2D} context2D - The context that is being unloaded
|
||||
*/
|
||||
tiledImage.viewer.raiseEvent("image-unloaded", {
|
||||
context2D: context2D,
|
||||
tile: tile
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a tile has just been unloaded from memory.
|
||||
* Triggered when a tile has just been unloaded from the cache.
|
||||
*
|
||||
* @event tile-unloaded
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
@ -260,6 +292,7 @@ $.TileCache.prototype = {
|
||||
tile: tile,
|
||||
tiledImage: tiledImage
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
1125
src/tiledimage.js
1125
src/tiledimage.js
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - TileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -370,6 +370,7 @@ $.TileSource.prototype = {
|
||||
point.y >= 0 && point.y <= 1 / this.aspectRatio;
|
||||
$.console.assert(validPoint, "[TileSource.getTileAtPoint] must be called with a valid point.");
|
||||
|
||||
|
||||
var widthScaled = this.dimensions.x * this.getLevelScale(level);
|
||||
var pixelX = point.x * widthScaled;
|
||||
var pixelY = point.y * widthScaled;
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - TileSourceCollection
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - TmsTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
118
src/viewer.js
118
src/viewer.js
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Viewer
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -89,6 +89,21 @@ $.Viewer = function( options ) {
|
||||
delete options.config;
|
||||
}
|
||||
|
||||
// Move deprecated drawer options from the base options object into a sub-object
|
||||
// This is an array to make it easy to add additional properties to convert to
|
||||
// drawer options later if it makes sense to set at the drawer level rather than
|
||||
// per tiled image (for example, subPixelRoundingForTransparency).
|
||||
let drawerOptionList = [
|
||||
'useCanvas', // deprecated
|
||||
];
|
||||
options.drawerOptions = Object.assign({},
|
||||
drawerOptionList.reduce((drawerOptions, option) => {
|
||||
drawerOptions[option] = options[option];
|
||||
delete options[option];
|
||||
return drawerOptions;
|
||||
}, {}),
|
||||
options.drawerOptions);
|
||||
|
||||
//Public properties
|
||||
//Allow the options object to override global defaults
|
||||
$.extend( true, this, {
|
||||
@ -198,6 +213,7 @@ $.Viewer = function( options ) {
|
||||
$.console.warn("Hash " + this.hash + " has already been used.");
|
||||
}
|
||||
|
||||
|
||||
//Private state properties
|
||||
THIS[ this.hash ] = {
|
||||
fsBoundsDelta: new $.Point( 1, 1 ),
|
||||
@ -418,13 +434,63 @@ $.Viewer = function( options ) {
|
||||
maxImageCacheCount: this.maxImageCacheCount
|
||||
});
|
||||
|
||||
// Create the drawer
|
||||
this.drawer = new $.Drawer({
|
||||
viewer: this,
|
||||
viewport: this.viewport,
|
||||
element: this.canvas,
|
||||
debugGridColor: this.debugGridColor
|
||||
});
|
||||
//Create the drawer based on selected options
|
||||
if (Object.prototype.hasOwnProperty.call(this.drawerOptions, 'useCanvas') ){
|
||||
$.console.error('useCanvas is deprecated, use the "drawer" option to indicate preferred drawer(s)');
|
||||
|
||||
// for backwards compatibility, use HTMLDrawer if useCanvas is defined and is falsey
|
||||
if (!this.drawerOptions.useCanvas){
|
||||
this.drawer = $.HTMLDrawer;
|
||||
}
|
||||
|
||||
delete this.drawerOptions.useCanvas;
|
||||
}
|
||||
let drawerCandidates = Array.isArray(this.drawer) ? this.drawer : [this.drawer];
|
||||
if (drawerCandidates.length === 0){
|
||||
// if an empty array was passed in, throw a warning and use the defaults
|
||||
// note: if the drawer option is not specified, the defaults will already be set so this won't apply
|
||||
drawerCandidates = [$.DEFAULT_SETTINGS.drawer].flat(); // ensure it is a list
|
||||
$.console.warn('No valid drawers were selected. Using the default value.');
|
||||
}
|
||||
|
||||
|
||||
this.drawer = null;
|
||||
for (let i = 0; i < drawerCandidates.length; i++) {
|
||||
|
||||
let drawerCandidate = drawerCandidates[i];
|
||||
let Drawer = null;
|
||||
|
||||
//if inherits from a drawer base, use it
|
||||
if (drawerCandidate && drawerCandidate.prototype instanceof $.DrawerBase) {
|
||||
Drawer = drawerCandidate;
|
||||
drawerCandidate = 'custom';
|
||||
} else if (typeof drawerCandidate === "string") {
|
||||
Drawer = $.determineDrawer(drawerCandidate);
|
||||
} else {
|
||||
$.console.warn('Unsupported drawer! Drawer must be an existing string type, or a class that extends OpenSeadragon.DrawerBase.');
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the drawer is supported, create it and break the loop
|
||||
if (Drawer && Drawer.isSupported()) {
|
||||
this.drawer = new Drawer({
|
||||
viewer: this,
|
||||
viewport: this.viewport,
|
||||
element: this.canvas,
|
||||
debugGridColor: this.debugGridColor,
|
||||
options: this.drawerOptions[drawerCandidate],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!this.drawer){
|
||||
$.console.error('No drawer could be created!');
|
||||
throw('Error with creating the selected drawer(s)');
|
||||
}
|
||||
|
||||
// Pass the imageSmoothingEnabled option along to the drawer
|
||||
this.drawer.setImageSmoothingEnabled(this.imageSmoothingEnabled);
|
||||
|
||||
// Overlay container
|
||||
this.overlaysContainer = $.makeNeutralElement( "div" );
|
||||
@ -470,6 +536,7 @@ $.Viewer = function( options ) {
|
||||
displayRegionColor: this.navigatorDisplayRegionColor,
|
||||
crossOriginPolicy: this.crossOriginPolicy,
|
||||
animationTime: this.animationTime,
|
||||
drawer: this.drawer.getType(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -496,11 +563,6 @@ $.Viewer = function( options ) {
|
||||
beginControlsAutoHide( _this );
|
||||
} );
|
||||
|
||||
// Initial canvas options
|
||||
if ( this.imageSmoothingEnabled !== undefined && !this.imageSmoothingEnabled){
|
||||
this.drawer.setImageSmoothingEnabled(this.imageSmoothingEnabled);
|
||||
}
|
||||
|
||||
// Register the viewer
|
||||
$._viewers.set(this.element, this);
|
||||
};
|
||||
@ -1040,7 +1102,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isFullPage: function () {
|
||||
return THIS[ this.hash ].fullPage;
|
||||
return THIS[this.hash] && THIS[ this.hash ].fullPage;
|
||||
},
|
||||
|
||||
|
||||
@ -1087,7 +1149,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( fullPage ) {
|
||||
if ( fullPage && this.element ) {
|
||||
|
||||
this.elementSize = $.getElementSize( this.element );
|
||||
this.pageScroll = $.getPageScroll();
|
||||
@ -2403,7 +2465,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
width: this.referenceStripWidth,
|
||||
tileSources: this.tileSources,
|
||||
prefixUrl: this.prefixUrl,
|
||||
useCanvas: this.useCanvas,
|
||||
viewer: this
|
||||
});
|
||||
|
||||
@ -2552,7 +2613,6 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
|
||||
ajaxHeaders: imgOptions.ajaxHeaders ?
|
||||
imgOptions.ajaxHeaders : viewer.ajaxHeaders,
|
||||
splitHashDataForPost: viewer.splitHashDataForPost,
|
||||
useCanvas: viewer.useCanvas,
|
||||
success: function( event ) {
|
||||
successCallback( event.tileSource );
|
||||
}
|
||||
@ -2570,9 +2630,6 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
|
||||
if (tileSource.ajaxWithCredentials === undefined) {
|
||||
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
|
||||
}
|
||||
if (tileSource.useCanvas === undefined) {
|
||||
tileSource.useCanvas = viewer.useCanvas;
|
||||
}
|
||||
|
||||
if ( $.isFunction( tileSource.getTileUrl ) ) {
|
||||
//Custom tile source
|
||||
@ -3705,7 +3762,7 @@ function updateOnce( viewer ) {
|
||||
|
||||
|
||||
var viewportChange = viewer.viewport.update();
|
||||
var animated = viewer.world.update() || viewportChange;
|
||||
var animated = viewer.world.update(viewportChange) || viewportChange;
|
||||
|
||||
if (viewportChange) {
|
||||
/**
|
||||
@ -3795,7 +3852,6 @@ function updateOnce( viewer ) {
|
||||
|
||||
function drawWorld( viewer ) {
|
||||
viewer.imageLoader.clear();
|
||||
viewer.drawer.clear();
|
||||
viewer.world.draw();
|
||||
|
||||
/**
|
||||
@ -3949,4 +4005,22 @@ function onFlip() {
|
||||
this.viewport.toggleFlip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find drawer
|
||||
*/
|
||||
$.determineDrawer = function( id ){
|
||||
for (let property in OpenSeadragon) {
|
||||
const drawer = OpenSeadragon[ property ],
|
||||
proto = drawer.prototype;
|
||||
if( proto &&
|
||||
proto instanceof OpenSeadragon.DrawerBase &&
|
||||
$.isFunction( proto.getType ) &&
|
||||
proto.getType.call( drawer ) === id
|
||||
){
|
||||
return drawer;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - Viewport
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -1133,7 +1133,7 @@ $.Viewport.prototype = {
|
||||
/**
|
||||
* Update the zoom, degrees, and center (X and Y) springs.
|
||||
* @function
|
||||
* @returns {Boolean} True if any change has been made, false otherwise.
|
||||
* @returns {Boolean} True if the viewport is still animating, false otherwise.
|
||||
*/
|
||||
update: function() {
|
||||
var _this = this;
|
||||
@ -1165,7 +1165,13 @@ $.Viewport.prototype = {
|
||||
this._oldZoom = this.zoomSpring.current.value;
|
||||
this._oldDegrees = this.degreesSpring.current.value;
|
||||
|
||||
return changed;
|
||||
var isAnimating = changed ||
|
||||
!this.zoomSpring.isAtTargetValue() ||
|
||||
!this.centerSpringX.isAtTargetValue() ||
|
||||
!this.centerSpringY.isAtTargetValue() ||
|
||||
!this.degreesSpring.isAtTargetValue();
|
||||
|
||||
return isAnimating;
|
||||
},
|
||||
|
||||
// private - pass true to use spring, or a number for degrees for immediate rotation
|
||||
|
1124
src/webgldrawer.js
Normal file
1124
src/webgldrawer.js
Normal file
File diff suppressed because it is too large
Load Diff
17
src/world.js
17
src/world.js
@ -2,7 +2,7 @@
|
||||
* OpenSeadragon - World
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2023 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
@ -242,11 +242,14 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
|
||||
/**
|
||||
* Updates (i.e. animates bounds of) all items.
|
||||
* @function
|
||||
* @param viewportChanged Whether the viewport changed, which indicates that
|
||||
* all TiledImages need to be updated.
|
||||
*/
|
||||
update: function() {
|
||||
update: function(viewportChanged) {
|
||||
var animated = false;
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
animated = this._items[i].update() || animated;
|
||||
animated = this._items[i].update(viewportChanged) || animated;
|
||||
}
|
||||
|
||||
return animated;
|
||||
@ -256,11 +259,11 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
* Draws all items.
|
||||
*/
|
||||
draw: function() {
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
this._items[i].draw();
|
||||
}
|
||||
|
||||
this.viewer.drawer.draw(this._items);
|
||||
this._needsDraw = false;
|
||||
this._items.forEach(function(item){
|
||||
this._needsDraw = item.setDrawn() || this._needsDraw;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
tileSources: "../data/testpattern.dzi",
|
||||
showNavigator: true
|
||||
showNavigator: true,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
@ -16,7 +16,6 @@
|
||||
// debugMode: true,
|
||||
zoomPerScroll: 1.02,
|
||||
showNavigator: testNavigator,
|
||||
useCanvas: true,
|
||||
// defaultZoomLevel: 2,
|
||||
// homeFillsViewer: true,
|
||||
// sequenceMode: true,
|
||||
@ -131,8 +130,9 @@
|
||||
var box = new OpenSeadragon.Rect(margins.left, margins.top,
|
||||
$('#contentDiv').width() - (margins.left + margins.right),
|
||||
$('#contentDiv').height() - (margins.top + margins.bottom));
|
||||
|
||||
self.viewer.drawer.debugRect(box);
|
||||
// If drawDebuggingRect is implemented, use it to show the box.
|
||||
// This is not implemented by all drawers however.
|
||||
self.viewer.drawer.drawDebuggingRect(box);
|
||||
});
|
||||
}
|
||||
|
||||
|
114
test/demo/drawercomparison.html
Normal file
114
test/demo/drawercomparison.html
Normal file
@ -0,0 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Drawer Comparison 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>
|
||||
<script type="text/javascript" src='../lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js'></script>
|
||||
<link rel="stylesheet" href="../lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
|
||||
|
||||
<script type="module" src="./drawercomparison.js"></script>
|
||||
<style type="text/css">
|
||||
.content{
|
||||
max-width:960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.mirrored{
|
||||
display:grid;
|
||||
grid-template-columns:50% 50%;
|
||||
gap: 2em;
|
||||
}
|
||||
.viewer-container {
|
||||
/* width: 600px;
|
||||
height: 400px; */
|
||||
aspect-ratio: 4 / 3;
|
||||
border: thin gray solid;
|
||||
position:relative;
|
||||
}
|
||||
.example-code{
|
||||
background-color:tan;
|
||||
border: thin black solid;
|
||||
padding:10px;
|
||||
display:inline-block;
|
||||
width:95%;
|
||||
}
|
||||
.description pre{
|
||||
display:inline-block;
|
||||
background-color:gainsboro;
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.image-options{
|
||||
display: grid;
|
||||
grid-template-columns: 2em 9em 1fr;
|
||||
padding:3px;
|
||||
border: thin gray solid;
|
||||
}
|
||||
.option-grid{
|
||||
display: grid;
|
||||
grid-template-columns: 7em 7em 9em 9em 10em 9em;
|
||||
/* grid-template-columns: repeat(5, auto); */
|
||||
}
|
||||
.image-options input[type=number]{
|
||||
width: 5em;
|
||||
}
|
||||
.image-options select{
|
||||
width: 5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
|
||||
<h2>Compare behavior of <strong>Context2d</strong> and <strong>WebGL</strong> drawers</h2>
|
||||
<div class="mirrored">
|
||||
<div>
|
||||
<h3 id="title-w1">Loading...</h3>
|
||||
<div id="canvasdrawer" class="viewer-container"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 id="title-w2">Loading...</h3>
|
||||
<div id="webgl" class="viewer-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="image-picker">
|
||||
<h3>Image options (drag and drop to re-order images)</h3>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<h2>HTMLDrawer: legacy pre-HTML5 drawer that uses <img> elements for tiles</h2>
|
||||
<div class="mirrored">
|
||||
<div>
|
||||
<div class="description">
|
||||
HTML-based rendering can be selected in two different ways:
|
||||
</div>
|
||||
<pre class="example-code">
|
||||
// via the 'html' drawer option:
|
||||
let viewer = OpenSeadragon({
|
||||
...
|
||||
drawer: 'html',
|
||||
...
|
||||
});
|
||||
|
||||
// or by passing the HTMLDrawer constructor
|
||||
let viewer = OpenSeadragon({
|
||||
...
|
||||
drawer:OpenSeadragon.HTMLDrawer,
|
||||
...
|
||||
});
|
||||
</pre>
|
||||
</div>
|
||||
<div id="htmldrawer" class="viewer-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
356
test/demo/drawercomparison.js
Normal file
356
test/demo/drawercomparison.js
Normal file
@ -0,0 +1,356 @@
|
||||
const sources = {
|
||||
"rainbow":"../data/testpattern.dzi",
|
||||
"leaves":"../data/iiif_2_0_sizes/info.json",
|
||||
"bblue":{
|
||||
type:'image',
|
||||
url: "../data/BBlue.png",
|
||||
},
|
||||
"duomo":"https://openseadragon.github.io/example-images/duomo/duomo.dzi",
|
||||
}
|
||||
const labels = {
|
||||
rainbow: 'Rainbow Grid',
|
||||
leaves: 'Leaves',
|
||||
bblue: 'Blue B',
|
||||
duomo: 'Duomo',
|
||||
}
|
||||
const drawers = {
|
||||
canvas: "Context2d drawer (default in OSD <= 4.1.0)",
|
||||
webgl: "New WebGL drawer"
|
||||
}
|
||||
|
||||
//Support drawer type from the url
|
||||
const url = new URL(window.location.href);
|
||||
const drawer1 = url.searchParams.get("left") || 'canvas';
|
||||
const drawer2 = url.searchParams.get("right") || 'webgl';
|
||||
|
||||
$("#title-w1").html(drawers[drawer1]);
|
||||
$("#title-w2").html(drawers[drawer2]);
|
||||
|
||||
//Double viewer setup for comparison - CanvasDrawer and WebGLDrawer
|
||||
// viewer1: canvas drawer
|
||||
let viewer1 = window.viewer1 = OpenSeadragon({
|
||||
id: "canvasdrawer",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
minZoomImageRatio:0.01,
|
||||
maxZoomPixelRatio:100,
|
||||
smoothTileEdgesMinZoom:1.1,
|
||||
crossOriginPolicy: 'Anonymous',
|
||||
ajaxWithCredentials: false,
|
||||
// maxImageCacheCount: 30,
|
||||
drawer:drawer1,
|
||||
blendTime:0,
|
||||
showNavigator:true,
|
||||
});
|
||||
|
||||
// viewer2: webgl drawer
|
||||
let viewer2 = window.viewer2 = OpenSeadragon({
|
||||
id: "webgl",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
minZoomImageRatio:0.01,
|
||||
maxZoomPixelRatio:100,
|
||||
smoothTileEdgesMinZoom:1.1,
|
||||
crossOriginPolicy: 'Anonymous',
|
||||
ajaxWithCredentials: false,
|
||||
// maxImageCacheCount: 30,
|
||||
drawer:drawer2,
|
||||
blendTime:0,
|
||||
showNavigator:true,
|
||||
});
|
||||
|
||||
// // viewer3: html drawer, unused
|
||||
var viewer3 = window.viewer3 = OpenSeadragon({
|
||||
id: "htmldrawer",
|
||||
drawer:'html',
|
||||
blendTime:2,
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
minZoomImageRatio:0.01,
|
||||
customDrawer: OpenSeadragon.HTMLDrawer,
|
||||
tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']],
|
||||
sequenceMode: true,
|
||||
crossOriginPolicy: 'Anonymous',
|
||||
ajaxWithCredentials: false
|
||||
});
|
||||
|
||||
|
||||
// Sync navigation of viewer1 and viewer 2
|
||||
var viewer1Leading = false;
|
||||
var viewer2Leading = false;
|
||||
|
||||
var viewer1Handler = function() {
|
||||
if (viewer2Leading) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewer1Leading = true;
|
||||
viewer2.viewport.zoomTo(viewer1.viewport.getZoom());
|
||||
viewer2.viewport.panTo(viewer1.viewport.getCenter());
|
||||
viewer2.viewport.rotateTo(viewer1.viewport.getRotation());
|
||||
viewer2.viewport.setFlip(viewer1.viewport.flipped);
|
||||
viewer1Leading = false;
|
||||
};
|
||||
|
||||
var viewer2Handler = function() {
|
||||
if (viewer1Leading) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewer2Leading = true;
|
||||
viewer1.viewport.zoomTo(viewer2.viewport.getZoom());
|
||||
viewer1.viewport.panTo(viewer2.viewport.getCenter());
|
||||
viewer1.viewport.rotateTo(viewer2.viewport.getRotation());
|
||||
viewer1.viewport.setFlip(viewer1.viewport.flipped);
|
||||
viewer2Leading = false;
|
||||
};
|
||||
|
||||
viewer1.addHandler('zoom', viewer1Handler);
|
||||
viewer2.addHandler('zoom', viewer2Handler);
|
||||
viewer1.addHandler('pan', viewer1Handler);
|
||||
viewer2.addHandler('pan', viewer2Handler);
|
||||
viewer1.addHandler('rotate', viewer1Handler);
|
||||
viewer2.addHandler('rotate', viewer2Handler);
|
||||
viewer1.addHandler('flip', viewer1Handler);
|
||||
viewer2.addHandler('flip', viewer2Handler);
|
||||
|
||||
|
||||
$('#image-picker').sortable({
|
||||
update: function(event, ui){
|
||||
let thisItem = ui.item.find('.toggle').data('item1');
|
||||
let items = $('#image-picker input.toggle:checked').toArray().map(item=>$(item).data('item1'));
|
||||
let newIndex = items.indexOf(thisItem);
|
||||
if(thisItem){
|
||||
viewer1.world.setItemIndex(thisItem, newIndex);
|
||||
}
|
||||
|
||||
thisItem = ui.item.find('.toggle').data('item2');
|
||||
items = $('#image-picker input.toggle:checked').toArray().map(item=>$(item).data('item2'));
|
||||
newIndex = items.indexOf(thisItem);
|
||||
if(thisItem){
|
||||
viewer2.world.setItemIndex(thisItem, newIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(sources).forEach((key, index)=>{
|
||||
let element = makeImagePickerElement(key, labels[key])
|
||||
$('#image-picker').append(element);
|
||||
if(index === 0){
|
||||
element.find('.toggle').prop('checked',true);
|
||||
}
|
||||
})
|
||||
|
||||
$('#image-picker').append(makeComparisonSwitcher());
|
||||
|
||||
$('#image-picker input.toggle').on('change',function(){
|
||||
let data = $(this).data();
|
||||
if(this.checked){
|
||||
addTileSource(viewer1, data.image, this);
|
||||
addTileSource(viewer2, data.image, this);
|
||||
} else {
|
||||
if(data.item1){
|
||||
viewer1.world.removeItem(data.item1);
|
||||
viewer2.world.removeItem(data.item2);
|
||||
$(this).data({item1: null, item2: null});
|
||||
}
|
||||
}
|
||||
}).trigger('change');
|
||||
|
||||
$('#image-picker input:not(.toggle)').on('change',function(){
|
||||
let data = $(this).data();
|
||||
let value = $(this).val();
|
||||
let tiledImage1 = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item1');
|
||||
let tiledImage2 = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item2');
|
||||
updateTiledImage(tiledImage1, data, value, this);
|
||||
updateTiledImage(tiledImage2, data, value, this);
|
||||
});
|
||||
|
||||
function updateTiledImage(tiledImage, data, value, item){
|
||||
let field = data.field;
|
||||
|
||||
if(tiledImage){
|
||||
//item = tiledImage
|
||||
if(field == 'x'){
|
||||
let bounds = tiledImage.getBoundsNoRotate();
|
||||
let position = new OpenSeadragon.Point(Number(value), bounds.y);
|
||||
tiledImage.setPosition(position);
|
||||
} else if ( field == 'y'){
|
||||
let bounds = tiledImage.getBoundsNoRotate();
|
||||
let position = new OpenSeadragon.Point(bounds.x, Number(value));
|
||||
tiledImage.setPosition(position);
|
||||
} else if (field == 'width'){
|
||||
tiledImage.setWidth(Number(value));
|
||||
} else if (field == 'degrees'){
|
||||
tiledImage.setRotation(Number(value));
|
||||
} else if (field == 'opacity'){
|
||||
tiledImage.setOpacity(Number(value));
|
||||
} else if (field == 'flipped'){
|
||||
tiledImage.setFlip($(item).prop('checked'));
|
||||
} else if (field == 'cropped'){
|
||||
if( $(item).prop('checked') ){
|
||||
let scale = tiledImage.source.width;
|
||||
let croppingPolygons = [ [{x:0.2*scale, y:0.2*scale}, {x:0.8*scale, y:0.2*scale}, {x:0.5*scale, y:0.8*scale}] ];
|
||||
tiledImage.setCroppingPolygons(croppingPolygons);
|
||||
} else {
|
||||
tiledImage.resetCroppingPolygons();
|
||||
}
|
||||
} else if (field == 'clipped'){
|
||||
if( $(item).prop('checked') ){
|
||||
let scale = tiledImage.source.width;
|
||||
let clipRect = new OpenSeadragon.Rect(0.1*scale, 0.2*scale, 0.6*scale, 0.4*scale);
|
||||
tiledImage.setClip(clipRect);
|
||||
} else {
|
||||
tiledImage.setClip(null);
|
||||
}
|
||||
} else if (field == 'debug'){
|
||||
if( $(item).prop('checked') ){
|
||||
tiledImage.debugMode = true;
|
||||
} else {
|
||||
tiledImage.debugMode = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//viewer-level option
|
||||
}
|
||||
}
|
||||
|
||||
$('.image-options select[data-field=composite]').append(getCompositeOperationOptions()).on('change',function(){
|
||||
let data = $(this).data();
|
||||
let tiledImage1 = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item1');
|
||||
if(tiledImage1){
|
||||
tiledImage1.setCompositeOperation(this.value == 'null' ? null : this.value);
|
||||
}
|
||||
let tiledImage2 = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item2');
|
||||
if(tiledImage2){
|
||||
tiledImage2.setCompositeOperation(this.value == 'null' ? null : this.value);
|
||||
}
|
||||
}).trigger('change');
|
||||
|
||||
$('.image-options select[data-field=wrapping]').append(getWrappingOptions()).on('change',function(){
|
||||
let data = $(this).data();
|
||||
let tiledImage = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item1');
|
||||
if(tiledImage){
|
||||
switch(this.value){
|
||||
case "None": tiledImage.wrapHorizontal = tiledImage.wrapVertical = false; break;
|
||||
case "Horizontal": tiledImage.wrapHorizontal = true; tiledImage.wrapVertical = false; break;
|
||||
case "Vertical": tiledImage.wrapHorizontal = false; tiledImage.wrapVertical = true; break;
|
||||
case "Both": tiledImage.wrapHorizontal = tiledImage.wrapVertical = true; break;
|
||||
}
|
||||
tiledImage.redraw();//trigger a redraw for the webgl renderer.
|
||||
}
|
||||
tiledImage = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item2');
|
||||
if(tiledImage){
|
||||
switch(this.value){
|
||||
case "None": tiledImage.wrapHorizontal = tiledImage.wrapVertical = false; break;
|
||||
case "Horizontal": tiledImage.wrapHorizontal = true; tiledImage.wrapVertical = false; break;
|
||||
case "Vertical": tiledImage.wrapHorizontal = false; tiledImage.wrapVertical = true; break;
|
||||
case "Both": tiledImage.wrapHorizontal = tiledImage.wrapVertical = true; break;
|
||||
}
|
||||
tiledImage.redraw();//trigger a redraw for the webgl renderer.
|
||||
}
|
||||
}).trigger('change');
|
||||
|
||||
function getWrappingOptions(){
|
||||
let opts = ['None', 'Horizontal', 'Vertical', 'Both'];
|
||||
let elements = opts.map((opt, i)=>{
|
||||
let el = $('<option>',{value:opt}).text(opt);
|
||||
if(i===0){
|
||||
el.attr('selected',true);
|
||||
}
|
||||
return el[0];
|
||||
// $('.image-options select').append(el);
|
||||
});
|
||||
return $(elements);
|
||||
}
|
||||
function getCompositeOperationOptions(){
|
||||
let opts = [null,'source-over','source-in','source-out','source-atop',
|
||||
'destination-over','destination-in','destination-out','destination-atop',
|
||||
'lighten','darken','copy','xor','multiply','screen','overlay','color-dodge',
|
||||
'color-burn','hard-light','soft-light','difference','exclusion',
|
||||
'hue','saturation','color','luminosity'];
|
||||
let elements = opts.map((opt, i)=>{
|
||||
let el = $('<option>',{value:opt}).text(opt);
|
||||
if(i===0){
|
||||
el.attr('selected',true);
|
||||
}
|
||||
return el[0];
|
||||
// $('.image-options select').append(el);
|
||||
});
|
||||
return $(elements);
|
||||
|
||||
}
|
||||
|
||||
function addTileSource(viewer, image, checkbox){
|
||||
let options = $(`#image-picker input[data-image=${image}][type=number]`).toArray().reduce((acc, input)=>{
|
||||
let field = $(input).data('field');
|
||||
if(field){
|
||||
acc[field] = Number(input.value);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
options.flipped = $(`#image-picker input[data-image=${image}][data-type=flipped]`).prop('checked');
|
||||
|
||||
let items = $('#image-picker input.toggle:checked').toArray();
|
||||
let insertionIndex = items.indexOf(checkbox);
|
||||
|
||||
let tileSource = sources[image];
|
||||
if(tileSource){
|
||||
viewer&&viewer.addTiledImage({tileSource: tileSource, ...options, index: insertionIndex});
|
||||
viewer&&viewer.world.addOnceHandler('add-item',function(ev){
|
||||
let item = ev.item;
|
||||
let field = viewer === viewer1 ? 'item1' : 'item2';
|
||||
$(checkbox).data(field,item);
|
||||
// item.source.hasTransparency = ()=>true; //simulate image with transparency, to show seams in default renderer
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getAvailableDrawerSelect(name, selectedDrawer) {
|
||||
return `
|
||||
<select name="${name}">
|
||||
${Object.entries(drawers).map(([k, v]) => {
|
||||
const selected = selectedDrawer === k ? "selected" : "";
|
||||
return `<option value="${k}" ${selected}>${v}</option>`;
|
||||
}).join("\n")}
|
||||
</select>`;
|
||||
}
|
||||
|
||||
function makeComparisonSwitcher() {
|
||||
const left = getAvailableDrawerSelect("left", drawer1),
|
||||
right = getAvailableDrawerSelect("right", drawer2);
|
||||
return `
|
||||
<div>
|
||||
Note: you can run the comparison with desired drawers like this: drawercomparison.html?left=[type]&right=[type]
|
||||
<form method="get">
|
||||
${left}
|
||||
${right}
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function makeImagePickerElement(key, label){
|
||||
return $(`<div class="image-options">
|
||||
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
|
||||
<label><input type="checkbox" data-image="" class="toggle"> __title__</label>
|
||||
<div class="option-grid">
|
||||
<label>X: <input type="number" value="0" data-image="" data-field="x"> </label>
|
||||
<label>Y: <input type="number" value="0" data-image="" data-field="y"> </label>
|
||||
<label>Width: <input type="number" value="1" data-image="" data-field="width" min="0"> </label>
|
||||
<label>Degrees: <input type="number" value="0" data-image="" data-field="degrees"> </label>
|
||||
<label>Opacity: <input type="number" value="1" data-image="" data-field="opacity" min="0" max="1" step="0.2"> </label>
|
||||
<span></span>
|
||||
<label>Flipped: <input type="checkbox" data-image="" data-field="flipped"></label>
|
||||
<label>Cropped: <input type="checkbox" data-image="" data-field="cropped"></label>
|
||||
<label>Clipped: <input type="checkbox" data-image="" data-field="clipped"></label>
|
||||
<label>Chess Tile Opacity: <input type="checkbox" data-image="" data-field="tile-level-opecity"></label>
|
||||
<label>Debug: <input type="checkbox" data-image="" data-field="debug"></label>
|
||||
<label>Composite: <select data-image="" data-field="composite"></select></label>
|
||||
<label>Wrap: <select data-image="" data-field="wrapping"></select></label>
|
||||
</div>
|
||||
</div>`.replaceAll('data-image=""', `data-image="${key}"`).replace('__title__', label));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
44
test/demo/drawerperformance.html
Normal file
44
test/demo/drawerperformance.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Drawer Comparison 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>
|
||||
<script type="text/javascript" src='../lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js'></script>
|
||||
<link rel="stylesheet" href="../lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/17/Stats.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script> -->
|
||||
|
||||
<script type="module" src="./drawerperformance.js"></script>
|
||||
<style type="text/css">
|
||||
.content{
|
||||
max-width:960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#drawer{
|
||||
height:800px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
|
||||
<h2>Compare performance of drawer implementations</h2>
|
||||
<div>
|
||||
<label>Select a drawer: </label>
|
||||
<select id="select-drawer">
|
||||
|
||||
</select>
|
||||
<label>Num images: </label>
|
||||
<input id="input-number" type="number" value="1" min="1" step="1">
|
||||
<button id="create-drawer">Create drawer</button>
|
||||
</div>
|
||||
<div id="drawer"></div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
1002
test/demo/drawerperformance.js
Normal file
1002
test/demo/drawerperformance.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -96,4 +96,4 @@
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
87
test/demo/rstats.css
Normal file
87
test/demo/rstats.css
Normal file
@ -0,0 +1,87 @@
|
||||
.alarm{
|
||||
color: #b70000;
|
||||
text-shadow: 0 0 0 #b70000,
|
||||
0 0 1px #fff,
|
||||
0 0 1px #fff,
|
||||
0 0 2px #fff,
|
||||
0 0 2px #fff,
|
||||
0 0 3px #fff,
|
||||
0 0 3px #fff,
|
||||
0 0 4px #fff,
|
||||
0 0 4px #fff;
|
||||
}
|
||||
|
||||
.rs-base{
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
padding: 10px;
|
||||
background-color: #222;
|
||||
font-size: 10px;
|
||||
line-height: 1.2em;
|
||||
width: 350px;
|
||||
font-family: 'Roboto Condensed', tahoma, sans-serif;
|
||||
left: 0;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.rs-base h1{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.4em;
|
||||
color: #fff;
|
||||
margin-bottom: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rs-base div.rs-group{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.rs-base div.rs-group.hidden{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rs-base div.rs-fraction{
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.rs-base div.rs-fraction p{
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rs-base div.rs-legend{
|
||||
position: absolute;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.rs-base div.rs-counter-base{
|
||||
position: relative;
|
||||
margin: 2px 0;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.rs-base span.rs-counter-id{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.rs-base div.rs-counter-value{
|
||||
position: absolute;
|
||||
left: 90px;
|
||||
width: 30px;
|
||||
height: 1em;
|
||||
top: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rs-base canvas.rs-canvas{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
@ -80,12 +80,12 @@
|
||||
// The changeover will look better if we wait for the first tile to be drawn.
|
||||
var tileDrawnHandler = function(event) {
|
||||
if (event.tiledImage === fullImage) {
|
||||
viewer.removeHandler('tile-drawn', tileDrawnHandler);
|
||||
viewer.removeHandler('update-viewport', tileDrawnHandler);
|
||||
viewer.world.removeItem(standin);
|
||||
}
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-drawn', tileDrawnHandler);
|
||||
viewer.addHandler('update-viewport', tileDrawnHandler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* global $, Util */
|
||||
/* global $, QUnit, Util */
|
||||
|
||||
(function () {
|
||||
|
||||
@ -146,9 +146,15 @@
|
||||
obj0[member0]();
|
||||
assert.equal(called, true, 'called through for ' + member0);
|
||||
assert.equal(errored, true, 'errored for ' + member0);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Log the name of the currently running test when it starts. Uses console.log rather than
|
||||
// $.console.log so that the message is printed even after the $.console is diverted (see below).
|
||||
QUnit.testStart((details) => {
|
||||
console.log(`Starting test ${details.module}.${details.name}`);
|
||||
});
|
||||
|
||||
/*
|
||||
Test console log capture
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
};
|
||||
|
||||
var viewer = null;
|
||||
var DONE = false;
|
||||
var OriginalLoader = OpenSeadragon.ImageLoader;
|
||||
var OriginalAjax = OpenSeadragon.makeAjaxRequest;
|
||||
|
||||
@ -156,7 +157,7 @@
|
||||
ASSERT = null;
|
||||
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
DONE ? viewer.destroy () : viewer.close();
|
||||
}
|
||||
viewer = null;
|
||||
|
||||
@ -271,6 +272,7 @@
|
||||
return "d1=a1&d2=a2###";
|
||||
},
|
||||
}, true, true);
|
||||
DONE = true; // mark the module as completed so the viewer can be destroyed
|
||||
});
|
||||
|
||||
})();
|
||||
|
@ -53,8 +53,8 @@
|
||||
});
|
||||
},
|
||||
afterEach: function() {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
@ -16,8 +16,8 @@
|
||||
});
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
@ -223,49 +223,50 @@
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
QUnit.test('FullScreen', function(assert) {
|
||||
const done = assert.async();
|
||||
if (!OpenSeadragon.supportsFullScreen) {
|
||||
assert.expect(0);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
// TODO: can this be enabled without breaking tests due to lack of short-duration user interaction?
|
||||
// QUnit.test('FullScreen', function(assert) {
|
||||
// const done = assert.async();
|
||||
// if (!OpenSeadragon.supportsFullScreen) {
|
||||
// assert.expect(0);
|
||||
// done();
|
||||
// return;
|
||||
// }
|
||||
|
||||
viewer.addHandler('open', function () {
|
||||
assert.ok(!OpenSeadragon.isFullScreen(), 'Started out not fullscreen');
|
||||
// viewer.addHandler('open', function () {
|
||||
// assert.ok(!OpenSeadragon.isFullScreen(), 'Started out not fullscreen');
|
||||
|
||||
const checkEnteringPreFullScreen = (event) => {
|
||||
viewer.removeHandler('pre-full-screen', checkEnteringPreFullScreen);
|
||||
assert.ok(event.fullScreen, 'Switching to fullscreen');
|
||||
assert.ok(!OpenSeadragon.isFullScreen(), 'Not yet fullscreen');
|
||||
};
|
||||
// const checkEnteringPreFullScreen = (event) => {
|
||||
// viewer.removeHandler('pre-full-screen', checkEnteringPreFullScreen);
|
||||
// assert.ok(event.fullScreen, 'Switching to fullscreen');
|
||||
// assert.ok(!OpenSeadragon.isFullScreen(), 'Not yet fullscreen');
|
||||
// };
|
||||
|
||||
const checkExitingFullScreen = (event) => {
|
||||
viewer.removeHandler('full-screen', checkExitingFullScreen);
|
||||
assert.ok(!event.fullScreen, 'Disabling fullscreen');
|
||||
assert.ok(!OpenSeadragon.isFullScreen(), 'Fullscreen disabled');
|
||||
done();
|
||||
}
|
||||
// const checkExitingFullScreen = (event) => {
|
||||
// viewer.removeHandler('full-screen', checkExitingFullScreen);
|
||||
// assert.ok(!event.fullScreen, 'Disabling fullscreen');
|
||||
// assert.ok(!OpenSeadragon.isFullScreen(), 'Fullscreen disabled');
|
||||
// done();
|
||||
// }
|
||||
|
||||
// The 'new' headless mode allows us to enter fullscreen, so verify
|
||||
// that we see the correct values returned. We will then close out
|
||||
// of fullscreen to check the same values when exiting.
|
||||
const checkAcquiredFullScreen = (event) => {
|
||||
viewer.removeHandler('full-screen', checkAcquiredFullScreen);
|
||||
viewer.addHandler('full-screen', checkExitingFullScreen);
|
||||
assert.ok(event.fullScreen, 'Acquired fullscreen');
|
||||
assert.ok(OpenSeadragon.isFullScreen(), 'Fullscreen enabled');
|
||||
viewer.setFullScreen(false);
|
||||
};
|
||||
// // The 'new' headless mode allows us to enter fullscreen, so verify
|
||||
// // that we see the correct values returned. We will then close out
|
||||
// // of fullscreen to check the same values when exiting.
|
||||
// const checkAcquiredFullScreen = (event) => {
|
||||
// viewer.removeHandler('full-screen', checkAcquiredFullScreen);
|
||||
// viewer.addHandler('full-screen', checkExitingFullScreen);
|
||||
// assert.ok(event.fullScreen, 'Acquired fullscreen');
|
||||
// assert.ok(OpenSeadragon.isFullScreen(), 'Fullscreen enabled');
|
||||
// viewer.setFullScreen(false);
|
||||
// };
|
||||
|
||||
|
||||
viewer.addHandler('pre-full-screen', checkEnteringPreFullScreen);
|
||||
viewer.addHandler('full-screen', checkAcquiredFullScreen);
|
||||
viewer.setFullScreen(true);
|
||||
});
|
||||
// viewer.addHandler('pre-full-screen', checkEnteringPreFullScreen);
|
||||
// viewer.addHandler('full-screen', checkAcquiredFullScreen);
|
||||
// viewer.setFullScreen(true);
|
||||
// });
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
// viewer.open('/test/data/testpattern.dzi');
|
||||
// });
|
||||
|
||||
QUnit.test('Close', function(assert) {
|
||||
var done = assert.async();
|
||||
@ -330,8 +331,8 @@
|
||||
height: 155
|
||||
} ]
|
||||
} );
|
||||
viewer.addOnceHandler('tile-drawn', function() {
|
||||
assert.ok(OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||
assert.ok(OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
||||
"Canvas should be tainted.");
|
||||
done();
|
||||
});
|
||||
@ -350,8 +351,8 @@
|
||||
height: 155
|
||||
} ]
|
||||
} );
|
||||
viewer.addOnceHandler('tile-drawn', function() {
|
||||
assert.ok(!OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||
assert.ok(!OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
||||
"Canvas should not be tainted.");
|
||||
done();
|
||||
});
|
||||
@ -374,8 +375,8 @@
|
||||
},
|
||||
crossOriginPolicy : false
|
||||
} );
|
||||
viewer.addOnceHandler('tile-drawn', function() {
|
||||
assert.ok(OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||
assert.ok(OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
||||
"Canvas should be tainted.");
|
||||
done();
|
||||
});
|
||||
@ -398,8 +399,8 @@
|
||||
crossOriginPolicy : "Anonymous"
|
||||
}
|
||||
} );
|
||||
viewer.addOnceHandler('tile-drawn', function() {
|
||||
assert.ok(!OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||
assert.ok(!OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
||||
"Canvas should not be tainted.");
|
||||
done();
|
||||
});
|
||||
|
4
test/modules/controls.js
vendored
4
test/modules/controls.js
vendored
@ -16,8 +16,8 @@
|
||||
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
@ -2,133 +2,154 @@
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
const drawerTypes = ['webgl','canvas','html'];
|
||||
drawerTypes.forEach(runDrawerTests);
|
||||
|
||||
QUnit.module('Drawer', {
|
||||
beforeEach: function () {
|
||||
$('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
function runDrawerTests(drawerType){
|
||||
|
||||
testLog.reset();
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
QUnit.module('Drawer-'+drawerType, {
|
||||
beforeEach: function () {
|
||||
$('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
viewer = null;
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
var createViewer = function(options) {
|
||||
options = options || {};
|
||||
// eslint-disable-next-line new-cap
|
||||
viewer = OpenSeadragon(OpenSeadragon.extend({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
}, options));
|
||||
};
|
||||
|
||||
// ----------
|
||||
QUnit.test('basics', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer();
|
||||
assert.ok(viewer.drawer, 'Drawer exists');
|
||||
assert.equal(viewer.drawer.canRotate(), OpenSeadragon.supportsCanvas, 'we can rotate if we have canvas');
|
||||
done();
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('rotation', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
|
||||
viewer.addHandler('open', function handler(event) {
|
||||
viewer.viewport.setRotation(30, true);
|
||||
Util.spyOnce(viewer.drawer.context, 'rotate', function() {
|
||||
assert.ok(true, 'drawing with new rotation');
|
||||
// ----------
|
||||
var createViewer = function(options) {
|
||||
options = options || {};
|
||||
// eslint-disable-next-line new-cap
|
||||
viewer = OpenSeadragon(OpenSeadragon.extend({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
drawer: drawerType,
|
||||
}, options));
|
||||
};
|
||||
|
||||
// ----------
|
||||
QUnit.test('basics', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer();
|
||||
assert.ok(viewer.drawer, 'Drawer exists');
|
||||
assert.equal(viewer.drawer.canRotate(), ['webgl','canvas'].includes(drawerType), 'we can rotate if we have canvas');
|
||||
done();
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('rotation', function(assert) {
|
||||
var done = assert.async();
|
||||
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
|
||||
// this test only makes sense for canvas drawer because of how it is
|
||||
// detected by watching for the canvas context rotate function
|
||||
// TODO: add test for actual rotation of the drawn image in a way that
|
||||
// applies to the webgl drawer as well as the canvas drawer.
|
||||
if(viewer.drawer.getType() !== 'canvas'){
|
||||
assert.expect(0);
|
||||
done();
|
||||
};
|
||||
|
||||
|
||||
viewer.addHandler('open', function handler(event) {
|
||||
viewer.viewport.setRotation(30, true);
|
||||
Util.spyOnce(viewer.drawer.context, 'rotate', function() {
|
||||
assert.ok(true, 'drawing with new rotation');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('debug', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi',
|
||||
debugMode: true
|
||||
});
|
||||
|
||||
// only test this for canvas and webgl drawers
|
||||
if( !['canvas','webgl'].includes(viewer.drawer.getType() )){
|
||||
assert.expect(0);
|
||||
done()
|
||||
}
|
||||
Util.spyOnce(viewer.drawer, '_drawDebugInfo', function() {
|
||||
assert.ok(true, '_drawDebugInfo is called');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('debug', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi',
|
||||
debugMode: true
|
||||
});
|
||||
// ----------
|
||||
QUnit.test('sketchCanvas', function(assert) {
|
||||
var done = assert.async();
|
||||
|
||||
Util.spyOnce(viewer.drawer, 'drawDebugInfo', function() {
|
||||
assert.ok(true, 'drawDebugInfo is called');
|
||||
done();
|
||||
});
|
||||
});
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi',
|
||||
});
|
||||
var drawer = viewer.drawer;
|
||||
|
||||
// ----------
|
||||
QUnit.test('sketchCanvas', function(assert) {
|
||||
var done = assert.async();
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
var drawer = viewer.drawer;
|
||||
// this test only makes sense for canvas drawer
|
||||
if(viewer.drawer.getType() !== 'canvas'){
|
||||
assert.expect(0);
|
||||
done();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-drawn', function noOpacityHandler() {
|
||||
viewer.removeHandler('tile-drawn', noOpacityHandler);
|
||||
assert.equal(drawer.sketchCanvas, null,
|
||||
'The sketch canvas should be null if no decimal opacity is used.');
|
||||
assert.equal(drawer.sketchContext, null,
|
||||
'The sketch context should be null if no decimal opacity is used.');
|
||||
testOpacityDecimal();
|
||||
});
|
||||
|
||||
function testOpacityDecimal() {
|
||||
var tiledImage;
|
||||
viewer.addTiledImage({
|
||||
tileSource: '/test/data/testpattern.dzi',
|
||||
opacity: 0.5,
|
||||
success: function(event) {
|
||||
tiledImage = event.item;
|
||||
}
|
||||
viewer.addHandler('tile-drawn', function noOpacityHandler() {
|
||||
viewer.removeHandler('tile-drawn', noOpacityHandler);
|
||||
assert.equal(drawer.sketchCanvas, null,
|
||||
'The sketch canvas should be null if no decimal opacity is used.');
|
||||
assert.equal(drawer.sketchContext, null,
|
||||
'The sketch context should be null if no decimal opacity is used.');
|
||||
testOpacityDecimal();
|
||||
});
|
||||
|
||||
viewer.addHandler('tile-drawn', function opacityDecimalHandler(event) {
|
||||
if (tiledImage !== event.tiledImage) {
|
||||
return;
|
||||
}
|
||||
viewer.removeHandler('tile-drawn', opacityDecimalHandler);
|
||||
assert.notEqual(drawer.sketchCanvas, null,
|
||||
'The sketch canvas should not be null once a decimal opacity has been used.');
|
||||
assert.notEqual(drawer.sketchContext, null,
|
||||
'The sketch context should not be null once a decimal opacity has been used.');
|
||||
function testOpacityDecimal() {
|
||||
var tiledImage;
|
||||
viewer.addTiledImage({
|
||||
tileSource: '/test/data/testpattern.dzi',
|
||||
opacity: 0.5,
|
||||
success: function(event) {
|
||||
tiledImage = event.item;
|
||||
}
|
||||
});
|
||||
|
||||
viewer.addHandler('tile-drawn', function opacityDecimalHandler(event) {
|
||||
if (tiledImage !== event.tiledImage) {
|
||||
return;
|
||||
}
|
||||
viewer.removeHandler('tile-drawn', opacityDecimalHandler);
|
||||
assert.notEqual(drawer.sketchCanvas, null,
|
||||
'The sketch canvas should not be null once a decimal opacity has been used.');
|
||||
assert.notEqual(drawer.sketchContext, null,
|
||||
'The sketch context should not be null once a decimal opacity has been used.');
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('deprecations', function(assert) {
|
||||
var done = assert.async();
|
||||
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
viewer.world.addHandler('add-item', function() {
|
||||
// no current deprecated methods
|
||||
assert.expect(0);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('deprecations', function(assert) {
|
||||
var done = assert.async();
|
||||
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
viewer.world.addHandler('add-item', function() {
|
||||
Util.testDeprecation(assert, viewer.drawer, 'addOverlay', viewer, 'addOverlay');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'updateOverlay', viewer, 'updateOverlay');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'removeOverlay', viewer, 'removeOverlay');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'clearOverlays', viewer, 'clearOverlays');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'reset', viewer.world, 'resetItems');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'update', viewer.world, 'draw');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'setOpacity', viewer.world.getItemAt(0), 'setOpacity');
|
||||
Util.testDeprecation(assert, viewer.drawer, 'getOpacity', viewer.world.getItemAt(0), 'getOpacity');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -17,10 +17,9 @@
|
||||
} );
|
||||
},
|
||||
afterEach: function () {
|
||||
if ( viewer && viewer.close ) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
} );
|
||||
@ -1155,6 +1154,12 @@
|
||||
// ----------
|
||||
QUnit.test( 'Viewer: event count test with \'tile-drawing\'', function (assert) {
|
||||
var done = assert.async();
|
||||
if(viewer.drawer.getType() !== 'canvas'){
|
||||
assert.expect(0);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
assert.ok(viewer.numberOfHandlers('tile-drawing') === 0,
|
||||
"'tile-drawing' event is empty by default.");
|
||||
|
||||
@ -1162,6 +1167,7 @@
|
||||
viewer.removeHandler( 'tile-drawing', tileDrawing );
|
||||
assert.ok(viewer.numberOfHandlers('tile-drawing') === 0,
|
||||
"'tile-drawing' deleted: count is 0.");
|
||||
|
||||
viewer.close();
|
||||
done();
|
||||
};
|
||||
@ -1185,6 +1191,12 @@
|
||||
|
||||
QUnit.test( 'Viewer: tile-drawing event', function (assert) {
|
||||
var done = assert.async();
|
||||
if(viewer.drawer.getType() !== 'canvas'){
|
||||
assert.expect(0);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
var tileDrawing = function ( event ) {
|
||||
viewer.removeHandler( 'tile-drawing', tileDrawing );
|
||||
assert.ok( event, 'Event handler should be invoked' );
|
||||
|
@ -5,15 +5,23 @@
|
||||
// This module tests whether our various file formats can be opened.
|
||||
// TODO: Add more file formats (with corresponding test data).
|
||||
|
||||
var viewer = null;
|
||||
|
||||
QUnit.module('Formats', {
|
||||
beforeEach: function () {
|
||||
var example = document.createElement("div");
|
||||
example.id = "example";
|
||||
document.getElementById("qunit-fixture").appendChild(example);
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
var viewer = null;
|
||||
|
||||
// ----------
|
||||
var testOpenUrl = function(relativeUrl, assert) {
|
||||
@ -34,12 +42,12 @@
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
assert.ok(true, 'Open event was sent');
|
||||
viewer.addHandler('tile-drawn', tileDrawnHandler);
|
||||
viewer.addHandler('tiled-image-drawn', tileDrawnHandler);
|
||||
};
|
||||
|
||||
var tileDrawnHandler = function(event) {
|
||||
viewer.removeHandler('tile-drawn', tileDrawnHandler);
|
||||
assert.ok(true, 'A tile has been drawn');
|
||||
viewer.removeHandler('tiled-image-drawn', tileDrawnHandler);
|
||||
assert.ok(true, 'A tiled image has been drawn');
|
||||
viewer.addHandler('close', closeHandler);
|
||||
viewer.close();
|
||||
};
|
||||
|
@ -15,8 +15,8 @@
|
||||
testLog.reset();
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
@ -2,275 +2,287 @@
|
||||
|
||||
( function() {
|
||||
var viewer;
|
||||
const drawerTypes = ['webgl','canvas'];
|
||||
drawerTypes.forEach(runDrawerTests);
|
||||
|
||||
QUnit.module( 'Multi-Image', {
|
||||
beforeEach: function() {
|
||||
$( '<div id="example"></div>' ).appendTo( "#qunit-fixture" );
|
||||
function runDrawerTests(drawerType){
|
||||
|
||||
testLog.reset();
|
||||
QUnit.module( 'Multi-Image-'+drawerType, {
|
||||
beforeEach: function() {
|
||||
$( '<div id="example"></div>' ).appendTo( "#qunit-fixture" );
|
||||
|
||||
viewer = OpenSeadragon( {
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
});
|
||||
},
|
||||
afterEach: function() {
|
||||
if ( viewer && viewer.close ) {
|
||||
viewer.close();
|
||||
}
|
||||
testLog.reset();
|
||||
|
||||
viewer = null;
|
||||
$("#example").remove();
|
||||
}
|
||||
} );
|
||||
|
||||
// ----------
|
||||
QUnit.test( 'Multi-image operations', function(assert) {
|
||||
var done = assert.async();
|
||||
assert.expect( 24 );
|
||||
viewer.addHandler( "open", function( ) {
|
||||
assert.equal( 1, viewer.world.getItemCount( ),
|
||||
"One item should be present after opening." );
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [ {
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
} ]
|
||||
viewer = OpenSeadragon( {
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
drawer: drawerType
|
||||
});
|
||||
},
|
||||
afterEach: function() {
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
};
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addFirstItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addFirstItemHandler );
|
||||
var item1 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 2,
|
||||
"2 items should be present after adding a item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 1,
|
||||
"The first added item should have a index of 1" );
|
||||
assert.equal( viewer.world.getItemAt( 1 ), item1,
|
||||
"The item at index 1 should be the first added item." );
|
||||
|
||||
viewer = null;
|
||||
$("#example").remove();
|
||||
}
|
||||
} );
|
||||
|
||||
// ----------
|
||||
QUnit.test( 'Multi-image operations', function(assert) {
|
||||
var done = assert.async();
|
||||
assert.expect( 24 );
|
||||
viewer.addHandler( "open", function( ) {
|
||||
assert.equal( 1, viewer.world.getItemCount( ),
|
||||
"One item should be present after opening." );
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [ {
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
} ]
|
||||
}
|
||||
};
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addSecondItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addSecondItemHandler );
|
||||
var item2 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 3,
|
||||
"3 items should be present after adding a second item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), 2,
|
||||
"If not specified, a item should be added with the highest index." );
|
||||
assert.equal( viewer.world.getItemAt( 2 ), item2,
|
||||
"The item at index 2 should be the second added item." );
|
||||
viewer.world.addHandler( "add-item", function addFirstItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addFirstItemHandler );
|
||||
var item1 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 2,
|
||||
"2 items should be present after adding a item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 1,
|
||||
"The first added item should have a index of 1" );
|
||||
assert.equal( viewer.world.getItemAt( 1 ), item1,
|
||||
"The item at index 1 should be the first added item." );
|
||||
|
||||
viewer.world.addHandler( "item-index-change",
|
||||
function itemIndexChangedHandler( event ) {
|
||||
viewer.world.removeHandler( "item-index-change",
|
||||
itemIndexChangedHandler );
|
||||
assert.equal( event.item, item2,
|
||||
"The item which changed index should be item2" );
|
||||
assert.equal( event.previousIndex, 2, "Previous index should be 2." );
|
||||
assert.equal( event.newIndex, 1, "New index should be 1." );
|
||||
});
|
||||
viewer.world.setItemIndex( item2, 1 );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), 1,
|
||||
"Item2 index should be 1 after setItemIndex." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item1 index should be 2 after setItemIndex." );
|
||||
assert.equal( viewer.world.getItemAt( 1 ), item2,
|
||||
"The item at index 1 should be item2." );
|
||||
assert.equal( viewer.world.getItemAt( 2 ), item1,
|
||||
"The item at index 2 should be item1." );
|
||||
|
||||
options.index = 2;
|
||||
options.tileSource.levels[0].url = "data/CCyan.png";
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addThirdItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addThirdItemHandler );
|
||||
var item3 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 4,
|
||||
"4 items should be present after adding a third item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item3 ), 2,
|
||||
"Item 3 should be added with index 2." );
|
||||
viewer.world.addHandler( "add-item", function addSecondItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addSecondItemHandler );
|
||||
var item2 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 3,
|
||||
"3 items should be present after adding a second item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), 2,
|
||||
"If not specified, a item should be added with the highest index." );
|
||||
assert.equal( viewer.world.getItemAt( 2 ), item2,
|
||||
"The item at index 2 should be the second added item." );
|
||||
|
||||
viewer.world.addHandler( "item-index-change",
|
||||
function itemIndexChangedHandler( event ) {
|
||||
viewer.world.removeHandler( "item-index-change",
|
||||
itemIndexChangedHandler );
|
||||
assert.equal( event.item, item2,
|
||||
"The item which changed index should be item2" );
|
||||
assert.equal( event.previousIndex, 2, "Previous index should be 2." );
|
||||
assert.equal( event.newIndex, 1, "New index should be 1." );
|
||||
});
|
||||
viewer.world.setItemIndex( item2, 1 );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), 1,
|
||||
"Item 2 should stay at index 1." );
|
||||
"Item2 index should be 1 after setItemIndex." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item1 index should be 2 after setItemIndex." );
|
||||
assert.equal( viewer.world.getItemAt( 1 ), item2,
|
||||
"The item at index 1 should be item2." );
|
||||
assert.equal( viewer.world.getItemAt( 2 ), item1,
|
||||
"The item at index 2 should be item1." );
|
||||
|
||||
options.index = 2;
|
||||
options.replace = true;
|
||||
options.tileSource.levels[0].url = "data/CCyan.png";
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function replaceAddItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", replaceAddItemHandler );
|
||||
var item4 = event.item;
|
||||
viewer.world.addHandler( "add-item", function addThirdItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addThirdItemHandler );
|
||||
var item3 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 4,
|
||||
"4 items should still be present after replacing the second item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item4 ), 2,
|
||||
"Item 4 should be added with index 2." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item3 ), -1,
|
||||
"Item 3 should be at index -1." );
|
||||
"4 items should be present after adding a third item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item3 ), 2,
|
||||
"Item 3 should be added with index 2." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), 1,
|
||||
"Item 2 should stay at index 1." );
|
||||
|
||||
viewer.world.addHandler( "remove-item", function removeItemHandler( event ) {
|
||||
viewer.world.removeHandler( "remove-item", removeItemHandler );
|
||||
options.index = 2;
|
||||
options.replace = true;
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function replaceAddItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", replaceAddItemHandler );
|
||||
var item4 = event.item;
|
||||
assert.equal( viewer.world.getItemCount( ), 4,
|
||||
"4 items should still be present after replacing the second item." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item4 ), 2,
|
||||
"Item 4 should be added with index 2." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item3 ), -1,
|
||||
"Item 3 should be at index -1." );
|
||||
|
||||
assert.equal( item2, event.item, "Removed item should be item2." );
|
||||
viewer.world.addHandler( "remove-item", function removeItemHandler( event ) {
|
||||
viewer.world.removeHandler( "remove-item", removeItemHandler );
|
||||
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item 1 should be at index 2." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), -1,
|
||||
"Item 2 should be at index -1." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item4 ), 1,
|
||||
"Item 4 should be at index 1." );
|
||||
assert.equal( item2, event.item, "Removed item should be item2." );
|
||||
|
||||
done();
|
||||
assert.equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item 1 should be at index 2." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item2 ), -1,
|
||||
"Item 2 should be at index -1." );
|
||||
assert.equal( viewer.world.getIndexOfItem( item4 ), 1,
|
||||
"Item 4 should be at index 1." );
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
viewer.world.removeItem( item2 );
|
||||
});
|
||||
|
||||
viewer.world.removeItem( item2 );
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test( 'Sequences as items', function(assert) {
|
||||
var done = assert.async();
|
||||
var options = {
|
||||
tileSource: [{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}, {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/BBlue.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
viewer.addHandler( "open", function openHandler() {
|
||||
viewer.removeHandler( "open", openHandler );
|
||||
|
||||
viewer.addHandler( "add-item-failed",
|
||||
function addItemFailedHandler( event ) {
|
||||
viewer.removeHandler( "add-item-failed", addItemFailedHandler );
|
||||
assert.equal( event.message, "[Viewer.addTiledImage] Sequences can not be added; add them one at a time instead." );
|
||||
assert.equal( event.options, options, "Item failed event should give the options." );
|
||||
done();
|
||||
} );
|
||||
viewer.addTiledImage( options );
|
||||
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('items are added in order', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler('open', function(event) {
|
||||
assert.equal(viewer.world.getItemAt(0).getContentSize().y, 2000, 'first image is tall');
|
||||
assert.equal(viewer.world.getItemAt(0).getBounds().width, 4, 'first image has 4 width');
|
||||
assert.equal(viewer.world.getItemAt(1).getContentSize().x, 2000, 'second image is wide');
|
||||
assert.equal(viewer.world.getItemAt(1).getBounds().width, 2, 'second image has 2 width');
|
||||
done();
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
viewer.open([
|
||||
{
|
||||
tileSource: '/test/data/tall.dzi',
|
||||
width: 4
|
||||
}, {
|
||||
tileSource: '/test/data/wide.dzi',
|
||||
width: 2
|
||||
}
|
||||
]);
|
||||
});
|
||||
// ----------
|
||||
QUnit.test( 'Sequences as items', function(assert) {
|
||||
var done = assert.async();
|
||||
var options = {
|
||||
tileSource: [{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}, {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/BBlue.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
QUnit.test('Viewer.addSimpleImage', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler("open", function openHandler() {
|
||||
viewer.removeHandler("open", openHandler);
|
||||
viewer.addHandler( "open", function openHandler() {
|
||||
viewer.removeHandler( "open", openHandler );
|
||||
|
||||
viewer.world.addHandler('add-item', function itemAdded(event) {
|
||||
viewer.world.removeHandler('add-item', itemAdded);
|
||||
assert.equal(event.item.opacity, 0.5,
|
||||
'Opacity option should be set when using addSimpleImage');
|
||||
viewer.addHandler( "add-item-failed",
|
||||
function addItemFailedHandler( event ) {
|
||||
viewer.removeHandler( "add-item-failed", addItemFailedHandler );
|
||||
assert.equal( event.message, "[Viewer.addTiledImage] Sequences can not be added; add them one at a time instead." );
|
||||
assert.equal( event.options, options, "Item failed event should give the options." );
|
||||
done();
|
||||
} );
|
||||
viewer.addTiledImage( options );
|
||||
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
// ----------
|
||||
QUnit.test('items are added in order', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler('open', function(event) {
|
||||
assert.equal(viewer.world.getItemAt(0).getContentSize().y, 2000, 'first image is tall');
|
||||
assert.equal(viewer.world.getItemAt(0).getBounds().width, 4, 'first image has 4 width');
|
||||
assert.equal(viewer.world.getItemAt(1).getContentSize().x, 2000, 'second image is wide');
|
||||
assert.equal(viewer.world.getItemAt(1).getBounds().width, 2, 'second image has 2 width');
|
||||
done();
|
||||
});
|
||||
|
||||
viewer.addSimpleImage({
|
||||
url: '/test/data/A.png',
|
||||
opacity: 0.5
|
||||
});
|
||||
viewer.open([
|
||||
{
|
||||
tileSource: '/test/data/tall.dzi',
|
||||
width: 4
|
||||
}, {
|
||||
tileSource: '/test/data/wide.dzi',
|
||||
width: 2
|
||||
}
|
||||
]);
|
||||
});
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
QUnit.test('Transparent image on top of others', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
QUnit.test('Viewer.addSimpleImage', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler("open", function openHandler() {
|
||||
viewer.removeHandler("open", openHandler);
|
||||
|
||||
var density = OpenSeadragon.pixelDensityRatio;
|
||||
|
||||
viewer.addHandler('open', function() {
|
||||
var firstImage = viewer.world.getItemAt(0);
|
||||
firstImage.addHandler('fully-loaded-change', function() {
|
||||
var imageData = viewer.drawer.context.getImageData(0, 0,
|
||||
500 * density, 500 * density);
|
||||
|
||||
// Pixel 250,250 will be in the hole of the A
|
||||
var expectedVal = getPixelValue(imageData, 250 * density, 250 * density);
|
||||
|
||||
assert.notEqual(expectedVal.r, 0, 'Red channel should not be 0');
|
||||
assert.notEqual(expectedVal.g, 0, 'Green channel should not be 0');
|
||||
assert.notEqual(expectedVal.b, 0, 'Blue channel should not be 0');
|
||||
assert.notEqual(expectedVal.a, 0, 'Alpha channel should not be 0');
|
||||
viewer.world.addHandler('add-item', function itemAdded(event) {
|
||||
viewer.world.removeHandler('add-item', itemAdded);
|
||||
assert.equal(event.item.opacity, 0.5,
|
||||
'Opacity option should be set when using addSimpleImage');
|
||||
done();
|
||||
});
|
||||
|
||||
viewer.addSimpleImage({
|
||||
url: '/test/data/A.png',
|
||||
success: function() {
|
||||
var secondImage = viewer.world.getItemAt(1);
|
||||
secondImage.addHandler('fully-loaded-change', function() {
|
||||
var imageData = viewer.drawer.context.getImageData(0, 0, 500 * density, 500 * density);
|
||||
var actualVal = getPixelValue(imageData, 250 * density, 250 * density);
|
||||
|
||||
assert.equal(actualVal.r, expectedVal.r,
|
||||
'Red channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.g, expectedVal.g,
|
||||
'Green channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.b, expectedVal.b,
|
||||
'Blue channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.a, expectedVal.a,
|
||||
'Alpha channel should not change in transparent part of the A');
|
||||
|
||||
var onAVal = getPixelValue(imageData, 333 * density, 250 * density);
|
||||
assert.equal(onAVal.r, 0, 'Red channel should be null on the A');
|
||||
assert.equal(onAVal.g, 0, 'Green channel should be null on the A');
|
||||
assert.equal(onAVal.b, 0, 'Blue channel should be null on the A');
|
||||
assert.equal(onAVal.a, 255, 'Alpha channel should be 255 on the A');
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
opacity: 0.5
|
||||
});
|
||||
});
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
function getPixelValue(imageData, x, y) {
|
||||
var offset = 4 * (y * imageData.width + x);
|
||||
return {
|
||||
r: imageData.data[offset],
|
||||
g: imageData.data[offset + 1],
|
||||
b: imageData.data[offset + 2],
|
||||
a: imageData.data[offset + 3]
|
||||
};
|
||||
}
|
||||
});
|
||||
QUnit.test('Transparent image on top of others', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
|
||||
var density = OpenSeadragon.pixelDensityRatio;
|
||||
|
||||
viewer.addHandler('open', function() {
|
||||
var firstImage = viewer.world.getItemAt(0);
|
||||
firstImage.addHandler('fully-loaded-change', function() {
|
||||
viewer.addOnceHandler('update-viewport', function(){
|
||||
var imageData = viewer.drawer.context.getImageData(0, 0,
|
||||
500 * density, 500 * density);
|
||||
|
||||
// Pixel 250,250 will be in the hole of the A
|
||||
var expectedVal = getPixelValue(imageData, 250 * density, 250 * density);
|
||||
|
||||
assert.notEqual(expectedVal.r, 0, 'Red channel should not be 0');
|
||||
assert.notEqual(expectedVal.g, 0, 'Green channel should not be 0');
|
||||
assert.notEqual(expectedVal.b, 0, 'Blue channel should not be 0');
|
||||
assert.notEqual(expectedVal.a, 0, 'Alpha channel should not be 0');
|
||||
|
||||
viewer.addSimpleImage({
|
||||
url: '/test/data/A.png',
|
||||
success: function() {
|
||||
var secondImage = viewer.world.getItemAt(1);
|
||||
secondImage.addHandler('fully-loaded-change', function() {
|
||||
viewer.addOnceHandler('update-viewport',function(){
|
||||
var imageData = viewer.drawer.context.getImageData(0, 0, 500 * density, 500 * density);
|
||||
var actualVal = getPixelValue(imageData, 250 * density, 250 * density);
|
||||
|
||||
assert.equal(actualVal.r, expectedVal.r,
|
||||
'Red channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.g, expectedVal.g,
|
||||
'Green channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.b, expectedVal.b,
|
||||
'Blue channel should not change in transparent part of the A');
|
||||
assert.equal(actualVal.a, expectedVal.a,
|
||||
'Alpha channel should not change in transparent part of the A');
|
||||
|
||||
var onAVal = getPixelValue(imageData, 333 * density, 250 * density);
|
||||
assert.equal(onAVal.r, 0, 'Red channel should be null on the A');
|
||||
assert.equal(onAVal.g, 0, 'Green channel should be null on the A');
|
||||
assert.equal(onAVal.b, 0, 'Blue channel should be null on the A');
|
||||
assert.equal(onAVal.a, 255, 'Alpha channel should be 255 on the A');
|
||||
|
||||
done();
|
||||
});
|
||||
// trigger a redraw so the event fires
|
||||
firstImage.redraw();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getPixelValue(imageData, x, y) {
|
||||
var offset = 4 * (y * imageData.width + x);
|
||||
return {
|
||||
r: imageData.data[offset],
|
||||
g: imageData.data[offset + 1],
|
||||
b: imageData.data[offset + 2],
|
||||
a: imageData.data[offset + 3]
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -41,6 +41,12 @@
|
||||
}
|
||||
|
||||
resetTestVariables();
|
||||
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -16,12 +16,22 @@
|
||||
},
|
||||
afterEach: function() {
|
||||
resetTestVariables();
|
||||
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
var resetTestVariables = function() {
|
||||
if (viewer) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
let errors = viewer.drawer._numGlMaxTextureErrors;
|
||||
if(errors > 0){
|
||||
console.log('Number of times MAX_TEXTURE_IMAGE_UNITS had a bad value:', errors);
|
||||
}
|
||||
viewer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,8 +10,8 @@
|
||||
testLog.reset();
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* global QUnit, $, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
let viewer;
|
||||
|
||||
let precision = 0.000000001;
|
||||
|
||||
QUnit.module('TiledImage', {
|
||||
beforeEach: function() {
|
||||
@ -13,12 +15,13 @@
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
drawer: 'canvas', // always use canvas drawer for these tests
|
||||
});
|
||||
},
|
||||
afterEach: function() {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
@ -119,8 +122,8 @@
|
||||
|
||||
viewer.addHandler('animation-finish', function animationHandler() {
|
||||
viewer.removeHandler('animation-finish', animationHandler);
|
||||
assert.propEqual(image.getBounds(), new OpenSeadragon.Rect(1, 2, 3, 3), 'target bounds after animation');
|
||||
assert.propEqual(image.getBounds(true), new OpenSeadragon.Rect(1, 2, 3, 3), 'current bounds after animation');
|
||||
Util.assertRectangleEquals(assert, new OpenSeadragon.Rect(1, 2, 3, 3), image.getBounds(), precision, 'target bounds after animation');
|
||||
Util.assertRectangleEquals(assert, new OpenSeadragon.Rect(1, 2, 3, 3), image.getBounds(true), precision, 'target bounds after animation');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -132,6 +135,7 @@
|
||||
QUnit.test('update', function(assert) {
|
||||
var done = assert.async();
|
||||
var handlerCount = 0;
|
||||
let expectedHandlers = 4;
|
||||
|
||||
viewer.addHandler('open', function(event) {
|
||||
var image = viewer.world.getItemAt(0);
|
||||
@ -160,6 +164,7 @@
|
||||
assert.ok(event.tile, 'update-tile event includes tile');
|
||||
});
|
||||
|
||||
|
||||
viewer.addHandler('tile-drawing', function tileDrawingHandler(event) {
|
||||
viewer.removeHandler('tile-drawing', tileDrawingHandler);
|
||||
handlerCount++;
|
||||
@ -170,18 +175,20 @@
|
||||
assert.ok(event.rendered, 'tile-drawing event includes a rendered');
|
||||
});
|
||||
|
||||
viewer.addHandler('tile-drawn', function tileDrawnHandler(event) {
|
||||
viewer.removeHandler('tile-drawn', tileDrawnHandler);
|
||||
handlerCount++;
|
||||
assert.equal(event.eventSource, viewer, 'sender of tile-drawn event was viewer');
|
||||
assert.equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
assert.ok(event.tile, 'tile-drawn event includes tile');
|
||||
|
||||
assert.equal(handlerCount, 4, 'correct number of handlers called');
|
||||
|
||||
viewer.addHandler('tiled-image-drawn', function tileDrawnHandler(event) {
|
||||
viewer.removeHandler('tiled-image-drawn', tileDrawnHandler);
|
||||
handlerCount++;
|
||||
assert.equal(event.eventSource, viewer, 'sender of tiled-image-drawn event was viewer');
|
||||
assert.equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
assert.ok(event.tiles, 'tiled-image-drawn event includes tiles');
|
||||
|
||||
assert.equal(handlerCount, expectedHandlers, 'correct number of handlers called');
|
||||
done();
|
||||
});
|
||||
|
||||
image.draw();
|
||||
viewer.drawer.draw( [ image ] );
|
||||
});
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
@ -190,14 +197,14 @@
|
||||
// ----------
|
||||
QUnit.test('reset', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler('tile-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler);
|
||||
assert.ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tile-drawn');
|
||||
viewer.addHandler('tiled-image-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tiled-image-drawn', updateHandler);
|
||||
assert.ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tiled-image-drawn');
|
||||
viewer.world.getItemAt(0).reset();
|
||||
assert.equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after reset');
|
||||
|
||||
viewer.addHandler('tile-drawn', function updateHandler2() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler2);
|
||||
viewer.addHandler('tiled-image-drawn', function updateHandler2() {
|
||||
viewer.removeHandler('tiled-image-drawn', updateHandler2);
|
||||
assert.ok(viewer.tileCache.numTilesLoaded() > 0, 'more tiles load');
|
||||
viewer.world.getItemAt(0).destroy();
|
||||
assert.equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after destroy');
|
||||
@ -225,7 +232,7 @@
|
||||
image.setClip(clip);
|
||||
assert.propEqual(image.getClip(), clip, 'clip is set correctly');
|
||||
|
||||
Util.spyOnce(viewer.drawer, 'setClip', function(rect) {
|
||||
Util.spyOnce(viewer.drawer, '_setClip', function(rect) {
|
||||
var homeBounds = viewer.viewport.getHomeBounds();
|
||||
var canvasClip = viewer.drawer
|
||||
.viewportToDrawerRectangle(homeBounds);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
(function() {
|
||||
var ASSERT = null;
|
||||
var done = null;
|
||||
var DYNAMIC_URL = "";
|
||||
var viewer = null;
|
||||
var OriginalAjax = OpenSeadragon.makeAjaxRequest;
|
||||
@ -16,9 +17,10 @@
|
||||
/**
|
||||
* Set up shared variables for test
|
||||
*/
|
||||
var configure = function(assert, url) {
|
||||
var configure = function(assert, url, assertAsyncDone) {
|
||||
ASSERT = assert;
|
||||
DYNAMIC_URL = url;
|
||||
done = assertAsyncDone;
|
||||
firstUrlPromise = new Promise(resolve => {
|
||||
firstUrlPromiseResolver = () => {
|
||||
isFirstUrlPromiseResolved = true;
|
||||
@ -94,6 +96,7 @@
|
||||
// Otherwise close viewer
|
||||
if (isFirstUrlPromiseResolved) {
|
||||
viewer.close();
|
||||
done();
|
||||
} else {
|
||||
firstUrlPromiseResolver();
|
||||
}
|
||||
@ -119,7 +122,11 @@
|
||||
|
||||
OpenSeadragon.extend( Tile.prototype, OpenSeadragon.Tile.prototype, {
|
||||
getUrl: function() {
|
||||
ASSERT.ok(true, 'Tile.getUrl called');
|
||||
// if ASSERT is still truthy, call ASSERT.ok. If the viewer
|
||||
// has already been destroyed and ASSERT has set to null, ignore this
|
||||
if(ASSERT){
|
||||
ASSERT.ok(true, 'Tile.getUrl called');
|
||||
}
|
||||
return OriginalTile.prototype.getUrl.apply(this);
|
||||
}
|
||||
});
|
||||
@ -129,9 +136,10 @@
|
||||
afterEach: function () {
|
||||
ASSERT = null;
|
||||
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
||||
OpenSeadragon.makeAjaxRequest = OriginalAjax;
|
||||
@ -188,11 +196,12 @@
|
||||
|
||||
// ----------
|
||||
QUnit.test('TileSource.getTileUrl supports returning a function', function(assert) {
|
||||
configure(assert, 'dynamicUrl');
|
||||
const done = assert.async();
|
||||
configure(assert, 'dynamicUrl', done);
|
||||
const viewer = testUrlCall('dynamicUrl');
|
||||
firstUrlPromise.then(() => {
|
||||
// after querying with first dynamic url, update the url and trigger new request
|
||||
DYNAMIC_URL = 'dyanmicUrl2';
|
||||
DYNAMIC_URL = 'dynamicUrl2';
|
||||
delete viewer.world.getItemAt(0).tilesMatrix[1][0][0];
|
||||
})
|
||||
});
|
||||
|
@ -19,8 +19,8 @@
|
||||
});
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
|
@ -25,10 +25,10 @@
|
||||
},
|
||||
|
||||
afterEach: function () {
|
||||
if (viewer1 && viewer1.destroy) {
|
||||
if (viewer1){
|
||||
viewer1.destroy();
|
||||
}
|
||||
if (viewer2 && viewer2.destroy) {
|
||||
if (viewer2){
|
||||
viewer2.destroy();
|
||||
}
|
||||
viewer1 = viewer2 = null;
|
||||
|
@ -21,8 +21,8 @@
|
||||
});
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
@ -80,6 +80,9 @@
|
||||
springStiffness: SPRING_STIFFNESS
|
||||
};
|
||||
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
viewerConfig[config.property] = level;
|
||||
viewer = OpenSeadragon(viewerConfig);
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
@ -96,6 +99,10 @@
|
||||
};
|
||||
|
||||
viewerConfig[config.property] = level;
|
||||
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
viewer = OpenSeadragon(viewerConfig);
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
viewer.open(DZI_PATH);
|
||||
@ -372,6 +379,9 @@
|
||||
);
|
||||
i++;
|
||||
if (i < testZoomLevels.length) {
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
viewer = OpenSeadragon({
|
||||
id: VIEWER_ID,
|
||||
prefixUrl: PREFIX_URL,
|
||||
@ -385,6 +395,9 @@
|
||||
done();
|
||||
}
|
||||
};
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
viewer = OpenSeadragon({
|
||||
id: VIEWER_ID,
|
||||
prefixUrl: PREFIX_URL,
|
||||
|
@ -16,8 +16,8 @@
|
||||
});
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
if (viewer){
|
||||
viewer.destroy();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
@ -159,7 +159,7 @@
|
||||
handlerCount++;
|
||||
});
|
||||
|
||||
viewer.world.draw();
|
||||
viewer.world.update();
|
||||
|
||||
assert.equal(handlerCount, 1, 'correct number of handlers called');
|
||||
done();
|
||||
@ -173,9 +173,9 @@
|
||||
// ----------
|
||||
QUnit.test('resetItems', function(assert) {
|
||||
var done = assert.async();
|
||||
viewer.addHandler('tile-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler);
|
||||
assert.ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tile-drawn');
|
||||
viewer.addHandler('tiled-image-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tiled-image-drawn', updateHandler);
|
||||
assert.ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tiled-image-drawn');
|
||||
viewer.world.resetItems();
|
||||
assert.equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after reset');
|
||||
done();
|
||||
|
Loading…
Reference in New Issue
Block a user