Fix comments on #2148: part 2. Better commens on the new TileSource API. Deprecation if 'tile-loaded' image event parameter. Unwrap ImageJob and add userData property.

This commit is contained in:
Jirka 2022-04-29 23:45:01 +02:00
parent 45a7a4aaf3
commit d82fd35323
6 changed files with 71 additions and 76 deletions

View File

@ -35,7 +35,6 @@
(function($){ (function($){
/** /**
* @private
* @class ImageJob * @class ImageJob
* @classdesc Handles downloading of a single image. * @classdesc Handles downloading of a single image.
* @param {Object} options - Options for this ImageJob. * @param {Object} options - Options for this ImageJob.
@ -50,7 +49,7 @@
* @param {Function} [options.abort] - Called when this image job is aborted. * @param {Function} [options.abort] - Called when this image job is aborted.
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete. * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
*/ */
function ImageJob (options) { $.ImageJob = function(options) {
$.extend(true, this, { $.extend(true, this, {
timeout: $.DEFAULT_SETTINGS.timeout, timeout: $.DEFAULT_SETTINGS.timeout,
@ -63,9 +62,16 @@ function ImageJob (options) {
* @memberof OpenSeadragon.ImageJob# * @memberof OpenSeadragon.ImageJob#
*/ */
this.data = null; this.data = null;
}
ImageJob.prototype = { /**
* User workspace to populate with helper variables
* @member {*} user data, for people to append their data
* @memberof OpenSeadragon.ImageJob#
*/
this.userData = {};
};
$.ImageJob.prototype = {
errorMsg: null, errorMsg: null,
/** /**
@ -100,7 +106,6 @@ ImageJob.prototype = {
this.callback(this); this.callback(this);
} }
}; };
/** /**
@ -146,7 +151,7 @@ $.ImageLoader.prototype = {
if (!options.source) { if (!options.source) {
$.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' + $.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' +
'TileSource since new API defines how images are fetched. Creating a dummy TileSource.'); 'TileSource since new API defines how images are fetched. Creating a dummy TileSource.');
let implementation = $.TileSource.prototype; var implementation = $.TileSource.prototype;
options.source = { options.source = {
downloadTileStart: implementation.downloadTileStart, downloadTileStart: implementation.downloadTileStart,
downloadTileAbort: implementation.downloadTileAbort, downloadTileAbort: implementation.downloadTileAbort,
@ -170,7 +175,7 @@ $.ImageLoader.prototype = {
abort: options.abort, abort: options.abort,
timeout: this.timeout timeout: this.timeout
}, },
newJob = new ImageJob(jobOptions); newJob = new $.ImageJob(jobOptions);
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) { if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
newJob.start(); newJob.start();

View File

@ -298,7 +298,9 @@ $.Tile.prototype = {
if ( !this.element ) { if ( !this.element ) {
var image = this.image; var image = this.image;
if (!this.image) return; if (!this.image) {
return;
}
this.element = $.makeNeutralElement( "div" ); this.element = $.makeNeutralElement( "div" );
this.imgElement = image.cloneNode(); this.imgElement = image.cloneNode();
@ -335,7 +337,7 @@ $.Tile.prototype = {
* @return {Image} * @return {Image}
*/ */
get image() { get image() {
this.cacheImageRecord.getImage(); return this.cacheImageRecord.getImage();
}, },
/** /**
@ -367,8 +369,7 @@ $.Tile.prototype = {
var position = this.position.times($.pixelDensityRatio), var position = this.position.times($.pixelDensityRatio),
size = this.size.times($.pixelDensityRatio), size = this.size.times($.pixelDensityRatio),
rendered, rendered;
hasTransparency;
if (!this.context2D && !this.cacheImageRecord) { if (!this.context2D && !this.cacheImageRecord) {
$.console.warn( $.console.warn(

View File

@ -139,8 +139,8 @@ $.TileCache.prototype = {
if (!imageRecord) { if (!imageRecord) {
if (!options.data) { if (!options.data) {
$.console.error("[TileCache.cacheTile] options.image was renamed to options.data. '.image' attribute " $.console.error("[TileCache.cacheTile] options.image was renamed to options.data. '.image' attribute " +
+ "has been deprecated and will be removed in the future."); "has been deprecated and will be removed in the future.");
options.data = options.image; options.data = options.image;
} }

View File

@ -1649,7 +1649,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @private * @private
* @inner * @inner
* @param {OpenSeadragon.Tile} tile * @param {OpenSeadragon.Tile} tile
* @param {*} data image data * @param {*} data image data, the output of TileSource.prototype.downloadTileFinish(), by default Image object
* @param {Number || undefined} cutoff * @param {Number || undefined} cutoff
* @param {XMLHttpRequest || undefined} tileRequest * @param {XMLHttpRequest || undefined} tileRequest
*/ */
@ -1667,7 +1667,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
if (increment === 0) { if (increment === 0) {
tile.loading = false; tile.loading = false;
tile.loaded = true; tile.loaded = true;
tile.hasTransparency = this.source.hasTransparency( tile.hasTransparency = _this.source.hasTransparency(
tile.context2D, tile.url, tile.ajaxHeaders, tile.postData tile.context2D, tile.url, tile.ajaxHeaders, tile.postData
); );
if (!tile.context2D) { if (!tile.context2D) {
@ -1689,8 +1689,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @event tile-loaded * @event tile-loaded
* @memberof OpenSeadragon.Viewer * @memberof OpenSeadragon.Viewer
* @type {object} * @type {object}
* @property {Image|*} image - The image (data) of the tile. * @property {Image || *} image - The image (data) of the tile. Deprecated.
* @property {*} data - image data * @property {*} data - image data, the output of TileSource.prototype.downloadTileFinish(),
* by default Image object
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile. * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded. * @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
* @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable). * @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
@ -1704,7 +1705,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
tiledImage: this, tiledImage: this,
tileRequest: tileRequest, tileRequest: tileRequest,
get image() { get image() {
$.console.error("[tile-loaded] event property 'image' has been deprecated and will be removed in the future"); $.console.error("[tile-loaded] event property 'image' has been deprecated and will be removed.");
return data; return data;
}, },
data: data, data: data,

View File

@ -720,33 +720,38 @@ $.TileSource.prototype = {
}, },
/** /**
* Download tile data * Download tile data.
* @param {object} context job context that you have to call finish(...) on. It also contains abort(...) function * Note that if you override any of downloadTile*() functions, you should override all of them.
* that can be called to abort the job. * @param {ImageJob} context job context that you have to call finish(...) on.
* @param {String} [context.src] - URL of image to download. * @param {String} [context.src] - URL of image to download.
* @param {String} [context.loadWithAjax] - Whether to load this image with AJAX. * @param {String} [context.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [context.ajaxHeaders] - Headers to add to the image request if using AJAX. * @param {String} [context.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads * @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [context.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @param {String} [context.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null * see TileSrouce::getPostData) or null
* @param {Function} [context.callback] - Called once image has been downloaded. *
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
* @param {Function} [context.callback] - Called automatically once image has been downloaded (triggered by finish).
* @param {Function} [context.finish] - Should be called unless abort() was executed, e.g. on all occasions,
* be it successful or unsuccessful request.
* @param {Function} [context.abort] - Called when this image job is aborted. * @param {Function} [context.abort] - Called when this image job is aborted.
* @param {Number} [context.timeout] - The max number of milliseconds that this image job may take to complete. * @param {Number} [context.timeout] - The max number of milliseconds that this image job may take to complete.
*/ */
downloadTileStart: function (context) { downloadTileStart: function (context) {
context.image = new Image(); var dataStore = context.userData;
dataStore.image = new Image();
context.image.onload = function(){ dataStore.image.onload = function(){
context.finish(true); context.finish(true);
}; };
context.image.onabort = context.image.onerror = function() { dataStore.image.onabort = dataStore.image.onerror = function() {
context.finish(false, "Image load aborted"); context.finish(false, "Image load aborted");
}; };
// Load the tile with an AJAX request if the loadWithAjax option is // Load the tile with an AJAX request if the loadWithAjax option is
// set. Otherwise load the image by setting the source proprety of the image object. // set. Otherwise load the image by setting the source proprety of the image object.
if (context.loadWithAjax) { if (context.loadWithAjax) {
context.request = $.makeAjaxRequest({ dataStore.request = $.makeAjaxRequest({
url: context.src, url: context.src,
withCredentials: context.ajaxWithCredentials, withCredentials: context.ajaxWithCredentials,
headers: context.ajaxHeaders, headers: context.ajaxHeaders,
@ -778,7 +783,7 @@ $.TileSource.prototype = {
} else { } else {
// Create a URL for the blob data and make it the source of the image object. // Create a URL for the blob data and make it the source of the image object.
// This will still trigger Image.onload to indicate a successful tile load. // This will still trigger Image.onload to indicate a successful tile load.
context.image.src = (window.URL || window.webkitURL).createObjectURL(blb); dataStore.image.src = (window.URL || window.webkitURL).createObjectURL(blb);
} }
}, },
error: function(request) { error: function(request) {
@ -787,50 +792,59 @@ $.TileSource.prototype = {
}); });
} else { } else {
if (context.crossOriginPolicy !== false) { if (context.crossOriginPolicy !== false) {
context.image.crossOrigin = context.crossOriginPolicy; dataStore.image.crossOrigin = context.crossOriginPolicy;
} }
dataStore.image.src = context.src;
context.image.src = context.src;
} }
}, },
/** /**
* Provide means of aborting the execution. * Provide means of aborting the execution.
* @param {object} context job, the same object as with downloadTileStart(..) * Note that if you override any of downloadTile*() functions, you should override all of them.
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
*/ */
downloadTileAbort: function (context) { downloadTileAbort: function (context) {
context.request.abort(); context.userData.request.abort();
}, },
/** /**
* @param {object} context job, the same object as with downloadTileStart(..) * Note that if you override any of downloadTile*() functions, you should override all of them,
* unless you just want to for example here change the format of the data.
* Note that unless this function returns Image object, you should also override *TileCache() functions
* to re-define how the data is being cached.
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
* @param successful true if successful * @param successful true if successful
* @return {null|*} null to indicate missing data or data object * @return {* || null} tile data in a format you want to have in the system, or null to indicate missing data
* for example, can return default value if the request was unsuccessful such as default error image * also for example, a default value (white image? error image?) can be returned if the request was unsuccessful
*/ */
downloadTileFinish: function (context, successful) { downloadTileFinish: function (context, successful) {
if (!context.image) { var image = context.userData.image;
if (!image) {
return null; return null;
} }
context.image.onload = context.image.onerror = context.image.onabort = null; image.onload = image.onerror = image.onabort = null;
if (!successful) { return successful ? image : null;
return null;
}
return context.image;
}, },
/** /**
* Create cache object from the result of the download process * Create cache object from the result of the download process. The
* @param {Tile} tile instance the cache was created with * cacheObject parameter should be used to attach the data to, there are no
* conventions on how it should be stored - all the logic is implemented within *TileCache() functions.
*
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object * @param {object} cacheObject context cache object
* @param {*} data the result of downloadTileFinish() function * @param {*} data the result of downloadTileFinish() function
* @param {Tile} tile instance the cache was created with
*/ */
createTileCache: function(cacheObject, data, tile) { createTileCache: function(cacheObject, data, tile) {
cacheObject._data = data; cacheObject._data = data;
}, },
/** /**
* Cache object destructor, unset all properties to allow GC collection. * Cache object destructor, unset all properties you created to allow GC collection.
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object * @param {object} cacheObject context cache object
*/ */
destroyTileCache: function (cacheObject) { destroyTileCache: function (cacheObject) {
@ -840,6 +854,7 @@ $.TileSource.prototype = {
/** /**
* Raw data getter * Raw data getter
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object * @param {object} cacheObject context cache object
* @return {*} cache data * @return {*} cache data
*/ */
@ -851,6 +866,7 @@ $.TileSource.prototype = {
* Compatibility image element getter * Compatibility image element getter
* - plugins might need image representation of the data * - plugins might need image representation of the data
* - div HTML rendering relies on image element presence * - div HTML rendering relies on image element presence
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object * @param {object} cacheObject context cache object
* @return {Image} cache data as an Image * @return {Image} cache data as an Image
*/ */
@ -862,6 +878,7 @@ $.TileSource.prototype = {
* Compatibility context 2D getter * Compatibility context 2D getter
* - most heavily used rendering method is a canvas-based approach, * - most heavily used rendering method is a canvas-based approach,
* convert the data to a canvas and return it's 2D context * convert the data to a canvas and return it's 2D context
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object * @param {object} cacheObject context cache object
* @return {CanvasRenderingContext2D} context of the canvas representation of the cache data * @return {CanvasRenderingContext2D} context of the canvas representation of the cache data
*/ */

View File

@ -2,35 +2,6 @@
(function() { (function() {
var tileSourceCacheAPI = {
createTileCache: function(cacheObject, data, tile) {
cacheObject._data = data;
},
destroyTileCache: function (cacheObject) {
cacheObject._data = null;
cacheObject._renderedContext = null;
},
getTileCacheData: function(cacheObject) {
return cacheObject._data;
},
getTileCacheDataAsImage: function(cacheObject) {
return cacheObject._data; //the data itself by default is Image
},
getTileCacheDataAsContext2D: function(cacheObject) {
if (!cacheObject._renderedContext) {
var canvas = document.createElement( 'canvas' );
canvas.width = cacheObject._data.width;
canvas.height = cacheObject._data.height;
cacheObject._renderedContext = canvas.getContext('2d');
cacheObject._renderedContext.drawImage( cacheObject._data, 0, 0 );
//since we are caching the prerendered image on a canvas
//allow the image to not be held in memory
cacheObject._data = null;
}
return cacheObject._renderedContext;
}
};
// ---------- // ----------
QUnit.module('TileCache', { QUnit.module('TileCache', {
beforeEach: function () { beforeEach: function () {
@ -49,11 +20,11 @@
}; };
var fakeTiledImage0 = { var fakeTiledImage0 = {
viewer: fakeViewer, viewer: fakeViewer,
source: tileSourceCacheAPI source: OpenSeadragon.TileSource.prototype
}; };
var fakeTiledImage1 = { var fakeTiledImage1 = {
viewer: fakeViewer, viewer: fakeViewer,
source: tileSourceCacheAPI source: OpenSeadragon.TileSource.prototype
}; };
var fakeTile0 = { var fakeTile0 = {
@ -106,7 +77,7 @@
}; };
var fakeTiledImage0 = { var fakeTiledImage0 = {
viewer: fakeViewer, viewer: fakeViewer,
source: tileSourceCacheAPI source: OpenSeadragon.TileSource.prototype
}; };
var fakeTile0 = { var fakeTile0 = {