2013-05-01 08:46:16 +04: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
|
|
|
|
2013-01-31 01:51:37 +04:00
|
|
|
var DEVICE_SCREEN = $.getWindowSize(),
|
2012-01-18 03:30:41 +04:00
|
|
|
BROWSER = $.Browser.vendor,
|
|
|
|
BROWSER_VERSION = $.Browser.version,
|
2012-01-12 03:22:13 +04:00
|
|
|
|
|
|
|
SUBPIXEL_RENDERING = (
|
2012-01-18 03:30:41 +04:00
|
|
|
( BROWSER == $.BROWSERS.FIREFOX ) ||
|
|
|
|
( BROWSER == $.BROWSERS.OPERA ) ||
|
|
|
|
( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) ||
|
2012-04-03 11:08:27 +04:00
|
|
|
( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 ) ||
|
|
|
|
( BROWSER == $.BROWSERS.IE && BROWSER_VERSION >= 9 )
|
2013-06-19 21:33:25 +04:00
|
|
|
),
|
2012-01-12 03:22:13 +04:00
|
|
|
|
2013-11-01 21:19:47 +04:00
|
|
|
USE_CANVAS = $.supportsCanvas;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
//console.error( 'USE_CANVAS ' + USE_CANVAS );
|
|
|
|
|
2012-01-25 23:14:02 +04:00
|
|
|
/**
|
|
|
|
* @class
|
2012-02-01 00:59:09 +04:00
|
|
|
* @param {OpenSeadragon.TileSource} source - Reference to Viewer tile source.
|
|
|
|
* @param {OpenSeadragon.Viewport} viewport - Reference to Viewer viewport.
|
|
|
|
* @param {Element} element - Reference to Viewer 'canvas'.
|
|
|
|
* @property {OpenSeadragon.TileSource} source - Reference to Viewer tile source.
|
|
|
|
* @property {OpenSeadragon.Viewport} viewport - Reference to Viewer viewport.
|
2012-02-01 06:01:37 +04:00
|
|
|
* @property {Element} container - Reference to Viewer 'canvas'.
|
|
|
|
* @property {Element|Canvas} canvas - TODO
|
|
|
|
* @property {CanvasContext} context - TODO
|
2012-02-01 00:59:09 +04:00
|
|
|
* @property {Object} config - Reference to Viewer config.
|
|
|
|
* @property {Number} downloading - How many images are currently being loaded in parallel.
|
|
|
|
* @property {Number} normHeight - Ratio of zoomable image height to width.
|
2012-02-01 06:01:37 +04:00
|
|
|
* @property {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
|
|
|
|
* @property {Array} tilesLoaded - An unordered list of Tiles with loaded images.
|
|
|
|
* @property {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
|
|
|
|
* @property {Array} overlays - An unordered list of Overlays added.
|
|
|
|
* @property {Array} lastDrawn - An unordered list of Tiles drawn last frame.
|
|
|
|
* @property {Number} lastResetTime - Last time for which the drawer was reset.
|
|
|
|
* @property {Boolean} midUpdate - Is the drawer currently updating the viewport?
|
|
|
|
* @property {Boolean} updateAgain - Does the drawer need to update the viewort again?
|
2012-02-02 01:56:04 +04:00
|
|
|
* @property {Element} element - DEPRECATED Alias for container.
|
2012-01-25 23:14:02 +04:00
|
|
|
*/
|
2012-03-01 17:38:15 +04:00
|
|
|
$.Drawer = function( options ) {
|
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
|
2012-03-16 19:36:28 +04:00
|
|
|
var args = arguments,
|
|
|
|
i;
|
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if( !$.isPlainObject( options ) ){
|
|
|
|
options = {
|
|
|
|
source: args[ 0 ],
|
|
|
|
viewport: args[ 1 ],
|
|
|
|
element: args[ 2 ]
|
|
|
|
};
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
$.extend( true, this, {
|
|
|
|
|
|
|
|
//internal state properties
|
2013-05-16 10:49:29 +04:00
|
|
|
viewer: null,
|
2012-03-01 17:38:15 +04:00
|
|
|
downloading: 0,
|
|
|
|
tilesMatrix: {},
|
|
|
|
tilesLoaded: [],
|
|
|
|
coverage: {},
|
|
|
|
lastDrawn: [],
|
|
|
|
lastResetTime: 0,
|
|
|
|
midUpdate: false,
|
|
|
|
updateAgain: true,
|
|
|
|
|
2013-01-31 01:51:37 +04:00
|
|
|
|
2013-06-19 21:33:25 +04:00
|
|
|
//internal state / configurable settings
|
2013-01-24 08:00:11 +04:00
|
|
|
overlays: [],
|
|
|
|
collectionOverlays: {},
|
2012-03-16 19:36:28 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
//configurable settings
|
|
|
|
maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount,
|
|
|
|
imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
|
|
|
minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
|
|
|
|
wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
|
|
|
|
wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
|
|
|
|
immediateRender: $.DEFAULT_SETTINGS.immediateRender,
|
|
|
|
blendTime: $.DEFAULT_SETTINGS.blendTime,
|
|
|
|
alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
|
2013-01-24 08:00:11 +04:00
|
|
|
minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
|
2013-01-31 01:51:37 +04:00
|
|
|
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
|
|
|
timeout: $.DEFAULT_SETTINGS.timeout
|
2012-03-01 17:38:15 +04:00
|
|
|
|
|
|
|
}, options );
|
|
|
|
|
2013-11-01 21:19:47 +04:00
|
|
|
if ( this.viewer ) {
|
|
|
|
USE_CANVAS = $.supportsCanvas && this.viewer.useCanvas;
|
|
|
|
}
|
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
this.container = $.getElement( this.element );
|
2012-02-01 00:59:09 +04:00
|
|
|
this.canvas = $.makeNeutralElement( USE_CANVAS ? "canvas" : "div" );
|
|
|
|
this.context = USE_CANVAS ? this.canvas.getContext( "2d" ) : null;
|
2012-03-01 17:38:15 +04:00
|
|
|
this.normHeight = this.source.dimensions.y / this.source.dimensions.x;
|
|
|
|
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
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
this.canvas.style.width = "100%";
|
|
|
|
this.canvas.style.height = "100%";
|
|
|
|
this.canvas.style.position = "absolute";
|
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 );
|
|
|
|
|
2012-03-16 19:36:28 +04:00
|
|
|
//create the correct type of overlay by convention if the overlays
|
|
|
|
//are not already OpenSeadragon.Overlays
|
|
|
|
for( i = 0; i < this.overlays.length; i++ ){
|
|
|
|
if( $.isPlainObject( this.overlays[ i ] ) ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-07-31 11:01:48 +04:00
|
|
|
this.overlays[ i ] = addOverlayFromConfiguration( this, this.overlays[ i ]);
|
2012-03-16 19:36:28 +04:00
|
|
|
|
|
|
|
} else if ( $.isFunction( this.overlays[ i ] ) ){
|
2013-01-24 08:00:11 +04:00
|
|
|
//TODO
|
2012-03-16 19:36:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//this.profiler = new $.Profiler();
|
2011-12-06 07:50:25 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
$.Drawer.prototype = {
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
|
|
|
* Adds an html element as an overlay to the current viewport. Useful for
|
|
|
|
* highlighting words or areas of interest on an image or other zoomable
|
|
|
|
* interface.
|
|
|
|
* @method
|
2013-07-31 11:01:48 +04:00
|
|
|
* @param {Element|String|Object} element - A reference to an element or an id for
|
|
|
|
* the element which will overlayed. Or an Object specifying the configuration for the overlay
|
2013-06-19 21:33:25 +04:00
|
|
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
2012-02-01 06:01:37 +04:00
|
|
|
* rectangle which will be overlayed.
|
2013-06-19 21:33:25 +04:00
|
|
|
* @param {OpenSeadragon.OverlayPlacement} placement - The position of the
|
|
|
|
* viewport which the location coordinates will be treated as relative
|
|
|
|
* to.
|
2013-07-31 11:01:48 +04:00
|
|
|
* @param {function} onDraw - If supplied the callback is called when the overlay
|
|
|
|
* needs to be drawn. It it the responsibility of the callback to do any drawing/positioning.
|
|
|
|
* It is passed position, size and element.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2013-07-31 11:01:48 +04:00
|
|
|
addOverlay: function( element, location, placement, onDraw ) {
|
|
|
|
var options;
|
|
|
|
if( $.isPlainObject( element ) ){
|
|
|
|
options = element;
|
|
|
|
} else {
|
|
|
|
options = {
|
|
|
|
element: element,
|
|
|
|
location: location,
|
|
|
|
placement: placement,
|
|
|
|
onDraw: onDraw
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
element = $.getElement(options.element);
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
if ( getOverlayIndex( this.overlays, element ) >= 0 ) {
|
|
|
|
// they're trying to add a duplicate overlay
|
2013-06-19 21:33:25 +04:00
|
|
|
return;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2013-07-16 06:04:37 +04:00
|
|
|
this.overlays.push( new $.Overlay({
|
|
|
|
element: element,
|
2013-07-31 11:01:48 +04:00
|
|
|
location: options.location,
|
|
|
|
placement: options.placement,
|
2013-08-01 07:36:00 +04:00
|
|
|
onDraw: options.onDraw
|
2013-07-16 06:04:37 +04:00
|
|
|
}) );
|
2012-02-01 00:59:09 +04:00
|
|
|
this.updateAgain = true;
|
2013-02-14 04:44:23 +04:00
|
|
|
if( this.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
this.viewer.raiseEvent( 'add-overlay', {
|
|
|
|
element: element,
|
2013-07-31 11:01:48 +04:00
|
|
|
location: options.location,
|
|
|
|
placement: options.placement
|
2013-02-14 04:44:23 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
2013-06-19 21:33:25 +04:00
|
|
|
* Updates the overlay represented by the reference to the element or
|
2012-02-01 06:01:37 +04:00
|
|
|
* element id moving it to the new location, relative to the new placement.
|
|
|
|
* @method
|
2013-06-19 21:33:25 +04:00
|
|
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
2012-02-01 06:01:37 +04:00
|
|
|
* rectangle which will be overlayed.
|
2013-06-19 21:33:25 +04:00
|
|
|
* @param {OpenSeadragon.OverlayPlacement} placement - The position of the
|
|
|
|
* viewport which the location coordinates will be treated as relative
|
|
|
|
* to.
|
2013-02-14 04:44:23 +04:00
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
updateOverlay: function( element, location, placement ) {
|
|
|
|
var i;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
element = $.getElement( element );
|
|
|
|
i = getOverlayIndex( this.overlays, element );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( i >= 0 ) {
|
|
|
|
this.overlays[ i ].update( location, placement );
|
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
if( this.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
this.viewer.raiseEvent( 'update-overlay', {
|
|
|
|
element: element,
|
|
|
|
location: location,
|
2013-02-14 04:44:23 +04:00
|
|
|
placement: placement
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return this;
|
2012-02-01 00:59:09 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
2013-06-19 21:33:25 +04:00
|
|
|
* Removes and overlay identified by the reference element or element id
|
2012-02-01 06:01:37 +04:00
|
|
|
* and schedules and update.
|
|
|
|
* @method
|
2013-06-19 21:33:25 +04:00
|
|
|
* @param {Element|String} element - A reference to the element or an
|
2012-02-01 06:01:37 +04:00
|
|
|
* element id which represent the ovelay content to be removed.
|
2013-02-14 04:44:23 +04:00
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
removeOverlay: function( element ) {
|
|
|
|
var i;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
element = $.getElement( element );
|
|
|
|
i = getOverlayIndex( this.overlays, element );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( i >= 0 ) {
|
|
|
|
this.overlays[ i ].destroy();
|
|
|
|
this.overlays.splice( i, 1 );
|
|
|
|
this.updateAgain = true;
|
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
if( this.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
this.viewer.raiseEvent( 'remove-overlay', {
|
2013-02-14 04:44:23 +04:00
|
|
|
element: element
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
|
|
|
* Removes all currently configured Overlays from this Drawer and schedules
|
|
|
|
* and update.
|
|
|
|
* @method
|
2013-02-14 04:44:23 +04:00
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
clearOverlays: function() {
|
|
|
|
while ( this.overlays.length > 0 ) {
|
|
|
|
this.overlays.pop().destroy();
|
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
if( this.viewer ){
|
2013-10-11 04:00:15 +04:00
|
|
|
this.viewer.raiseEvent( 'clear-overlay', {} );
|
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
|
|
|
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
2013-06-19 21:33:25 +04:00
|
|
|
* Returns whether the Drawer is scheduled for an update at the
|
2012-02-01 06:01:37 +04:00
|
|
|
* soonest possible opportunity.
|
|
|
|
* @method
|
2013-06-19 21:33:25 +04:00
|
|
|
* @returns {Boolean} - Whether the Drawer is scheduled for an update at the
|
2012-02-01 06:01:37 +04:00
|
|
|
* soonest possible opportunity.
|
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
needsUpdate: function() {
|
|
|
|
return this.updateAgain;
|
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
|
|
|
* Returns the total number of tiles that have been loaded by this Drawer.
|
|
|
|
* @method
|
2013-06-19 21:33:25 +04:00
|
|
|
* @returns {Number} - The total number of tiles that have been loaded by
|
2012-02-01 06:01:37 +04:00
|
|
|
* this Drawer.
|
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
numTilesLoaded: function() {
|
|
|
|
return this.tilesLoaded.length;
|
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
2013-06-19 21:33:25 +04:00
|
|
|
* Clears all tiles and triggers an update on the next call to
|
2012-02-01 06:01:37 +04:00
|
|
|
* Drawer.prototype.update().
|
|
|
|
* @method
|
2013-02-14 04:44:23 +04:00
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
reset: function() {
|
|
|
|
clearTiles( this );
|
2013-06-21 00:15:04 +04:00
|
|
|
this.lastResetTime = $.now();
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = true;
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
|
|
|
* Forces the Drawer to update.
|
|
|
|
* @method
|
2013-02-14 04:44:23 +04:00
|
|
|
* @return {OpenSeadragon.Drawer} Chainable.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
update: function() {
|
|
|
|
//this.profiler.beginUpdate();
|
|
|
|
this.midUpdate = true;
|
|
|
|
updateViewport( this );
|
|
|
|
this.midUpdate = false;
|
|
|
|
//this.profiler.endUpdate();
|
2013-02-14 04:44:23 +04:00
|
|
|
return this;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-02-01 06:01:37 +04:00
|
|
|
/**
|
2013-06-19 21:33:25 +04:00
|
|
|
* Used internally to load images when required. May also be used to
|
|
|
|
* preload a set of images so the browser will have them available in
|
2012-02-01 06:01:37 +04:00
|
|
|
* the local cache to optimize user experience in certain cases. Because
|
|
|
|
* the number of parallel image loads is configurable, if too many images
|
2013-06-19 21:33:25 +04:00
|
|
|
* are currently being loaded, the request will be ignored. Since by
|
|
|
|
* default drawer.imageLoaderLimit is 0, the native browser parallel
|
2012-02-01 06:01:37 +04:00
|
|
|
* image loading policy will be used.
|
|
|
|
* @method
|
|
|
|
* @param {String} src - The url of the image to load.
|
|
|
|
* @param {Function} callback - The function that will be called with the
|
2013-01-25 23:36:41 +04:00
|
|
|
* Image object as the only parameter if it was loaded successfully.
|
|
|
|
* If an error occured, or the request timed out or was aborted,
|
|
|
|
* the parameter is null instead.
|
2013-01-28 22:24:13 +04:00
|
|
|
* @return {Boolean} loading - Whether the request was submitted or ignored
|
2012-03-01 17:38:15 +04:00
|
|
|
* based on OpenSeadragon.DEFAULT_SETTINGS.imageLoaderLimit.
|
2012-02-01 06:01:37 +04:00
|
|
|
*/
|
2012-02-01 00:59:09 +04:00
|
|
|
loadImage: function( src, callback ) {
|
|
|
|
var _this = this,
|
|
|
|
loading = false,
|
|
|
|
image,
|
|
|
|
jobid,
|
|
|
|
complete;
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
if ( !this.imageLoaderLimit ||
|
2012-03-01 17:38:15 +04:00
|
|
|
this.downloading < this.imageLoaderLimit ) {
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
this.downloading++;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
image = new Image();
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2013-01-25 23:36:41 +04:00
|
|
|
complete = function( imagesrc, resultingImage ){
|
2012-02-01 00:59:09 +04:00
|
|
|
_this.downloading--;
|
|
|
|
if (typeof ( callback ) == "function") {
|
|
|
|
try {
|
2013-01-25 23:36:41 +04:00
|
|
|
callback( resultingImage );
|
2012-02-01 00:59:09 +04:00
|
|
|
} catch ( e ) {
|
|
|
|
$.console.error(
|
2013-06-19 21:33:25 +04:00
|
|
|
"%s while executing %s callback: %s",
|
2012-02-01 00:59:09 +04:00
|
|
|
e.name,
|
|
|
|
src,
|
|
|
|
e.message,
|
|
|
|
e
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
};
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
image.onload = function(){
|
2012-09-29 14:29:43 +04:00
|
|
|
finishLoadingImage( image, complete, true, jobid );
|
2012-02-01 00:59:09 +04:00
|
|
|
};
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
image.onabort = image.onerror = function(){
|
2012-09-29 14:29:43 +04:00
|
|
|
finishLoadingImage( image, complete, false, jobid );
|
2012-02-01 00:59:09 +04:00
|
|
|
};
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
jobid = window.setTimeout( function(){
|
|
|
|
finishLoadingImage( image, complete, false, jobid );
|
2013-01-31 01:51:37 +04:00
|
|
|
}, this.timeout );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
loading = true;
|
|
|
|
image.src = src;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return loading;
|
2013-08-16 21:32:21 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
canRotate: function() {
|
|
|
|
return USE_CANVAS;
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
|
|
|
};
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2013-02-13 07:40:08 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
*/
|
2013-07-31 11:01:48 +04:00
|
|
|
function addOverlayFromConfiguration( drawer, overlay ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-02-13 07:40:08 +04:00
|
|
|
var element = null,
|
|
|
|
rect = ( overlay.height && overlay.width ) ? new $.Rect(
|
2013-06-19 21:33:25 +04:00
|
|
|
overlay.x || overlay.px,
|
|
|
|
overlay.y || overlay.py,
|
|
|
|
overlay.width,
|
2013-02-13 07:40:08 +04:00
|
|
|
overlay.height
|
|
|
|
) : new $.Point(
|
2013-06-19 21:33:25 +04:00
|
|
|
overlay.x || overlay.px,
|
2013-02-13 07:40:08 +04:00
|
|
|
overlay.y || overlay.py
|
|
|
|
),
|
2013-06-19 21:33:25 +04:00
|
|
|
id = overlay.id ?
|
2013-02-13 07:40:08 +04:00
|
|
|
overlay.id :
|
|
|
|
"openseadragon-overlay-"+Math.floor(Math.random()*10000000);
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-02-13 07:40:08 +04:00
|
|
|
element = $.getElement(overlay.id);
|
|
|
|
if( !element ){
|
|
|
|
element = document.createElement("a");
|
|
|
|
element.href = "#/overlay/"+id;
|
|
|
|
}
|
|
|
|
element.id = id;
|
2013-05-13 06:53:41 +04:00
|
|
|
$.addClass( element, overlay.className ?
|
2013-02-13 07:40:08 +04:00
|
|
|
overlay.className :
|
|
|
|
"openseadragon-overlay"
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if(overlay.px !== undefined){
|
2013-06-26 20:15:37 +04:00
|
|
|
//if they specified 'px' so it's in pixel coordinates so
|
2013-02-13 07:40:08 +04:00
|
|
|
//we need to translate to viewport coordinates
|
|
|
|
rect = drawer.viewport.imageToViewportRectangle( rect );
|
|
|
|
}
|
2013-07-16 06:04:37 +04:00
|
|
|
|
2013-02-13 07:40:08 +04:00
|
|
|
if( overlay.placement ){
|
2013-07-16 06:04:37 +04:00
|
|
|
return new $.Overlay({
|
|
|
|
element: element,
|
|
|
|
location: drawer.viewport.pointFromPixel(rect),
|
|
|
|
placement: $.OverlayPlacement[overlay.placement.toUpperCase()],
|
2013-07-31 11:01:48 +04:00
|
|
|
onDraw: overlay.onDraw
|
2013-07-16 06:04:37 +04:00
|
|
|
});
|
2013-02-13 07:40:08 +04:00
|
|
|
}else{
|
2013-07-16 06:04:37 +04:00
|
|
|
return new $.Overlay({
|
|
|
|
element: element,
|
|
|
|
location: rect,
|
2013-07-31 11:01:48 +04:00
|
|
|
onDraw: overlay.onDraw
|
2013-07-16 06:04:37 +04:00
|
|
|
});
|
2013-02-13 07:40:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
2013-06-26 20:15:37 +04:00
|
|
|
* Pretty much every other line in this needs to be documented so it's clear
|
2012-02-01 00:59:09 +04:00
|
|
|
* how each piece of this routine contributes to the drawing process. That's
|
|
|
|
* why there are so many TODO's inside this function.
|
|
|
|
*/
|
|
|
|
function updateViewport( drawer ) {
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.updateAgain = false;
|
|
|
|
|
2013-02-14 04:44:23 +04:00
|
|
|
if( drawer.viewer ){
|
2013-10-11 04:00:15 +04:00
|
|
|
drawer.viewer.raiseEvent( 'update-viewport', {} );
|
2013-02-14 04:44:23 +04:00
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
var tile,
|
|
|
|
level,
|
|
|
|
best = null,
|
|
|
|
haveDrawn = false,
|
2013-06-21 00:15:04 +04:00
|
|
|
currentTime = $.now(),
|
2012-02-01 00:59:09 +04:00
|
|
|
viewportSize = drawer.viewport.getContainerSize(),
|
|
|
|
viewportBounds = drawer.viewport.getBounds( true ),
|
|
|
|
viewportTL = viewportBounds.getTopLeft(),
|
|
|
|
viewportBR = viewportBounds.getBottomRight(),
|
2013-06-19 21:33:25 +04:00
|
|
|
zeroRatioC = drawer.viewport.deltaPixelsFromPoints(
|
|
|
|
drawer.source.getPixelRatio( 0 ),
|
2012-02-01 00:59:09 +04:00
|
|
|
true
|
|
|
|
).x,
|
|
|
|
lowestLevel = Math.max(
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.source.minLevel,
|
|
|
|
Math.floor(
|
|
|
|
Math.log( drawer.minZoomImageRatio ) /
|
2012-02-01 00:59:09 +04:00
|
|
|
Math.log( 2 )
|
|
|
|
)
|
|
|
|
),
|
2013-02-09 21:21:51 +04:00
|
|
|
highestLevel = Math.min(
|
2012-09-07 16:55:19 +04:00
|
|
|
Math.abs(drawer.source.maxLevel),
|
2013-06-19 21:33:25 +04:00
|
|
|
Math.abs(Math.floor(
|
|
|
|
Math.log( zeroRatioC / drawer.minPixelRatio ) /
|
2012-02-01 00:59:09 +04:00
|
|
|
Math.log( 2 )
|
2012-09-07 16:55:19 +04:00
|
|
|
))
|
2012-02-23 16:18:28 +04:00
|
|
|
),
|
2013-08-14 01:39:22 +04:00
|
|
|
degrees = drawer.viewport.degrees,
|
2012-02-23 16:18:28 +04:00
|
|
|
renderPixelRatioC,
|
|
|
|
renderPixelRatioT,
|
|
|
|
zeroRatioT,
|
|
|
|
optimalRatio,
|
|
|
|
levelOpacity,
|
|
|
|
levelVisibility;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
while ( drawer.lastDrawn.length > 0 ) {
|
|
|
|
tile = drawer.lastDrawn.pop();
|
|
|
|
tile.beingDrawn = false;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
drawer.canvas.innerHTML = "";
|
|
|
|
if ( USE_CANVAS ) {
|
2013-02-08 18:21:28 +04:00
|
|
|
if( drawer.canvas.width != viewportSize.x ||
|
|
|
|
drawer.canvas.height != viewportSize.y ){
|
|
|
|
drawer.canvas.width = viewportSize.x;
|
|
|
|
drawer.canvas.height = viewportSize.y;
|
2013-01-31 01:51:37 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2013-08-14 01:39:22 +04:00
|
|
|
//Change bounds for rotation
|
|
|
|
if (degrees === 90 || degrees === 270) {
|
|
|
|
var rotatedBounds = viewportBounds.rotate( degrees );
|
|
|
|
viewportTL = rotatedBounds.getTopLeft();
|
|
|
|
viewportBR = rotatedBounds.getBottomRight();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Don't draw if completely outside of the viewport
|
2013-06-19 21:33:25 +04:00
|
|
|
if ( !drawer.wrapHorizontal &&
|
2012-02-01 00:59:09 +04:00
|
|
|
( viewportBR.x < 0 || viewportTL.x > 1 ) ) {
|
|
|
|
return;
|
2013-06-19 21:33:25 +04:00
|
|
|
} else if
|
2012-03-01 17:38:15 +04:00
|
|
|
( !drawer.wrapVertical &&
|
2012-02-01 00:59:09 +04:00
|
|
|
( viewportBR.y < 0 || viewportTL.y > drawer.normHeight ) ) {
|
|
|
|
return;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( !drawer.wrapHorizontal ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
viewportTL.x = Math.max( viewportTL.x, 0 );
|
|
|
|
viewportBR.x = Math.min( viewportBR.x, 1 );
|
|
|
|
}
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( !drawer.wrapVertical ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
viewportTL.y = Math.max( viewportTL.y, 0 );
|
|
|
|
viewportBR.y = Math.min( viewportBR.y, drawer.normHeight );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
lowestLevel = Math.min( lowestLevel, highestLevel );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
2013-06-25 22:26:09 +04:00
|
|
|
var drawLevel; // FIXME: drawLevel should have a more explanatory name
|
2012-02-01 00:59:09 +04:00
|
|
|
for ( level = highestLevel; level >= lowestLevel; level-- ) {
|
2013-06-25 22:26:09 +04:00
|
|
|
drawLevel = false;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-23 16:18:28 +04:00
|
|
|
//Avoid calculations for draw if we have already drawn this
|
|
|
|
renderPixelRatioC = drawer.viewport.deltaPixelsFromPoints(
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.source.getPixelRatio( level ),
|
2012-02-23 16:18:28 +04:00
|
|
|
true
|
|
|
|
).x;
|
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( ( !haveDrawn && renderPixelRatioC >= drawer.minPixelRatio ) ||
|
2012-02-23 16:18:28 +04:00
|
|
|
( level == lowestLevel ) ) {
|
|
|
|
drawLevel = true;
|
|
|
|
haveDrawn = true;
|
|
|
|
} else if ( !haveDrawn ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-08-14 01:39:22 +04:00
|
|
|
//Perform calculations for draw if we haven't drawn this
|
2012-02-23 16:18:28 +04:00
|
|
|
renderPixelRatioT = drawer.viewport.deltaPixelsFromPoints(
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.source.getPixelRatio( level ),
|
2012-02-23 16:18:28 +04:00
|
|
|
false
|
|
|
|
).x;
|
|
|
|
|
2013-06-19 21:33:25 +04:00
|
|
|
zeroRatioT = drawer.viewport.deltaPixelsFromPoints(
|
|
|
|
drawer.source.getPixelRatio(
|
2013-03-06 14:51:31 +04:00
|
|
|
Math.max(
|
|
|
|
drawer.source.getClosestLevel( drawer.viewport.containerSize ) - 1,
|
|
|
|
0
|
|
|
|
)
|
2013-06-19 21:33:25 +04:00
|
|
|
),
|
2012-02-23 16:18:28 +04:00
|
|
|
false
|
|
|
|
).x;
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
optimalRatio = drawer.immediateRender ?
|
|
|
|
1 :
|
2012-02-23 16:18:28 +04:00
|
|
|
zeroRatioT;
|
|
|
|
|
|
|
|
levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 );
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
levelVisibility = optimalRatio / Math.abs(
|
|
|
|
optimalRatio - renderPixelRatioT
|
2012-02-23 16:18:28 +04:00
|
|
|
);
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
best = updateLevel(
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer,
|
2012-02-23 16:18:28 +04:00
|
|
|
haveDrawn,
|
2013-06-25 22:26:09 +04:00
|
|
|
drawLevel,
|
2013-06-19 21:33:25 +04:00
|
|
|
level,
|
2012-02-23 16:18:28 +04:00
|
|
|
levelOpacity,
|
|
|
|
levelVisibility,
|
2013-06-19 21:33:25 +04:00
|
|
|
viewportTL,
|
|
|
|
viewportBR,
|
|
|
|
currentTime,
|
|
|
|
best
|
2012-02-01 00:59:09 +04:00
|
|
|
);
|
|
|
|
|
|
|
|
//TODO
|
|
|
|
if ( providesCoverage( drawer.coverage, level ) ) {
|
|
|
|
break;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
drawTiles( drawer, drawer.lastDrawn );
|
|
|
|
drawOverlays( drawer.viewport, drawer.overlays, drawer.container );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//TODO
|
|
|
|
if ( best ) {
|
|
|
|
loadTile( drawer, best, currentTime );
|
|
|
|
// because we haven't finished drawing, so
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.updateAgain = true;
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
|
2013-06-25 22:26:09 +04:00
|
|
|
function updateLevel( drawer, haveDrawn, drawLevel, level, levelOpacity, levelVisibility, viewportTL, viewportBR, currentTime, best ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
var x, y,
|
|
|
|
tileTL,
|
|
|
|
tileBR,
|
|
|
|
numberOfTiles,
|
2012-02-23 16:18:28 +04:00
|
|
|
viewportCenter = drawer.viewport.pixelFromPoint( drawer.viewport.getCenter() );
|
2012-02-01 00:59:09 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2013-02-14 04:44:23 +04:00
|
|
|
if( drawer.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.viewer.raiseEvent( 'update-level', {
|
2013-02-14 04:44:23 +04:00
|
|
|
havedrawn: haveDrawn,
|
2013-06-19 21:33:25 +04:00
|
|
|
level: level,
|
2013-02-14 04:44:23 +04:00
|
|
|
opacity: levelOpacity,
|
2013-06-19 21:33:25 +04:00
|
|
|
visibility: levelVisibility,
|
|
|
|
topleft: viewportTL,
|
|
|
|
bottomright: viewportBR,
|
|
|
|
currenttime: currentTime,
|
2013-02-14 04:44:23 +04:00
|
|
|
best: best
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
//OK, a new drawing so do your calculations
|
|
|
|
tileTL = drawer.source.getTileAtPoint( level, viewportTL );
|
|
|
|
tileBR = drawer.source.getTileAtPoint( level, viewportBR );
|
|
|
|
numberOfTiles = drawer.source.getNumTiles( level );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
resetCoverage( drawer.coverage, level );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( !drawer.wrapHorizontal ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
tileBR.x = Math.min( tileBR.x, numberOfTiles.x - 1 );
|
|
|
|
}
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( !drawer.wrapVertical ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
tileBR.y = Math.min( tileBR.y, numberOfTiles.y - 1 );
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
for ( x = tileTL.x; x <= tileBR.x; x++ ) {
|
|
|
|
for ( y = tileTL.y; y <= tileBR.y; y++ ) {
|
|
|
|
|
2013-06-19 21:33:25 +04:00
|
|
|
best = updateTile(
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer,
|
|
|
|
drawLevel,
|
|
|
|
haveDrawn,
|
|
|
|
x, y,
|
|
|
|
level,
|
|
|
|
levelOpacity,
|
|
|
|
levelVisibility,
|
|
|
|
viewportCenter,
|
|
|
|
numberOfTiles,
|
|
|
|
currentTime,
|
|
|
|
best
|
2012-01-24 07:48:45 +04:00
|
|
|
);
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return best;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function updateTile( drawer, drawLevel, haveDrawn, x, y, level, levelOpacity, levelVisibility, viewportCenter, numberOfTiles, currentTime, best){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
var tile = getTile(
|
|
|
|
x, y,
|
|
|
|
level,
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.source,
|
|
|
|
drawer.tilesMatrix,
|
2013-06-19 21:33:25 +04:00
|
|
|
currentTime,
|
|
|
|
numberOfTiles,
|
|
|
|
drawer.normHeight
|
2012-02-01 00:59:09 +04:00
|
|
|
),
|
2013-06-19 01:55:19 +04:00
|
|
|
drawTile = drawLevel;
|
2012-02-01 00:59:09 +04:00
|
|
|
|
2013-02-14 04:44:23 +04:00
|
|
|
if( drawer.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.viewer.raiseEvent( 'update-tile', {
|
2013-02-14 04:44:23 +04:00
|
|
|
tile: tile
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
setCoverage( drawer.coverage, level, x, y, false );
|
|
|
|
|
|
|
|
if ( !tile.exists ) {
|
|
|
|
return best;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( haveDrawn && !drawTile ) {
|
|
|
|
if ( isCovered( drawer.coverage, level, x, y ) ) {
|
|
|
|
setCoverage( drawer.coverage, level, x, y, true );
|
|
|
|
} else {
|
|
|
|
drawTile = true;
|
2012-01-19 06:52:22 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !drawTile ) {
|
|
|
|
return best;
|
|
|
|
}
|
2012-01-19 06:52:22 +04:00
|
|
|
|
2013-06-19 21:33:25 +04:00
|
|
|
positionTile(
|
|
|
|
tile,
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.source.tileOverlap,
|
|
|
|
drawer.viewport,
|
2013-06-19 21:33:25 +04:00
|
|
|
viewportCenter,
|
|
|
|
levelVisibility
|
2012-02-01 00:59:09 +04:00
|
|
|
);
|
2012-01-19 06:52:22 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( tile.loaded ) {
|
2013-05-29 22:18:25 +04:00
|
|
|
var needsUpdate = blendTile(
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer,
|
2013-06-19 21:33:25 +04:00
|
|
|
tile,
|
2012-02-01 00:59:09 +04:00
|
|
|
x, y,
|
|
|
|
level,
|
2013-06-19 21:33:25 +04:00
|
|
|
levelOpacity,
|
|
|
|
currentTime
|
2012-01-19 06:52:22 +04:00
|
|
|
);
|
2013-05-29 22:18:25 +04:00
|
|
|
|
|
|
|
if ( needsUpdate ) {
|
|
|
|
drawer.updateAgain = true;
|
|
|
|
}
|
2012-03-20 11:38:27 +04:00
|
|
|
} else if ( tile.loading ) {
|
2013-06-19 21:33:25 +04:00
|
|
|
// the tile is already in the download queue
|
2012-03-20 23:00:25 +04:00
|
|
|
// thanks josh1093 for finally translating this typo
|
2012-02-01 00:59:09 +04:00
|
|
|
} else {
|
|
|
|
best = compareTiles( best, tile );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return best;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-01-24 07:48:45 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, normHeight ) {
|
|
|
|
var xMod,
|
|
|
|
yMod,
|
|
|
|
bounds,
|
|
|
|
exists,
|
|
|
|
url,
|
|
|
|
tile;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !tilesMatrix[ level ] ) {
|
|
|
|
tilesMatrix[ level ] = {};
|
|
|
|
}
|
|
|
|
if ( !tilesMatrix[ level ][ x ] ) {
|
|
|
|
tilesMatrix[ level ][ x ] = {};
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !tilesMatrix[ level ][ x ][ y ] ) {
|
|
|
|
xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
|
|
|
|
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
|
|
|
bounds = tileSource.getTileBounds( level, xMod, yMod );
|
|
|
|
exists = tileSource.tileExists( level, xMod, yMod );
|
|
|
|
url = tileSource.getTileUrl( level, xMod, yMod );
|
|
|
|
|
|
|
|
bounds.x += 1.0 * ( x - xMod ) / numTiles.x;
|
|
|
|
bounds.y += normHeight * ( y - yMod ) / numTiles.y;
|
|
|
|
|
|
|
|
tilesMatrix[ level ][ x ][ y ] = new $.Tile(
|
2013-06-19 21:33:25 +04:00
|
|
|
level,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
bounds,
|
|
|
|
exists,
|
2012-02-01 00:59:09 +04:00
|
|
|
url
|
|
|
|
);
|
|
|
|
}
|
2012-01-19 06:52:22 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
tile = tilesMatrix[ level ][ x ][ y ];
|
|
|
|
tile.lastTouchTime = time;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return tile;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function loadTile( drawer, tile, time ) {
|
2013-01-31 05:23:45 +04:00
|
|
|
if( drawer.viewport.collectionMode ){
|
|
|
|
drawer.midUpdate = false;
|
|
|
|
onTileLoad( drawer, tile, time );
|
|
|
|
} else {
|
|
|
|
tile.loading = drawer.loadImage(
|
|
|
|
tile.url,
|
|
|
|
function( image ){
|
|
|
|
onTileLoad( drawer, tile, time, image );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function onTileLoad( drawer, tile, time, image ) {
|
|
|
|
var insertionIndex,
|
|
|
|
cutoff,
|
|
|
|
worstTile,
|
|
|
|
worstTime,
|
|
|
|
worstLevel,
|
|
|
|
worstTileIndex,
|
|
|
|
prevTile,
|
|
|
|
prevTime,
|
|
|
|
prevLevel,
|
|
|
|
i;
|
|
|
|
|
|
|
|
tile.loading = false;
|
|
|
|
|
|
|
|
if ( drawer.midUpdate ) {
|
|
|
|
$.console.warn( "Tile load callback in middle of drawing routine." );
|
|
|
|
return;
|
2013-01-31 05:23:45 +04:00
|
|
|
} else if ( !image && !drawer.viewport.collectionMode ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
$.console.log( "Tile %s failed to load: %s", tile, tile.url );
|
2013-02-08 18:21:28 +04:00
|
|
|
if( !drawer.debugMode ){
|
|
|
|
tile.exists = false;
|
|
|
|
return;
|
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
} else if ( time < drawer.lastResetTime ) {
|
|
|
|
$.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
|
|
|
|
return;
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
tile.loaded = true;
|
|
|
|
tile.image = image;
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2013-02-08 18:21:28 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
insertionIndex = drawer.tilesLoaded.length;
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
cutoff = Math.ceil( Math.log( drawer.source.tileSize ) / Math.log( 2 ) );
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
worstTile = null;
|
|
|
|
worstTileIndex = -1;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
for ( i = drawer.tilesLoaded.length - 1; i >= 0; i-- ) {
|
|
|
|
prevTile = drawer.tilesLoaded[ i ];
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( prevTile.level <= drawer.cutoff || prevTile.beingDrawn ) {
|
|
|
|
continue;
|
|
|
|
} else if ( !worstTile ) {
|
|
|
|
worstTile = prevTile;
|
|
|
|
worstTileIndex = i;
|
|
|
|
continue;
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
prevTime = prevTile.lastTouchTime;
|
|
|
|
worstTime = worstTile.lastTouchTime;
|
|
|
|
prevLevel = prevTile.level;
|
|
|
|
worstLevel = worstTile.level;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2013-06-19 21:33:25 +04:00
|
|
|
if ( prevTime < worstTime ||
|
2012-02-01 00:59:09 +04:00
|
|
|
( prevTime == worstTime && prevLevel > worstLevel ) ) {
|
|
|
|
worstTile = prevTile;
|
|
|
|
worstTileIndex = i;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( worstTile && worstTileIndex >= 0 ) {
|
|
|
|
worstTile.unload();
|
|
|
|
insertionIndex = worstTileIndex;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.tilesLoaded[ insertionIndex ] = tile;
|
|
|
|
drawer.updateAgain = true;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility ){
|
|
|
|
var boundsTL = tile.bounds.getTopLeft(),
|
|
|
|
boundsSize = tile.bounds.getSize(),
|
|
|
|
positionC = viewport.pixelFromPoint( boundsTL, true ),
|
|
|
|
positionT = viewport.pixelFromPoint( boundsTL, false ),
|
|
|
|
sizeC = viewport.deltaPixelsFromPoints( boundsSize, true ),
|
|
|
|
sizeT = viewport.deltaPixelsFromPoints( boundsSize, false ),
|
|
|
|
tileCenter = positionT.plus( sizeT.divide( 2 ) ),
|
|
|
|
tileDistance = viewportCenter.distanceTo( tileCenter );
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !overlap ) {
|
|
|
|
sizeC = sizeC.plus( new $.Point( 1, 1 ) );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
tile.position = positionC;
|
|
|
|
tile.size = sizeC;
|
|
|
|
tile.distance = tileDistance;
|
|
|
|
tile.visibility = levelVisibility;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function blendTile( drawer, tile, x, y, level, levelOpacity, currentTime ){
|
2012-03-01 17:38:15 +04:00
|
|
|
var blendTimeMillis = 1000 * drawer.blendTime,
|
2012-02-01 00:59:09 +04:00
|
|
|
deltaTime,
|
|
|
|
opacity;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !tile.blendStart ) {
|
|
|
|
tile.blendStart = currentTime;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
deltaTime = currentTime - tile.blendStart;
|
2013-03-06 14:51:31 +04:00
|
|
|
opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2012-03-01 17:38:15 +04:00
|
|
|
if ( drawer.alwaysBlend ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
opacity *= levelOpacity;
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
tile.opacity = opacity;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
drawer.lastDrawn.push( tile );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( opacity == 1 ) {
|
|
|
|
setCoverage( drawer.coverage, level, x, y, true );
|
|
|
|
} else if ( deltaTime < blendTimeMillis ) {
|
|
|
|
return true;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return false;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
function clearTiles( drawer ) {
|
|
|
|
drawer.tilesMatrix = {};
|
|
|
|
drawer.tilesLoaded = [];
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
* Returns true if the given tile provides coverage to lower-level tiles of
|
|
|
|
* lower resolution representing the same content. If neither x nor y is
|
|
|
|
* given, returns true if the entire visible level provides coverage.
|
2013-06-19 21:33:25 +04:00
|
|
|
*
|
2012-02-01 00:59:09 +04:00
|
|
|
* Note that out-of-bounds tiles provide coverage in this sense, since
|
|
|
|
* there's no content that they would need to cover. Tiles at non-existent
|
|
|
|
* levels that are within the image bounds, however, do not.
|
|
|
|
*/
|
|
|
|
function providesCoverage( coverage, level, x, y ) {
|
|
|
|
var rows,
|
|
|
|
cols,
|
|
|
|
i, j;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !coverage[ level ] ) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( x === undefined || y === undefined ) {
|
|
|
|
rows = coverage[ level ];
|
|
|
|
for ( i in rows ) {
|
|
|
|
if ( rows.hasOwnProperty( i ) ) {
|
|
|
|
cols = rows[ i ];
|
|
|
|
for ( j in cols ) {
|
|
|
|
if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return true;
|
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return (
|
|
|
|
coverage[ level ][ x] === undefined ||
|
|
|
|
coverage[ level ][ x ][ y ] === undefined ||
|
|
|
|
coverage[ level ][ x ][ y ] === true
|
|
|
|
);
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
* Returns true if the given tile is completely covered by higher-level
|
|
|
|
* tiles of higher resolution representing the same content. If neither x
|
|
|
|
* nor y is given, returns true if the entire visible level is covered.
|
|
|
|
*/
|
|
|
|
function isCovered( coverage, level, x, y ) {
|
|
|
|
if ( x === undefined || y === undefined ) {
|
|
|
|
return providesCoverage( coverage, level + 1 );
|
|
|
|
} else {
|
|
|
|
return (
|
|
|
|
providesCoverage( coverage, level + 1, 2 * x, 2 * y ) &&
|
|
|
|
providesCoverage( coverage, level + 1, 2 * x, 2 * y + 1 ) &&
|
|
|
|
providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y ) &&
|
|
|
|
providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y + 1 )
|
|
|
|
);
|
|
|
|
}
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
* Sets whether the given tile provides coverage or not.
|
|
|
|
*/
|
|
|
|
function setCoverage( coverage, level, x, y, covers ) {
|
|
|
|
if ( !coverage[ level ] ) {
|
|
|
|
$.console.warn(
|
2013-06-19 21:33:25 +04:00
|
|
|
"Setting coverage for a tile before its level's coverage has been reset: %s",
|
2012-02-01 00:59:09 +04:00
|
|
|
level
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
if ( !coverage[ level ][ x ] ) {
|
|
|
|
coverage[ level ][ x ] = {};
|
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
coverage[ level ][ x ][ y ] = covers;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
* Resets coverage information for the given level. This should be called
|
|
|
|
* after every draw routine. Note that at the beginning of the next draw
|
2013-06-19 21:33:25 +04:00
|
|
|
* routine, coverage for every visible tile should be explicitly set.
|
2012-02-01 00:59:09 +04:00
|
|
|
*/
|
|
|
|
function resetCoverage( coverage, level ) {
|
|
|
|
coverage[ level ] = {};
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
|
|
|
* Determines the 'z-index' of the given overlay. Overlays are ordered in
|
|
|
|
* a z-index based on the order they are added to the Drawer.
|
|
|
|
*/
|
|
|
|
function getOverlayIndex( overlays, element ) {
|
|
|
|
var i;
|
|
|
|
for ( i = overlays.length - 1; i >= 0; i-- ) {
|
2012-02-02 01:56:04 +04:00
|
|
|
if ( overlays[ i ].element == element ) {
|
2012-02-01 00:59:09 +04:00
|
|
|
return i;
|
2011-12-28 03:17:24 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
return -1;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @inner
|
2013-06-19 21:33:25 +04:00
|
|
|
* Determines whether the 'last best' tile for the area is better than the
|
2012-02-01 00:59:09 +04:00
|
|
|
* tile in question.
|
|
|
|
*/
|
|
|
|
function compareTiles( previousBest, tile ) {
|
|
|
|
if ( !previousBest ) {
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tile.visibility > previousBest.visibility ) {
|
|
|
|
return tile;
|
|
|
|
} else if ( tile.visibility == previousBest.visibility ) {
|
|
|
|
if ( tile.distance < previousBest.distance ) {
|
|
|
|
return tile;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
return previousBest;
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2011-12-28 03:17:24 +04:00
|
|
|
function finishLoadingImage( image, callback, successful, jobid ){
|
|
|
|
|
|
|
|
image.onload = null;
|
|
|
|
image.onabort = null;
|
|
|
|
image.onerror = null;
|
|
|
|
|
|
|
|
if ( jobid ) {
|
|
|
|
window.clearTimeout( jobid );
|
|
|
|
}
|
2013-02-26 19:19:48 +04:00
|
|
|
$.requestAnimationFrame( function() {
|
2011-12-28 03:17:24 +04:00
|
|
|
callback( image.src, successful ? image : null);
|
2013-02-26 19:19:48 +04:00
|
|
|
});
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2011-12-28 03:17:24 +04:00
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
function drawOverlays( viewport, overlays, container ){
|
|
|
|
var i,
|
|
|
|
length = overlays.length;
|
|
|
|
for ( i = 0; i < length; i++ ) {
|
|
|
|
drawOverlay( viewport, overlays[ i ], container );
|
2012-01-12 03:32:17 +04:00
|
|
|
}
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
function drawOverlay( viewport, overlay, container ){
|
|
|
|
|
|
|
|
overlay.position = viewport.pixelFromPoint(
|
2013-06-19 21:33:25 +04:00
|
|
|
overlay.bounds.getTopLeft(),
|
2012-02-01 00:59:09 +04:00
|
|
|
true
|
|
|
|
);
|
|
|
|
overlay.size = viewport.deltaPixelsFromPoints(
|
2013-06-19 21:33:25 +04:00
|
|
|
overlay.bounds.getSize(),
|
2012-02-01 00:59:09 +04:00
|
|
|
true
|
|
|
|
);
|
2013-08-14 23:43:49 +04:00
|
|
|
overlay.drawHTML( container, viewport );
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
function drawTiles( drawer, lastDrawn ){
|
2013-06-19 21:33:25 +04:00
|
|
|
var i,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile,
|
|
|
|
tileKey,
|
|
|
|
viewer,
|
|
|
|
viewport,
|
|
|
|
position,
|
|
|
|
tileSource,
|
|
|
|
collectionTileSource;
|
2012-02-01 00:59:09 +04:00
|
|
|
|
|
|
|
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
|
|
|
|
tile = lastDrawn[ i ];
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
//We dont actually 'draw' a collection tile, rather its used to house
|
|
|
|
//an overlay which does the drawing in its own viewport
|
|
|
|
if( drawer.viewport.collectionMode ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
tileKey = tile.x + '/' + tile.y;
|
|
|
|
viewport = drawer.viewport;
|
|
|
|
collectionTileSource = viewport.collectionTileSource;
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
if( !drawer.collectionOverlays[ tileKey ] ){
|
2013-06-19 21:33:25 +04:00
|
|
|
|
|
|
|
position = collectionTileSource.layout == 'horizontal' ?
|
2013-01-31 21:30:13 +04:00
|
|
|
tile.y + ( tile.x * collectionTileSource.rows ) :
|
2013-06-19 01:58:04 +04:00
|
|
|
tile.x + ( tile.y * collectionTileSource.rows );
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-06-19 01:58:04 +04:00
|
|
|
if (position < collectionTileSource.tileSources.length) {
|
|
|
|
tileSource = collectionTileSource.tileSources[ position ];
|
|
|
|
} else {
|
|
|
|
tileSource = null;
|
|
|
|
}
|
2013-01-31 21:30:13 +04:00
|
|
|
|
|
|
|
//$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position);
|
2013-01-24 08:00:11 +04:00
|
|
|
if( tileSource ){
|
|
|
|
drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({
|
2013-10-13 02:30:05 +04:00
|
|
|
hash: viewport.viewer.hash + "-" + tileKey,
|
2013-03-06 14:51:31 +04:00
|
|
|
element: $.makeNeutralElement( "div" ),
|
|
|
|
mouseNavEnabled: false,
|
|
|
|
showNavigator: false,
|
|
|
|
showSequenceControl: false,
|
|
|
|
showNavigationControl: false,
|
2013-01-24 08:00:11 +04:00
|
|
|
tileSources: [
|
|
|
|
tileSource
|
|
|
|
]
|
|
|
|
});
|
2013-06-19 21:33:25 +04:00
|
|
|
|
2013-02-13 07:40:08 +04:00
|
|
|
//TODO: IE seems to barf on this, not sure if its just the border
|
2013-06-19 21:33:25 +04:00
|
|
|
// but we probably need to clear this up with a better
|
2013-02-13 07:40:08 +04:00
|
|
|
// test of support for various css features
|
|
|
|
if( SUBPIXEL_RENDERING ){
|
|
|
|
viewer.element.style.border = '1px solid rgba(255,255,255,0.38)';
|
2013-06-19 21:33:25 +04:00
|
|
|
viewer.element.style['-webkit-box-reflect'] =
|
2013-02-13 07:40:08 +04:00
|
|
|
'below 0px -webkit-gradient('+
|
|
|
|
'linear,left '+
|
|
|
|
'top,left '+
|
|
|
|
'bottom,from(transparent),color-stop(62%,transparent),to(rgba(255,255,255,0.62))'+
|
|
|
|
')';
|
2013-06-19 21:33:25 +04:00
|
|
|
}
|
2013-01-24 08:00:11 +04:00
|
|
|
|
|
|
|
drawer.addOverlay(
|
|
|
|
viewer.element,
|
|
|
|
tile.bounds
|
2013-06-19 21:33:25 +04:00
|
|
|
);
|
2013-01-24 08:00:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
}else{
|
|
|
|
viewer = drawer.collectionOverlays[ tileKey ];
|
|
|
|
if( viewer.viewport ){
|
|
|
|
viewer.viewport.resize( tile.size, true );
|
|
|
|
viewer.viewport.goHome( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 00:59:09 +04:00
|
|
|
} else {
|
2013-01-24 08:00:11 +04:00
|
|
|
|
|
|
|
if ( USE_CANVAS ) {
|
2013-08-14 01:39:22 +04:00
|
|
|
// TODO do this in a more performant way
|
|
|
|
// specifically, don't save,rotate,restore every time we draw a tile
|
2013-08-16 02:15:20 +04:00
|
|
|
if( drawer.viewport.degrees !== 0 ) {
|
|
|
|
offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees );
|
|
|
|
tile.drawCanvas( drawer.context );
|
|
|
|
restoreRotationChanges( tile, drawer.canvas, drawer.context );
|
|
|
|
} else {
|
|
|
|
tile.drawCanvas( drawer.context );
|
|
|
|
}
|
2013-01-24 08:00:11 +04:00
|
|
|
} else {
|
|
|
|
tile.drawHTML( drawer.canvas );
|
|
|
|
}
|
|
|
|
|
2013-02-02 00:18:53 +04:00
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.beingDrawn = true;
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2013-02-02 00:18:53 +04:00
|
|
|
|
|
|
|
if( drawer.debugMode ){
|
|
|
|
try{
|
|
|
|
drawDebugInfo( drawer, tile, lastDrawn.length, i );
|
|
|
|
}catch(e){
|
|
|
|
$.console.error(e);
|
|
|
|
}
|
|
|
|
}
|
2013-02-14 04:44:23 +04:00
|
|
|
|
|
|
|
if( drawer.viewer ){
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.viewer.raiseEvent( 'tile-drawn', {
|
2013-02-14 04:44:23 +04:00
|
|
|
tile: tile
|
|
|
|
});
|
|
|
|
}
|
2013-01-24 08:00:11 +04:00
|
|
|
}
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-02-01 00:59:09 +04:00
|
|
|
|
2013-08-14 01:39:22 +04:00
|
|
|
function offsetForRotation( tile, canvas, context, degrees ){
|
|
|
|
var cx = canvas.width / 2,
|
|
|
|
cy = canvas.height / 2,
|
|
|
|
px = tile.position.x - cx,
|
|
|
|
py = tile.position.y - cy;
|
|
|
|
|
|
|
|
context.save();
|
|
|
|
|
|
|
|
context.translate(cx, cy);
|
|
|
|
context.rotate( Math.PI / 180 * degrees);
|
|
|
|
tile.position.x = px;
|
|
|
|
tile.position.y = py;
|
|
|
|
}
|
|
|
|
|
|
|
|
function restoreRotationChanges( tile, canvas, context ){
|
|
|
|
var cx = canvas.width / 2,
|
|
|
|
cy = canvas.height / 2,
|
|
|
|
px = tile.position.x + cx,
|
|
|
|
py = tile.position.y + cy;
|
|
|
|
|
|
|
|
tile.position.x = px;
|
|
|
|
tile.position.y = py;
|
|
|
|
|
|
|
|
context.restore();
|
|
|
|
}
|
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
|
|
|
|
function drawDebugInfo( drawer, tile, count, i ){
|
|
|
|
|
|
|
|
if ( USE_CANVAS ) {
|
2013-03-01 17:14:35 +04:00
|
|
|
drawer.context.save();
|
2013-01-24 08:00:11 +04:00
|
|
|
drawer.context.lineWidth = 2;
|
2013-02-02 00:18:53 +04:00
|
|
|
drawer.context.font = 'small-caps bold 13px ariel';
|
2013-01-24 08:00:11 +04:00
|
|
|
drawer.context.strokeStyle = drawer.debugGridColor;
|
|
|
|
drawer.context.fillStyle = drawer.debugGridColor;
|
2013-06-19 21:33:25 +04:00
|
|
|
drawer.context.strokeRect(
|
|
|
|
tile.position.x,
|
|
|
|
tile.position.y,
|
|
|
|
tile.size.x,
|
|
|
|
tile.size.y
|
2013-01-24 08:00:11 +04:00
|
|
|
);
|
2013-02-13 07:40:08 +04:00
|
|
|
if( tile.x === 0 && tile.y === 0 ){
|
2013-01-24 08:00:11 +04:00
|
|
|
drawer.context.fillText(
|
|
|
|
"Zoom: " + drawer.viewport.getZoom(),
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y - 30
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
2013-06-19 21:33:25 +04:00
|
|
|
"Pan: " + drawer.viewport.getBounds().toString(),
|
|
|
|
tile.position.x,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y - 20
|
|
|
|
);
|
|
|
|
}
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Level: " + tile.level,
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 20
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Column: " + tile.x,
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 30
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Row: " + tile.y,
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 40
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Order: " + i + " of " + count,
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 50
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Size: " + tile.size.toString(),
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 60
|
|
|
|
);
|
|
|
|
drawer.context.fillText(
|
|
|
|
"Position: " + tile.position.toString(),
|
2013-06-19 21:33:25 +04:00
|
|
|
tile.position.x + 10,
|
2013-01-24 08:00:11 +04:00
|
|
|
tile.position.y + 70
|
|
|
|
);
|
2013-03-01 17:14:35 +04:00
|
|
|
drawer.context.restore();
|
2012-02-01 00:59:09 +04:00
|
|
|
}
|
2013-01-29 21:32:58 +04:00
|
|
|
}
|
2012-01-12 03:32:17 +04:00
|
|
|
|
2013-01-24 08:00:11 +04:00
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
}( OpenSeadragon ));
|