mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 06:36:11 +03:00
Merge pull request #402 from rdlester/master
Added ImageLoader; loads batches of images using async queue pattern
This commit is contained in:
commit
975828c057
23
Gruntfile.js
23
Gruntfile.js
@ -45,6 +45,7 @@ module.exports = function(grunt) {
|
||||
"src/referencestrip.js",
|
||||
"src/displayrectangle.js",
|
||||
"src/spring.js",
|
||||
"src/imageLoader.js",
|
||||
"src/tile.js",
|
||||
"src/overlay.js",
|
||||
"src/drawer.js",
|
||||
@ -73,11 +74,11 @@ module.exports = function(grunt) {
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
banner: "//! <%= pkg.name %> <%= pkg.version %>\n"
|
||||
+ "//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n"
|
||||
+ "//! Git commit: <%= gitInfo %>\n"
|
||||
+ "//! http://openseadragon.github.io\n"
|
||||
+ "//! License: http://openseadragon.github.io/license/\n\n",
|
||||
banner: "//! <%= pkg.name %> <%= pkg.version %>\n" +
|
||||
"//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n" +
|
||||
"//! Git commit: <%= gitInfo %>\n" +
|
||||
"//! http://openseadragon.github.io\n" +
|
||||
"//! License: http://openseadragon.github.io/license/\n\n",
|
||||
process: true
|
||||
},
|
||||
dist: {
|
||||
@ -182,9 +183,9 @@ module.exports = function(grunt) {
|
||||
// Creates a directory tree to be compressed into a package.
|
||||
grunt.registerTask("copy:package", function() {
|
||||
grunt.file.recurse("build/openseadragon", function(abspath, rootdir, subdir, filename) {
|
||||
var dest = packageDir
|
||||
+ (subdir ? subdir + "/" : '/')
|
||||
+ filename;
|
||||
var dest = packageDir +
|
||||
(subdir ? subdir + "/" : '/') +
|
||||
filename;
|
||||
grunt.file.copy(abspath, dest);
|
||||
});
|
||||
grunt.file.copy("changelog.txt", packageDir + "changelog.txt");
|
||||
@ -200,9 +201,9 @@ module.exports = function(grunt) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dest = releaseRoot
|
||||
+ (subdir ? subdir + "/" : '/')
|
||||
+ filename;
|
||||
var dest = releaseRoot +
|
||||
(subdir ? subdir + "/" : '/') +
|
||||
filename;
|
||||
|
||||
grunt.file.copy(abspath, dest);
|
||||
});
|
||||
|
120
src/drawer.js
120
src/drawer.js
@ -76,7 +76,7 @@ $.Drawer = function( options ) {
|
||||
|
||||
//internal state properties
|
||||
viewer: null,
|
||||
downloading: 0, // How many images are currently being loaded in parallel.
|
||||
imageLoader: new $.ImageLoader(),
|
||||
tilesMatrix: {}, // A '3d' dictionary [level][x][y] --> Tile.
|
||||
tilesLoaded: [], // An unordered list of Tiles with loaded images.
|
||||
coverage: {}, // A '3d' dictionary [level][x][y] --> Boolean.
|
||||
@ -92,7 +92,6 @@ $.Drawer = function( options ) {
|
||||
//configurable settings
|
||||
opacity: $.DEFAULT_SETTINGS.opacity,
|
||||
maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount,
|
||||
imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||
minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
|
||||
wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
|
||||
wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
|
||||
@ -296,77 +295,6 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the local cache to optimize user experience in certain cases. Because
|
||||
* the number of parallel image loads is configurable, if too many images
|
||||
* are currently being loaded, the request will be ignored. Since by
|
||||
* default drawer.imageLoaderLimit is 0, the native browser parallel
|
||||
* 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
|
||||
* 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.
|
||||
* @return {Boolean} loading - Whether the request was submitted or ignored
|
||||
* based on OpenSeadragon.DEFAULT_SETTINGS.imageLoaderLimit.
|
||||
*/
|
||||
loadImage: function( src, callback ) {
|
||||
var _this = this,
|
||||
loading = false,
|
||||
image,
|
||||
jobid,
|
||||
complete;
|
||||
|
||||
if ( !this.imageLoaderLimit ||
|
||||
this.downloading < this.imageLoaderLimit ) {
|
||||
|
||||
this.downloading++;
|
||||
|
||||
image = new Image();
|
||||
|
||||
if ( _this.crossOriginPolicy !== false ) {
|
||||
image.crossOrigin = _this.crossOriginPolicy;
|
||||
}
|
||||
|
||||
complete = function( imagesrc, resultingImage ){
|
||||
_this.downloading--;
|
||||
if (typeof ( callback ) == "function") {
|
||||
try {
|
||||
callback( resultingImage );
|
||||
} catch ( e ) {
|
||||
$.console.error(
|
||||
"%s while executing %s callback: %s",
|
||||
e.name,
|
||||
src,
|
||||
e.message,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
image.onload = function(){
|
||||
finishLoadingImage( image, complete, true, jobid );
|
||||
};
|
||||
|
||||
image.onabort = image.onerror = function(){
|
||||
finishLoadingImage( image, complete, false, jobid );
|
||||
};
|
||||
|
||||
jobid = window.setTimeout( function(){
|
||||
finishLoadingImage( image, complete, false, jobid );
|
||||
}, this.timeout );
|
||||
|
||||
loading = true;
|
||||
image.src = src;
|
||||
}
|
||||
|
||||
return loading;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether rotation is supported or not.
|
||||
* @method
|
||||
@ -436,13 +364,13 @@ function updateViewport( drawer ) {
|
||||
levelOpacity,
|
||||
levelVisibility;
|
||||
|
||||
//TODO
|
||||
// Reset tile's internal drawn state
|
||||
while ( drawer.lastDrawn.length > 0 ) {
|
||||
tile = drawer.lastDrawn.pop();
|
||||
tile.beingDrawn = false;
|
||||
}
|
||||
|
||||
//TODO
|
||||
// Clear canvas
|
||||
drawer.canvas.innerHTML = "";
|
||||
if ( drawer.useCanvas ) {
|
||||
if( drawer.canvas.width != viewportSize.x ||
|
||||
@ -470,7 +398,7 @@ function updateViewport( drawer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO
|
||||
// Calculate viewport rect / bounds
|
||||
if ( !drawer.wrapHorizontal ) {
|
||||
viewportTL.x = Math.max( viewportTL.x, 0 );
|
||||
viewportBR.x = Math.min( viewportBR.x, 1 );
|
||||
@ -480,10 +408,12 @@ function updateViewport( drawer ) {
|
||||
viewportBR.y = Math.min( viewportBR.y, drawer.normHeight );
|
||||
}
|
||||
|
||||
//TODO
|
||||
// Calculations for the interval of levels to draw
|
||||
// (above in initial var statement)
|
||||
// can return invalid intervals; fix that here if necessary
|
||||
lowestLevel = Math.min( lowestLevel, highestLevel );
|
||||
|
||||
//TODO
|
||||
// Update any level that will be drawn
|
||||
var drawLevel; // FIXME: drawLevel should have a more explanatory name
|
||||
for ( level = highestLevel; level >= lowestLevel; level-- ) {
|
||||
drawLevel = false;
|
||||
@ -528,7 +458,7 @@ function updateViewport( drawer ) {
|
||||
optimalRatio - renderPixelRatioT
|
||||
);
|
||||
|
||||
//TODO
|
||||
// Update the level and keep track of 'best' tile to load
|
||||
best = updateLevel(
|
||||
drawer,
|
||||
haveDrawn,
|
||||
@ -542,16 +472,17 @@ function updateViewport( drawer ) {
|
||||
best
|
||||
);
|
||||
|
||||
//TODO
|
||||
// Stop the loop if lower-res tiles would all be covered by
|
||||
// already drawn tiles
|
||||
if ( providesCoverage( drawer.coverage, level ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
// Perform the actual drawing
|
||||
drawTiles( drawer, drawer.lastDrawn );
|
||||
|
||||
//TODO
|
||||
// Load the new 'best' tile
|
||||
if ( best ) {
|
||||
loadTile( drawer, best, currentTime );
|
||||
// because we haven't finished drawing, so
|
||||
@ -756,18 +687,18 @@ function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, normHeig
|
||||
return tile;
|
||||
}
|
||||
|
||||
|
||||
function loadTile( drawer, tile, time ) {
|
||||
if( drawer.viewport.collectionMode ){
|
||||
drawer.midUpdate = false;
|
||||
onTileLoad( drawer, tile, time );
|
||||
} else {
|
||||
tile.loading = drawer.loadImage(
|
||||
tile.url,
|
||||
function( image ){
|
||||
tile.loading = true;
|
||||
drawer.imageLoader.addJob({
|
||||
src: tile.url,
|
||||
callback: function( image ){
|
||||
onTileLoad( drawer, tile, time, image );
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1018,21 +949,6 @@ function compareTiles( previousBest, tile ) {
|
||||
return previousBest;
|
||||
}
|
||||
|
||||
function finishLoadingImage( image, callback, successful, jobid ){
|
||||
|
||||
image.onload = null;
|
||||
image.onabort = null;
|
||||
image.onerror = null;
|
||||
|
||||
if ( jobid ) {
|
||||
window.clearTimeout( jobid );
|
||||
}
|
||||
$.requestAnimationFrame( function() {
|
||||
callback( image.src, successful ? image : null);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function drawTiles( drawer, lastDrawn ){
|
||||
var i,
|
||||
tile,
|
||||
|
171
src/imageLoader.js
Normal file
171
src/imageLoader.js
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* OpenSeadragon - ImageLoader
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 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( $ ){
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @class ImageJob
|
||||
* @classdesc Handles loading a single image for use in a single {@link OpenSeadragon.Tile}.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @param {String} source - URL of image to download.
|
||||
* @param {Function} callback - Called once image has finished downloading.
|
||||
*/
|
||||
function ImageJob ( options ) {
|
||||
|
||||
$.extend( true, this, {
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobId: null,
|
||||
}, options );
|
||||
|
||||
/**
|
||||
* Image object which will contain downloaded image.
|
||||
* @member {Image} image
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
ImageJob.prototype = {
|
||||
|
||||
/**
|
||||
* Initiates downloading of associated image.
|
||||
* @method
|
||||
*/
|
||||
start: function(){
|
||||
var _this = this;
|
||||
|
||||
this.image = new Image();
|
||||
|
||||
if ( _this.crossOriginPolicy !== false ) {
|
||||
this.image.crossOrigin = this.crossOriginPolicy;
|
||||
}
|
||||
|
||||
this.image.onload = function(){
|
||||
_this.finish( true );
|
||||
};
|
||||
this.image.onabort = this.image.onerror = function(){
|
||||
_this.finish( false );
|
||||
};
|
||||
|
||||
this.jobId = window.setTimeout( function(){
|
||||
_this.finish( false );
|
||||
}, this.timeout);
|
||||
|
||||
this.image.src = this.src;
|
||||
},
|
||||
|
||||
finish: function( successful ) {
|
||||
this.image.onload = this.image.onerror = this.image.onabort = null;
|
||||
if (!successful) {
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
if ( this.jobId ) {
|
||||
window.clearTimeout( this.jobId );
|
||||
}
|
||||
|
||||
this.callback( this );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @classdesc Handles downloading of a set of images using asynchronous queue pattern.
|
||||
*/
|
||||
$.ImageLoader = function() {
|
||||
|
||||
$.extend( true, this, {
|
||||
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||
jobQueue: [],
|
||||
jobsInProgress: 0
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$.ImageLoader.prototype = {
|
||||
|
||||
/**
|
||||
* Add an unloaded image to the loader queue.
|
||||
* @method
|
||||
* @param {String} src - URL of image to download.
|
||||
* @param {Function} callback - Called once image has been downloaded.
|
||||
*/
|
||||
addJob: function( options ) {
|
||||
var _this = this,
|
||||
complete = function( job ) {
|
||||
completeJob( _this, job, options.callback );
|
||||
},
|
||||
jobOptions = {
|
||||
src: options.src,
|
||||
callback: complete
|
||||
},
|
||||
newJob = new ImageJob( jobOptions );
|
||||
|
||||
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
||||
newJob.start();
|
||||
this.jobsInProgress++;
|
||||
}
|
||||
else {
|
||||
this.jobQueue.push( newJob );
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleans up ImageJob once completed.
|
||||
* @method
|
||||
* @private
|
||||
* @param loader - ImageLoader used to start job.
|
||||
* @param job - The ImageJob that has completed.
|
||||
* @param callback - Called once cleanup is finished.
|
||||
*/
|
||||
function completeJob( loader, job, callback ) {
|
||||
var nextJob;
|
||||
|
||||
loader.jobsInProgress--;
|
||||
|
||||
if ( (!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
||||
nextJob = loader.jobQueue.shift();
|
||||
nextJob.start();
|
||||
}
|
||||
|
||||
callback( job.image );
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
Loading…
Reference in New Issue
Block a user