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($){
/**
* @private
* @class ImageJob
* @classdesc Handles downloading of a single image.
* @param {Object} options - Options for this ImageJob.
@ -50,7 +49,7 @@
* @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.
*/
function ImageJob (options) {
$.ImageJob = function(options) {
$.extend(true, this, {
timeout: $.DEFAULT_SETTINGS.timeout,
@ -63,9 +62,16 @@ function ImageJob (options) {
* @memberof OpenSeadragon.ImageJob#
*/
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,
/**
@ -100,7 +106,6 @@ ImageJob.prototype = {
this.callback(this);
}
};
/**
@ -146,7 +151,7 @@ $.ImageLoader.prototype = {
if (!options.source) {
$.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' +
'TileSource since new API defines how images are fetched. Creating a dummy TileSource.');
let implementation = $.TileSource.prototype;
var implementation = $.TileSource.prototype;
options.source = {
downloadTileStart: implementation.downloadTileStart,
downloadTileAbort: implementation.downloadTileAbort,
@ -170,7 +175,7 @@ $.ImageLoader.prototype = {
abort: options.abort,
timeout: this.timeout
},
newJob = new ImageJob(jobOptions);
newJob = new $.ImageJob(jobOptions);
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
newJob.start();

View File

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

View File

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

View File

@ -1649,7 +1649,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @private
* @inner
* @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 {XMLHttpRequest || undefined} tileRequest
*/
@ -1667,7 +1667,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
if (increment === 0) {
tile.loading = false;
tile.loaded = true;
tile.hasTransparency = this.source.hasTransparency(
tile.hasTransparency = _this.source.hasTransparency(
tile.context2D, tile.url, tile.ajaxHeaders, tile.postData
);
if (!tile.context2D) {
@ -1689,8 +1689,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @event tile-loaded
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {Image|*} image - The image (data) of the tile.
* @property {*} data - image data
* @property {Image || *} image - The image (data) of the tile. Deprecated.
* @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.Tile} tile - The tile which has been loaded.
* @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,
tileRequest: tileRequest,
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;
},
data: data,

View File

@ -720,33 +720,38 @@ $.TileSource.prototype = {
},
/**
* Download tile data
* @param {object} context job context that you have to call finish(...) on. It also contains abort(...) function
* that can be called to abort the job.
* Download tile data.
* Note that if you override any of downloadTile*() functions, you should override all of them.
* @param {ImageJob} context job context that you have to call finish(...) on.
* @param {String} [context.src] - URL of image to download.
* @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.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [context.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* 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 {Number} [context.timeout] - The max number of milliseconds that this image job may take to complete.
*/
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.image.onabort = context.image.onerror = function() {
dataStore.image.onabort = dataStore.image.onerror = function() {
context.finish(false, "Image load aborted");
};
// 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.
if (context.loadWithAjax) {
context.request = $.makeAjaxRequest({
dataStore.request = $.makeAjaxRequest({
url: context.src,
withCredentials: context.ajaxWithCredentials,
headers: context.ajaxHeaders,
@ -778,7 +783,7 @@ $.TileSource.prototype = {
} else {
// 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.
context.image.src = (window.URL || window.webkitURL).createObjectURL(blb);
dataStore.image.src = (window.URL || window.webkitURL).createObjectURL(blb);
}
},
error: function(request) {
@ -787,50 +792,59 @@ $.TileSource.prototype = {
});
} else {
if (context.crossOriginPolicy !== false) {
context.image.crossOrigin = context.crossOriginPolicy;
dataStore.image.crossOrigin = context.crossOriginPolicy;
}
context.image.src = context.src;
dataStore.image.src = context.src;
}
},
/**
* 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) {
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
* @return {null|*} null to indicate missing data or data object
* for example, can return default value if the request was unsuccessful such as default error image
* @return {* || null} tile data in a format you want to have in the system, or null to indicate missing data
* also for example, a default value (white image? error image?) can be returned if the request was unsuccessful
*/
downloadTileFinish: function (context, successful) {
if (!context.image) {
var image = context.userData.image;
if (!image) {
return null;
}
context.image.onload = context.image.onerror = context.image.onabort = null;
if (!successful) {
return null;
}
return context.image;
image.onload = image.onerror = image.onabort = null;
return successful ? image : null;
},
/**
* Create cache object from the result of the download process
* @param {Tile} tile instance the cache was created with
* Create cache object from the result of the download process. The
* 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 {*} data the result of downloadTileFinish() function
* @param {Tile} tile instance the cache was created with
*/
createTileCache: function(cacheObject, data, tile) {
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
*/
destroyTileCache: function (cacheObject) {
@ -840,6 +854,7 @@ $.TileSource.prototype = {
/**
* Raw data getter
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @return {*} cache data
*/
@ -851,6 +866,7 @@ $.TileSource.prototype = {
* Compatibility image element getter
* - plugins might need image representation of the data
* - 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
* @return {Image} cache data as an Image
*/
@ -862,6 +878,7 @@ $.TileSource.prototype = {
* Compatibility context 2D getter
* - most heavily used rendering method is a canvas-based approach,
* 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
* @return {CanvasRenderingContext2D} context of the canvas representation of the cache data
*/

View File

@ -2,35 +2,6 @@
(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', {
beforeEach: function () {
@ -49,11 +20,11 @@
};
var fakeTiledImage0 = {
viewer: fakeViewer,
source: tileSourceCacheAPI
source: OpenSeadragon.TileSource.prototype
};
var fakeTiledImage1 = {
viewer: fakeViewer,
source: tileSourceCacheAPI
source: OpenSeadragon.TileSource.prototype
};
var fakeTile0 = {
@ -106,7 +77,7 @@
};
var fakeTiledImage0 = {
viewer: fakeViewer,
source: tileSourceCacheAPI
source: OpenSeadragon.TileSource.prototype
};
var fakeTile0 = {