2017-08-01 00:05:18 +03:00
|
|
|
/*
|
2013-05-14 08:00:24 +04:00
|
|
|
* OpenSeadragon - Drawer
|
2013-05-01 08:46:16 +04:00
|
|
|
*
|
|
|
|
* Copyright (C) 2009 CodePlex Foundation
|
2013-05-14 07:32:09 +04:00
|
|
|
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
2013-05-01 08:46:16 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
(function( $ ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-01-25 23:14:02 +04:00
|
|
|
/**
|
2013-11-16 10:19:53 +04:00
|
|
|
* @class Drawer
|
|
|
|
* @memberof OpenSeadragon
|
2014-11-04 22:53:39 +03:00
|
|
|
* @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.
|
2012-01-25 23:14:02 +04:00
|
|
|
*/
|
2012-03-01 17:38:15 +04:00
|
|
|
$.Drawer = function( options ) {
|
2014-08-07 00:48:18 +04:00
|
|
|
|
|
|
|
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
//backward compatibility for positional args while prefering more
|
2012-03-01 17:38:15 +04:00
|
|
|
//idiomatic javascript options object as the only argument
|
2014-08-12 04:04:20 +04:00
|
|
|
var args = arguments;
|
2012-03-16 19:36:28 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if( !$.isPlainObject( options ) ){
|
|
|
|
options = {
|
2013-11-25 20:48:44 +04:00
|
|
|
source: args[ 0 ], // Reference to Viewer tile source.
|
|
|
|
viewport: args[ 1 ], // Reference to Viewer viewport.
|
|
|
|
element: args[ 2 ] // Parent element.
|
2012-03-01 17:38:15 +04:00
|
|
|
};
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2014-08-12 04:04:20 +04:00
|
|
|
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
|
|
|
|
$.console.assert( options.element, "[Drawer] options.element is required" );
|
|
|
|
|
2014-08-07 00:48:18 +04:00
|
|
|
if ( options.source ) {
|
|
|
|
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
|
2014-08-01 02:54:20 +04:00
|
|
|
}
|
|
|
|
|
2014-08-12 04:04:20 +04:00
|
|
|
this.viewer = options.viewer;
|
2014-08-13 03:15:17 +04:00
|
|
|
this.viewport = options.viewport;
|
2017-07-26 17:57:17 +03:00
|
|
|
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
2015-04-21 02:25:36 +03:00
|
|
|
if (options.opacity) {
|
|
|
|
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
|
|
|
|
}
|
2012-03-01 17:38:15 +04:00
|
|
|
|
2013-11-02 00:02:28 +04:00
|
|
|
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
|
2013-11-25 20:48:44 +04:00
|
|
|
/**
|
|
|
|
* 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#
|
|
|
|
*/
|
2014-08-12 04:04:20 +04:00
|
|
|
this.container = $.getElement( options.element );
|
2013-11-25 20:48:44 +04:00
|
|
|
/**
|
|
|
|
* 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#
|
|
|
|
*/
|
2013-11-02 00:02:28 +04:00
|
|
|
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
|
2013-11-25 20:48:44 +04:00
|
|
|
/**
|
|
|
|
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
|
|
|
|
* @member {Object} context
|
|
|
|
* @memberof OpenSeadragon.Drawer#
|
|
|
|
*/
|
2013-11-02 00:02:28 +04:00
|
|
|
this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
|
2014-07-22 22:13:22 +04:00
|
|
|
|
2015-04-19 23:10:37 +03:00
|
|
|
/**
|
|
|
|
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
2015-04-21 02:25:36 +03:00
|
|
|
* to the main canvas due to opacity. Lazily initialized.
|
2015-04-19 23:10:37 +03:00
|
|
|
*/
|
2015-04-21 02:25:36 +03:00
|
|
|
this.sketchCanvas = null;
|
|
|
|
this.sketchContext = null;
|
2015-04-19 23:10:37 +03:00
|
|
|
|
2013-11-25 20:48:44 +04:00
|
|
|
/**
|
|
|
|
* @member {Element} element
|
|
|
|
* @memberof OpenSeadragon.Drawer#
|
|
|
|
* @deprecated Alias for {@link OpenSeadragon.Drawer#container}.
|
|
|
|
*/
|
2012-03-01 17:38:15 +04:00
|
|
|
this.element = this.container;
|
|
|
|
|
2013-08-15 23:54:32 +04:00
|
|
|
// 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';
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2015-01-29 19:19:49 +03:00
|
|
|
// 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();
|
2015-04-21 02:25:36 +03:00
|
|
|
this.canvas.width = viewportSize.x;
|
|
|
|
this.canvas.height = viewportSize.y;
|
2015-01-29 19:19:49 +03:00
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
this.canvas.style.width = "100%";
|
|
|
|
this.canvas.style.height = "100%";
|
|
|
|
this.canvas.style.position = "absolute";
|
2013-12-09 18:26:36 +04:00
|
|
|
$.setElementOpacity( this.canvas, this.opacity, true );
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-01-12 03:22:13 +04:00
|
|
|
// explicit left-align
|
|
|
|
this.container.style.textAlign = "left";
|
2012-02-01 00:59:09 +04:00
|
|
|
this.container.appendChild( this.canvas );
|
2011-12-06 07:50:25 +04:00
|
|
|
};
|
|
|
|
|
2016-01-25 00:09:18 +03:00
|
|
|
/** @lends OpenSeadragon.Drawer.prototype */
|
|
|
|
$.Drawer.prototype = {
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2013-07-31 11:01:48 +04:00
|
|
|
addOverlay: function( element, location, placement, onDraw ) {
|
2014-03-01 17:32:38 +04:00
|
|
|
$.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
|
|
|
|
this.viewer.addOverlay( element, location, placement, onDraw );
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
updateOverlay: function( element, location, placement ) {
|
2014-03-01 17:32:38 +04:00
|
|
|
$.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
|
|
|
|
this.viewer.updateOverlay( element, location, placement );
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2012-02-01 00:59:09 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
removeOverlay: function( element ) {
|
2014-03-01 17:32:38 +04:00
|
|
|
$.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
|
2014-03-26 23:28:35 +04:00
|
|
|
this.viewer.removeOverlay( element );
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
clearOverlays: function() {
|
2014-03-01 17:32:38 +04:00
|
|
|
$.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
|
|
|
|
this.viewer.clearOverlays();
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2012-02-01 00:59:09 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2013-12-09 18:26:36 +04:00
|
|
|
/**
|
|
|
|
* Set the opacity of the drawer.
|
|
|
|
* @param {Number} opacity
|
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
|
|
|
*/
|
|
|
|
setOpacity: function( opacity ) {
|
2015-04-21 02:25:36 +03:00
|
|
|
$.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 );
|
|
|
|
}
|
2013-12-09 18:26:36 +04:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the opacity of the drawer.
|
|
|
|
* @returns {Number}
|
|
|
|
*/
|
|
|
|
getOpacity: function() {
|
2015-04-21 02:25:36 +03:00
|
|
|
$.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;
|
2013-12-09 18:26:36 +04:00
|
|
|
},
|
2014-08-12 04:04:20 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
needsUpdate: function() {
|
2014-12-02 22:44:02 +03:00
|
|
|
$.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
|
|
|
|
return this.viewer.world.needsDraw();
|
2012-02-01 00:59:09 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
numTilesLoaded: function() {
|
2014-11-21 03:02:02 +03:00
|
|
|
$.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
|
|
|
|
return this.viewer.tileCache.numTilesLoaded();
|
2012-02-01 00:59:09 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
reset: function() {
|
2014-11-21 03:02:02 +03:00
|
|
|
$.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
|
|
|
|
this.viewer.world.resetItems();
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// deprecated
|
2012-02-01 00:59:09 +04:00
|
|
|
update: function() {
|
2014-12-02 22:44:02 +03:00
|
|
|
$.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
|
2014-11-21 03:02:02 +03:00
|
|
|
this.clear();
|
2014-12-02 22:44:02 +03:00
|
|
|
this.viewer.world.draw();
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2013-11-25 20:48:44 +04:00
|
|
|
/**
|
|
|
|
* @return {Boolean} True if rotation is supported.
|
|
|
|
*/
|
2013-08-16 21:32:21 +04:00
|
|
|
canRotate: function() {
|
2013-11-02 00:02:28 +04:00
|
|
|
return this.useCanvas;
|
2014-06-18 04:26:10 +04:00
|
|
|
},
|
2014-06-18 21:35:23 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2015-04-21 02:25:36 +03:00
|
|
|
this.sketchCanvas = null;
|
|
|
|
this.sketchContext = null;
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
/**
|
|
|
|
* Clears the Drawer so it's ready to draw another frame.
|
|
|
|
*/
|
2015-04-23 01:30:49 +03:00
|
|
|
clear: function() {
|
2014-08-07 00:48:18 +04:00
|
|
|
this.canvas.innerHTML = "";
|
|
|
|
if ( this.useCanvas ) {
|
2015-01-29 19:19:49 +03:00
|
|
|
var viewportSize = this._calculateCanvasSize();
|
2014-08-07 00:48:18 +04:00
|
|
|
if( this.canvas.width != viewportSize.x ||
|
|
|
|
this.canvas.height != viewportSize.y ) {
|
|
|
|
this.canvas.width = viewportSize.x;
|
|
|
|
this.canvas.height = viewportSize.y;
|
2015-04-21 02:25:36 +03:00
|
|
|
if ( this.sketchCanvas !== null ) {
|
2016-01-24 02:40:53 +03:00
|
|
|
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
|
|
|
this.sketchCanvas.width = sketchCanvasSize.x;
|
|
|
|
this.sketchCanvas.height = sketchCanvasSize.y;
|
2015-04-21 02:25:36 +03:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2015-04-23 01:30:49 +03:00
|
|
|
this._clear();
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2016-04-27 18:08:44 +03:00
|
|
|
_clear: function (useSketch, bounds) {
|
|
|
|
if (!this.useCanvas) {
|
2015-04-23 01:30:49 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-04-27 18:08:44 +03:00
|
|
|
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);
|
|
|
|
}
|
2015-04-23 01:30:49 +03:00
|
|
|
},
|
|
|
|
|
2015-04-09 20:57:28 +03:00
|
|
|
/**
|
2015-12-12 03:07:23 +03:00
|
|
|
* Scale from OpenSeadragon viewer rectangle to drawer rectangle
|
|
|
|
* (ignoring rotation)
|
2015-04-09 20:57:28 +03:00
|
|
|
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
|
2015-04-10 17:20:54 +03:00
|
|
|
* @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
|
2015-04-09 20:57:28 +03:00
|
|
|
*/
|
|
|
|
viewportToDrawerRectangle: function(rectangle) {
|
2015-12-23 02:19:22 +03:00
|
|
|
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
|
|
|
|
var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
|
2015-04-09 20:57:28 +03:00
|
|
|
|
|
|
|
return new $.Rect(
|
|
|
|
topLeft.x * $.pixelDensityRatio,
|
|
|
|
topLeft.y * $.pixelDensityRatio,
|
2017-01-08 17:52:57 +03:00
|
|
|
size.x * $.pixelDensityRatio,
|
|
|
|
size.y * $.pixelDensityRatio
|
2015-04-09 20:57:28 +03:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
/**
|
|
|
|
* Draws the given tile.
|
|
|
|
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
2014-11-25 00:25:20 +03:00
|
|
|
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
|
|
|
* drawingHandler({context, tile, rendered})
|
2015-04-19 23:10:37 +03:00
|
|
|
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
2014-11-25 00:25:20 +03:00
|
|
|
* where <code>rendered</code> is the context with the pre-drawn image.
|
2015-11-06 09:55:30 +03:00
|
|
|
* @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
|
2014-11-04 22:53:39 +03:00
|
|
|
*/
|
2015-12-12 03:07:23 +03:00
|
|
|
drawTile: function(tile, drawingHandler, useSketch, scale, translate) {
|
2014-11-25 00:25:20 +03:00
|
|
|
$.console.assert(tile, '[Drawer.drawTile] tile is required');
|
|
|
|
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
|
|
|
|
|
2015-12-12 03:07:23 +03:00
|
|
|
if (this.useCanvas) {
|
|
|
|
var context = this._getContext(useSketch);
|
2015-11-05 17:19:56 +03:00
|
|
|
scale = scale || 1;
|
2015-12-12 03:07:23 +03:00
|
|
|
tile.drawCanvas(context, drawingHandler, scale, translate);
|
2014-08-07 00:48:18 +04:00
|
|
|
} else {
|
|
|
|
tile.drawHTML( this.canvas );
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
2013-02-02 00:18:53 +04:00
|
|
|
|
2015-04-21 02:25:36 +03:00
|
|
|
_getContext: function( useSketch ) {
|
|
|
|
var context = this.context;
|
|
|
|
if ( useSketch ) {
|
|
|
|
if (this.sketchCanvas === null) {
|
|
|
|
this.sketchCanvas = document.createElement( "canvas" );
|
2016-01-24 02:40:53 +03:00
|
|
|
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
|
|
|
this.sketchCanvas.width = sketchCanvasSize.x;
|
|
|
|
this.sketchCanvas.height = sketchCanvasSize.y;
|
2015-04-21 02:25:36 +03:00
|
|
|
this.sketchContext = this.sketchCanvas.getContext( "2d" );
|
2016-01-26 20:04:34 +03:00
|
|
|
|
|
|
|
// 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) {
|
2016-01-27 03:30:09 +03:00
|
|
|
var self = this;
|
2017-01-08 13:13:32 +03:00
|
|
|
this.viewer.addHandler('rotate', function resizeSketchCanvas() {
|
|
|
|
if (self.viewport.getRotation() === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self.viewer.removeHandler('rotate', resizeSketchCanvas);
|
2016-01-27 03:30:09 +03:00
|
|
|
var sketchCanvasSize = self._calculateSketchCanvasSize();
|
|
|
|
self.sketchCanvas.width = sketchCanvasSize.x;
|
|
|
|
self.sketchCanvas.height = sketchCanvasSize.y;
|
2016-01-26 20:04:34 +03:00
|
|
|
});
|
|
|
|
}
|
2015-04-21 02:25:36 +03:00
|
|
|
}
|
|
|
|
context = this.sketchContext;
|
|
|
|
}
|
|
|
|
return context;
|
|
|
|
},
|
|
|
|
|
2015-03-18 20:03:44 +03:00
|
|
|
// private
|
2015-04-21 02:25:36 +03:00
|
|
|
saveContext: function( useSketch ) {
|
2015-03-18 20:03:44 +03:00
|
|
|
if (!this.useCanvas) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-21 02:25:36 +03:00
|
|
|
this._getContext( useSketch ).save();
|
2015-03-18 20:03:44 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// private
|
2015-04-21 02:25:36 +03:00
|
|
|
restoreContext: function( useSketch ) {
|
2015-03-18 20:03:44 +03:00
|
|
|
if (!this.useCanvas) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-21 02:25:36 +03:00
|
|
|
this._getContext( useSketch ).restore();
|
2015-03-18 20:03:44 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// private
|
2015-04-19 23:10:37 +03:00
|
|
|
setClip: function(rect, useSketch) {
|
2015-03-18 20:03:44 +03:00
|
|
|
if (!this.useCanvas) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-21 02:25:36 +03:00
|
|
|
var context = this._getContext( useSketch );
|
2015-04-19 23:10:37 +03:00
|
|
|
context.beginPath();
|
|
|
|
context.rect(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
context.clip();
|
2015-03-18 20:03:44 +03:00
|
|
|
},
|
|
|
|
|
2015-04-08 21:13:56 +03:00
|
|
|
// private
|
2015-04-19 23:10:37 +03:00
|
|
|
drawRectangle: function(rect, fillStyle, useSketch) {
|
|
|
|
if (!this.useCanvas) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-21 02:25:36 +03:00
|
|
|
var context = this._getContext( useSketch );
|
2015-04-19 23:10:37 +03:00
|
|
|
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.
|
2016-04-27 18:08:44 +03:00
|
|
|
* @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.
|
2015-04-19 23:10:37 +03:00
|
|
|
*/
|
2016-01-14 23:32:54 +03:00
|
|
|
blendSketch: function(opacity, scale, translate, compositeOperation) {
|
2016-04-27 18:08:44 +03:00
|
|
|
var options = opacity;
|
|
|
|
if (!$.isPlainObject(options)) {
|
|
|
|
options = {
|
|
|
|
opacity: opacity,
|
|
|
|
scale: scale,
|
|
|
|
translate: translate,
|
|
|
|
compositeOperation: compositeOperation
|
|
|
|
};
|
|
|
|
}
|
2015-04-21 02:25:36 +03:00
|
|
|
if (!this.useCanvas || !this.sketchCanvas) {
|
2015-04-08 21:13:56 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-04-27 18:08:44 +03:00
|
|
|
opacity = options.opacity;
|
|
|
|
compositeOperation = options.compositeOperation;
|
|
|
|
var bounds = options.bounds;
|
2016-01-24 02:40:53 +03:00
|
|
|
|
2015-04-15 14:32:41 +03:00
|
|
|
this.context.save();
|
2015-04-19 23:10:37 +03:00
|
|
|
this.context.globalAlpha = opacity;
|
2016-01-20 20:48:15 +03:00
|
|
|
if (compositeOperation) {
|
2016-01-19 22:49:04 +03:00
|
|
|
this.context.globalCompositeOperation = compositeOperation;
|
|
|
|
}
|
2016-04-27 18:08:44 +03:00
|
|
|
if (bounds) {
|
2017-06-16 03:28:04 +03:00
|
|
|
// Internet Explorer, Microsoft Edge, and Safari have problems
|
2017-01-08 13:13:32 +03:00
|
|
|
// when you call context.drawImage with negative x or y
|
2017-06-16 03:28:04 +03:00
|
|
|
// or x + width or y + height greater than the canvas width or height respectively.
|
2016-09-23 22:57:35 +03:00
|
|
|
if (bounds.x < 0) {
|
|
|
|
bounds.width += bounds.x;
|
|
|
|
bounds.x = 0;
|
|
|
|
}
|
2017-06-16 03:28:04 +03:00
|
|
|
if (bounds.x + bounds.width > this.canvas.width) {
|
|
|
|
bounds.width = this.canvas.width - bounds.x;
|
2016-09-23 22:57:35 +03:00
|
|
|
}
|
|
|
|
if (bounds.y < 0) {
|
|
|
|
bounds.height += bounds.y;
|
|
|
|
bounds.y = 0;
|
|
|
|
}
|
2017-06-16 03:28:04 +03:00
|
|
|
if (bounds.y + bounds.height > this.canvas.height) {
|
|
|
|
bounds.height = this.canvas.height - bounds.y;
|
2016-09-23 22:57:35 +03:00
|
|
|
}
|
2017-01-08 13:13:32 +03:00
|
|
|
|
2016-04-27 18:08:44 +03:00
|
|
|
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,
|
2017-01-08 17:52:57 +03:00
|
|
|
(this.canvas.height + 2 * heightExt) * scale,
|
2016-04-27 18:08:44 +03:00
|
|
|
-widthExt,
|
|
|
|
-heightExt,
|
|
|
|
this.canvas.width + 2 * widthExt,
|
|
|
|
this.canvas.height + 2 * heightExt
|
|
|
|
);
|
|
|
|
}
|
2015-04-15 14:32:41 +03:00
|
|
|
this.context.restore();
|
2015-04-08 21:13:56 +03:00
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2016-08-17 16:43:08 +03:00
|
|
|
drawDebugInfo: function(tile, count, i, tiledImage) {
|
2015-04-15 14:40:12 +03:00
|
|
|
if ( !this.useCanvas ) {
|
|
|
|
return;
|
|
|
|
}
|
2013-01-24 08:00:11 +04:00
|
|
|
|
2017-08-01 00:43:10 +03:00
|
|
|
var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
|
2015-04-21 02:25:36 +03:00
|
|
|
var context = this.context;
|
2015-04-19 23:10:37 +03:00
|
|
|
context.save();
|
|
|
|
context.lineWidth = 2 * $.pixelDensityRatio;
|
|
|
|
context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
|
2017-08-01 00:43:10 +03:00
|
|
|
context.strokeStyle = this.debugGridColor[colorIndex];
|
|
|
|
context.fillStyle = this.debugGridColor[colorIndex];
|
2013-02-02 00:18:53 +04:00
|
|
|
|
2015-04-15 14:40:12 +03:00
|
|
|
if ( this.viewport.degrees !== 0 ) {
|
2016-10-24 23:03:31 +03:00
|
|
|
this._offsetForRotation({degrees: this.viewport.degrees});
|
2018-04-04 13:31:18 +03:00
|
|
|
} else{
|
2018-04-06 13:52:10 +03:00
|
|
|
if(this.viewer.doFlip) {
|
|
|
|
this._flip();
|
2018-04-04 13:31:18 +03:00
|
|
|
}
|
2015-04-15 14:40:12 +03:00
|
|
|
}
|
2017-01-21 21:58:21 +03:00
|
|
|
if (tiledImage.getRotation(true) % 360 !== 0) {
|
2016-10-24 23:03:31 +03:00
|
|
|
this._offsetForRotation({
|
2017-01-07 21:24:29 +03:00
|
|
|
degrees: tiledImage.getRotation(true),
|
2016-10-24 23:03:31 +03:00
|
|
|
point: tiledImage.viewport.pixelFromPointNoRotate(
|
|
|
|
tiledImage._getRotationPoint(true), true)
|
|
|
|
});
|
2015-04-15 14:40:12 +03:00
|
|
|
}
|
2013-02-02 00:18:53 +04:00
|
|
|
|
2015-04-19 23:10:37 +03:00
|
|
|
context.strokeRect(
|
2015-04-15 14:40:12 +03:00
|
|
|
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.
|
2015-04-19 23:10:37 +03:00
|
|
|
context.translate( tileCenterX, tileCenterY );
|
|
|
|
context.rotate( Math.PI / 180 * -this.viewport.degrees );
|
|
|
|
context.translate( -tileCenterX, -tileCenterY );
|
2015-04-15 14:40:12 +03:00
|
|
|
|
|
|
|
if( tile.x === 0 && tile.y === 0 ){
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Zoom: " + this.viewport.getZoom(),
|
|
|
|
tile.position.x * $.pixelDensityRatio,
|
|
|
|
(tile.position.y - 30) * $.pixelDensityRatio
|
2014-08-07 00:48:18 +04:00
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Pan: " + this.viewport.getBounds().toString(),
|
|
|
|
tile.position.x * $.pixelDensityRatio,
|
|
|
|
(tile.position.y - 20) * $.pixelDensityRatio
|
2014-08-07 00:48:18 +04:00
|
|
|
);
|
2015-04-15 14:40:12 +03:00
|
|
|
}
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Level: " + tile.level,
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 20) * $.pixelDensityRatio
|
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Column: " + tile.x,
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 30) * $.pixelDensityRatio
|
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Row: " + tile.y,
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 40) * $.pixelDensityRatio
|
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Order: " + i + " of " + count,
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 50) * $.pixelDensityRatio
|
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Size: " + tile.size.toString(),
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 60) * $.pixelDensityRatio
|
|
|
|
);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.fillText(
|
2015-04-15 14:40:12 +03:00
|
|
|
"Position: " + tile.position.toString(),
|
|
|
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
|
|
|
(tile.position.y + 70) * $.pixelDensityRatio
|
|
|
|
);
|
2015-02-03 22:45:39 +03:00
|
|
|
|
2015-04-15 14:40:12 +03:00
|
|
|
if ( this.viewport.degrees !== 0 ) {
|
2015-12-12 03:07:23 +03:00
|
|
|
this._restoreRotationChanges();
|
2013-02-02 00:18:53 +04:00
|
|
|
}
|
2017-01-21 21:58:21 +03:00
|
|
|
if (tiledImage.getRotation(true) % 360 !== 0) {
|
2016-08-17 16:43:08 +03:00
|
|
|
this._restoreRotationChanges();
|
|
|
|
}
|
2015-04-19 23:10:37 +03:00
|
|
|
context.restore();
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
2013-02-14 04:44:23 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2015-04-21 02:25:36 +03:00
|
|
|
debugRect: function(rect) {
|
2014-10-22 04:11:09 +04:00
|
|
|
if ( this.useCanvas ) {
|
2015-04-21 02:25:36 +03:00
|
|
|
var context = this.context;
|
2015-04-19 23:10:37 +03:00
|
|
|
context.save();
|
|
|
|
context.lineWidth = 2 * $.pixelDensityRatio;
|
2017-07-26 17:57:17 +03:00
|
|
|
context.strokeStyle = this.debugGridColor[0];
|
|
|
|
context.fillStyle = this.debugGridColor[0];
|
2014-10-22 04:11:09 +04:00
|
|
|
|
2015-04-19 23:10:37 +03:00
|
|
|
context.strokeRect(
|
2015-02-03 22:45:39 +03:00
|
|
|
rect.x * $.pixelDensityRatio,
|
|
|
|
rect.y * $.pixelDensityRatio,
|
|
|
|
rect.width * $.pixelDensityRatio,
|
|
|
|
rect.height * $.pixelDensityRatio
|
2014-10-22 04:11:09 +04:00
|
|
|
);
|
|
|
|
|
2015-04-19 23:10:37 +03:00
|
|
|
context.restore();
|
2014-10-22 04:11:09 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-01-24 02:40:53 +03:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
},
|
|
|
|
|
2016-08-17 16:43:08 +03:00
|
|
|
getCanvasCenter: function() {
|
|
|
|
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
|
|
|
|
},
|
2013-08-14 01:39:22 +04:00
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2016-10-24 23:03:31 +03:00
|
|
|
_offsetForRotation: function(options) {
|
|
|
|
var point = options.point ?
|
|
|
|
options.point.times($.pixelDensityRatio) :
|
|
|
|
this.getCanvasCenter();
|
2013-08-14 01:39:22 +04:00
|
|
|
|
2016-10-24 23:03:31 +03:00
|
|
|
var context = this._getContext(options.useSketch);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.save();
|
2013-08-14 01:39:22 +04:00
|
|
|
|
2016-08-17 16:43:08 +03:00
|
|
|
context.translate(point.x, point.y);
|
2018-04-06 13:52:10 +03:00
|
|
|
if(this.viewer.doFlip){
|
2018-04-04 13:31:18 +03:00
|
|
|
context.rotate(Math.PI / 180 * -options.degrees);
|
|
|
|
context.scale(-1, 1);
|
2018-04-06 13:52:10 +03:00
|
|
|
} else{
|
2018-04-04 13:31:18 +03:00
|
|
|
context.rotate(Math.PI / 180 * options.degrees);
|
|
|
|
}
|
2016-08-17 16:43:08 +03:00
|
|
|
context.translate(-point.x, -point.y);
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
2013-08-14 01:39:22 +04:00
|
|
|
|
2018-04-04 13:31:18 +03:00
|
|
|
// private
|
|
|
|
_flip: function(options) {
|
2018-04-06 13:52:10 +03:00
|
|
|
options = options || {};
|
2018-04-04 13:31:18 +03:00
|
|
|
var point = options.point ?
|
|
|
|
options.point.times($.pixelDensityRatio) :
|
|
|
|
this.getCanvasCenter();
|
|
|
|
var context = this._getContext(options.useSketch);
|
2018-04-06 13:52:10 +03:00
|
|
|
// context.save();
|
2018-04-04 13:31:18 +03:00
|
|
|
|
|
|
|
context.translate(point.x, 0);
|
|
|
|
context.scale(-1, 1);
|
|
|
|
context.translate(-point.x, 0);
|
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2015-12-12 03:07:23 +03:00
|
|
|
_restoreRotationChanges: function(useSketch) {
|
|
|
|
var context = this._getContext(useSketch);
|
2015-04-19 23:10:37 +03:00
|
|
|
context.restore();
|
2015-01-29 19:19:49 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// private
|
|
|
|
_calculateCanvasSize: function() {
|
|
|
|
var pixelDensityRatio = $.pixelDensityRatio;
|
|
|
|
var viewportSize = this.viewport.getContainerSize();
|
|
|
|
return {
|
|
|
|
x: viewportSize.x * pixelDensityRatio,
|
|
|
|
y: viewportSize.y * pixelDensityRatio
|
|
|
|
};
|
2016-01-24 02:40:53 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// private
|
|
|
|
_calculateSketchCanvasSize: function() {
|
|
|
|
var canvasSize = this._calculateCanvasSize();
|
2016-01-26 20:04:34 +03:00
|
|
|
if (this.viewport.getRotation() === 0) {
|
|
|
|
return canvasSize;
|
|
|
|
}
|
|
|
|
// If the viewport is rotated, we need a larger sketch canvas in order
|
|
|
|
// to support edge smoothing.
|
2016-01-24 02:40:53 +03:00
|
|
|
var sketchCanvasSize = Math.ceil(Math.sqrt(
|
|
|
|
canvasSize.x * canvasSize.x +
|
|
|
|
canvasSize.y * canvasSize.y));
|
|
|
|
return {
|
|
|
|
x: sketchCanvasSize,
|
|
|
|
y: sketchCanvasSize
|
|
|
|
};
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2014-08-07 00:48:18 +04:00
|
|
|
};
|
2013-01-24 08:00:11 +04:00
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
}( OpenSeadragon ));
|