mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 14:46:10 +03:00
Add base drawer options and fix docs. Implement 'simple internal cache' for drawer data, optional to use.
This commit is contained in:
parent
cae6ec6bee
commit
d91df0126b
@ -47,11 +47,9 @@
|
||||
*/
|
||||
|
||||
class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
||||
constructor(options){
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.declareSupportedDataFormats("context2d");
|
||||
|
||||
/**
|
||||
* The HTML element (canvas) that this drawer uses for drawing
|
||||
* @member {Element} canvas
|
||||
@ -71,7 +69,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
||||
* @memberof OpenSeadragon.CanvasDrawer#
|
||||
* @private
|
||||
*/
|
||||
this.context = this.canvas.getContext( '2d' );
|
||||
this.context = this.canvas.getContext('2d');
|
||||
|
||||
// Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
||||
// to the main canvas due to opacity. Lazily initialized.
|
||||
@ -100,6 +98,10 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
||||
return 'canvas';
|
||||
}
|
||||
|
||||
getSupportedDataFormats() {
|
||||
return ["context2d"];
|
||||
}
|
||||
|
||||
/**
|
||||
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
||||
* @returns {Element} the canvas to draw into
|
||||
@ -287,7 +289,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
||||
// Note: Disabled on iOS devices per default as it causes a native crash
|
||||
useSketch = true;
|
||||
|
||||
const context = tile.length && this.getCompatibleData(tile);
|
||||
const context = tile.length && this.getDataToDraw(tile);
|
||||
if (context) {
|
||||
sketchScale = context.canvas.width / (tile.size.x * $.pixelDensityRatio);
|
||||
} else {
|
||||
@ -572,7 +574,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
||||
return;
|
||||
}
|
||||
|
||||
const rendered = this.getCompatibleData(tile);
|
||||
const rendered = this.getDataToDraw(tile);
|
||||
if (!rendered) {
|
||||
return;
|
||||
}
|
||||
|
@ -354,8 +354,8 @@ $.DataTypeConvertor = class {
|
||||
}
|
||||
let edge = conversionPath[i];
|
||||
let y = edge.transform(tile, x);
|
||||
if (!y) {
|
||||
$.console.warn(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting to %s)`, edge.target);
|
||||
if (y === undefined) {
|
||||
$.console.error(`[OpenSeadragon.convertor.convert] data mid result undefined value (while converting using %s)`, edge);
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
//node.value holds the type string
|
||||
@ -389,8 +389,8 @@ $.DataTypeConvertor = class {
|
||||
/**
|
||||
* Destroy the data item given.
|
||||
* @param {string} type data type
|
||||
* @param {?} data
|
||||
* @return {OpenSeadragon.Promise<?>|undefined} promise resolution with data passed from constructor, or undefined
|
||||
* @param {any} data
|
||||
* @return {OpenSeadragon.Promise<any>|undefined} promise resolution with data passed from constructor, or undefined
|
||||
* if not such conversion exists
|
||||
*/
|
||||
destroy(data, type) {
|
||||
|
@ -34,7 +34,14 @@
|
||||
|
||||
(function( $ ){
|
||||
|
||||
const OpenSeadragon = $; // (re)alias back to OpenSeadragon for JSDoc
|
||||
/**
|
||||
* @typedef BaseDrawerOptions
|
||||
* @memberOf OpenSeadragon
|
||||
* @property {boolean} [detachedCache=false] specify whether the drawer should use
|
||||
* detached (=internal) cache object in case it has to perform type conversion
|
||||
*/
|
||||
|
||||
const OpenSeadragon = $; // (re)alias back to OpenSeadragon for JSDoc
|
||||
/**
|
||||
* @class OpenSeadragon.DrawerBase
|
||||
* @classdesc Base class for Drawers that handle rendering of tiles for an {@link OpenSeadragon.Viewer}.
|
||||
@ -54,7 +61,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
this.viewer = options.viewer;
|
||||
this.viewport = options.viewport;
|
||||
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
||||
this.options = options.options || {};
|
||||
this.options = $.extend({}, this.defaultOptions, options.options);
|
||||
|
||||
this.container = $.getElement( options.element );
|
||||
|
||||
@ -80,10 +87,24 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
this._checkInterfaceImplementation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for the current drawer.
|
||||
* The base implementation provides default shared options.
|
||||
* Overrides should enumerate all defaults or extend from this implementation.
|
||||
* return $.extend({}, super.options, { ... custom drawer instance options ... });
|
||||
* @returns {BaseDrawerOptions} common options
|
||||
*/
|
||||
get defaultOptions() {
|
||||
return {
|
||||
detachedCache: false
|
||||
};
|
||||
}
|
||||
|
||||
// protect the canvas member with a getter
|
||||
get canvas(){
|
||||
return this._renderingTarget;
|
||||
}
|
||||
|
||||
get element(){
|
||||
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
|
||||
return this.container;
|
||||
@ -98,24 +119,13 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define which data types are compatible for this drawer to work with.
|
||||
* See default type list in OpenSeadragon.DataTypeConvertor
|
||||
* @param formats
|
||||
*/
|
||||
declareSupportedDataFormats(...formats) {
|
||||
this._formats = formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve data types
|
||||
* @abstract
|
||||
* @return {[string]}
|
||||
*/
|
||||
getSupportedDataFormats() {
|
||||
if (!this._formats || this._formats.length < 1) {
|
||||
$.console.error("A drawer must define its supported rendering data types using declareSupportedDataFormats!");
|
||||
}
|
||||
return this._formats;
|
||||
throw "Drawer.getSupportedDataFormats must define its supported rendering data types!";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,26 +134,15 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
* value, the rendering _MUST NOT_ proceed. It should
|
||||
* await next animation frames and check again for availability.
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
* @return {any|null|false} null if cache not available
|
||||
*/
|
||||
getCompatibleData(tile) {
|
||||
getDataToDraw(tile) {
|
||||
const cache = tile.getCache(tile.cacheKey);
|
||||
if (!cache) {
|
||||
$.console.warn("Attempt to draw tile %s when not cached!", tile);
|
||||
return null;
|
||||
}
|
||||
|
||||
const formats = this.getSupportedDataFormats();
|
||||
if (!formats.includes(cache.type)) {
|
||||
cache.transformTo(formats.length > 1 ? formats : formats[0]);
|
||||
return false; // type is NOT compatible
|
||||
}
|
||||
|
||||
// Cache in the process of loading, no-op
|
||||
if (!cache.loaded) {
|
||||
return false; // cache is NOT ready
|
||||
}
|
||||
|
||||
// Ensured compatible
|
||||
return cache.data;
|
||||
return cache.getDataForRendering(this.getSupportedDataFormats(), this.options.detachedCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,6 +229,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
*
|
||||
*/
|
||||
_checkInterfaceImplementation(){
|
||||
// TODO: is this necessary? why not throw just in the method itself?
|
||||
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
||||
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
||||
}
|
||||
|
@ -51,8 +51,6 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
constructor(options){
|
||||
super(options);
|
||||
|
||||
this.declareSupportedDataFormats("image");
|
||||
|
||||
/**
|
||||
* The HTML element (div) that this drawer uses for drawing
|
||||
* @member {Element} canvas
|
||||
@ -87,6 +85,10 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
return 'html';
|
||||
}
|
||||
|
||||
getSupportedDataFormats() {
|
||||
return ["image"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
||||
*/
|
||||
@ -212,7 +214,7 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
// content during animation of the container size.
|
||||
|
||||
if ( !tile.element ) {
|
||||
const image = this.getCompatibleData(tile);
|
||||
const image = this.getDataToDraw(tile);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
@ -761,12 +761,16 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} DrawerOptions
|
||||
* @typedef {Object.<string, Object>} DrawerOptions - give the renderer options (both shared - BaseDrawerOptions, and custom).
|
||||
* Supports arbitrary keys: you can register any drawer on the OpenSeadragon namespace, it will get automatically recognized
|
||||
* and its getType() implementation will define what key to specify the options with.
|
||||
* @memberof OpenSeadragon
|
||||
* @property {Object} webgl - options if the WebGLDrawer is used. No options are currently supported.
|
||||
* @property {Object} canvas - options if the CanvasDrawer is used. No options are currently supported.
|
||||
* @property {Object} html - options if the HTMLDrawer is used. No options are currently supported.
|
||||
* @property {Object} custom - options if a custom drawer is used. No options are currently supported.
|
||||
* @property {BaseDrawerOptions} [webgl] - options if the WebGLDrawer is used.
|
||||
* @property {BaseDrawerOptions} [canvas] - options if the CanvasDrawer is used.
|
||||
* @property {BaseDrawerOptions} [html] - options if the HTMLDrawer is used.
|
||||
* @property {BaseDrawerOptions} [custom] - options if a custom drawer is used.
|
||||
*
|
||||
* //Note: if you want to add change options for target drawer change type to {BaseDrawerOptions & MyDrawerOpts}
|
||||
*/
|
||||
|
||||
|
||||
@ -2637,6 +2641,10 @@ function OpenSeadragon( options ){
|
||||
* keys and booleans as values.
|
||||
*/
|
||||
setImageFormatsSupported: function(formats) {
|
||||
//TODO: how to deal with this within the data pipeline?
|
||||
// $.console.warn("setImageFormatsSupported method is deprecated. You should check that" +
|
||||
// " the system supports your TileSources by implementing corresponding data type convertors.");
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
$.extend(FILEFORMATS, formats);
|
||||
},
|
||||
|
105
src/tile.js
105
src/tile.js
@ -46,7 +46,7 @@
|
||||
* this tile failed to load? )
|
||||
* @param {String|Function} url The URL of this tile's image or a function that returns a url.
|
||||
* @param {CanvasRenderingContext2D} [context2D=undefined] The context2D of this tile if it
|
||||
* * is provided directly by the tile source. Deprecated: use Tile::setCache(...) instead.
|
||||
* * is provided directly by the tile source. Deprecated: use Tile::addCache(...) instead.
|
||||
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
|
||||
* @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).
|
||||
* @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the
|
||||
@ -143,24 +143,10 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
||||
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
|
||||
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
||||
}
|
||||
/**
|
||||
* The unique main cache key for this tile. Created automatically
|
||||
* from the given tiledImage.source.getTileHashKey(...) implementation.
|
||||
* @member {String} cacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.cacheKey = cacheKey;
|
||||
/**
|
||||
* By default equal to tile.cacheKey, marks a cache associated with this tile
|
||||
* that holds the cache original data (it was loaded with). In case you
|
||||
* change the tile data, the tile original data should be left with the cache
|
||||
* 'originalCacheKey' and the new, modified data should be stored in cache 'cacheKey'.
|
||||
* This key is used in cache resolution: in case new tile data is requested, if
|
||||
* this cache key exists in the cache it is loaded.
|
||||
* @member {String} originalCacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.originalCacheKey = this.cacheKey;
|
||||
|
||||
this._cKey = cacheKey || "";
|
||||
this._ocKey = cacheKey || "";
|
||||
|
||||
/**
|
||||
* Is this tile loaded?
|
||||
* @member {Boolean} loaded
|
||||
@ -304,6 +290,43 @@ $.Tile.prototype = {
|
||||
return this.level + "/" + this.x + "_" + this.y;
|
||||
},
|
||||
|
||||
/**
|
||||
* The unique main cache key for this tile. Created automatically
|
||||
* from the given tiledImage.source.getTileHashKey(...) implementation.
|
||||
* @member {String} cacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
get cacheKey() {
|
||||
return this._cKey;
|
||||
},
|
||||
set cacheKey(value) {
|
||||
if (this._cKey !== value) {
|
||||
let ref = this._caches[this._cKey];
|
||||
if (ref) {
|
||||
// make sure we free drawer internal cache
|
||||
ref.destroyInternalCache();
|
||||
}
|
||||
this._cKey = value;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* By default equal to tile.cacheKey, marks a cache associated with this tile
|
||||
* that holds the cache original data (it was loaded with). In case you
|
||||
* change the tile data, the tile original data should be left with the cache
|
||||
* 'originalCacheKey' and the new, modified data should be stored in cache 'cacheKey'.
|
||||
* This key is used in cache resolution: in case new tile data is requested, if
|
||||
* this cache key exists in the cache it is loaded.
|
||||
* @member {String} originalCacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
set originalCacheKey(value) {
|
||||
throw "Original Cache Key cannot be managed manually!";
|
||||
},
|
||||
get originalCacheKey() {
|
||||
return this._ocKey;
|
||||
},
|
||||
|
||||
/**
|
||||
* The Image object for this tile.
|
||||
* @member {Object} image
|
||||
@ -405,21 +428,21 @@ $.Tile.prototype = {
|
||||
* @deprecated
|
||||
*/
|
||||
set cacheImageRecord(value) {
|
||||
$.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::setCache.");
|
||||
$.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::addCache.");
|
||||
const cache = this._caches[this.cacheKey];
|
||||
|
||||
if (!value) {
|
||||
this.unsetCache(this.cacheKey);
|
||||
this.removeCache(this.cacheKey);
|
||||
} else {
|
||||
const _this = this;
|
||||
cache.await().then(x => _this.setCache(this.cacheKey, x, cache.type, false));
|
||||
cache.await().then(x => _this.addCache(this.cacheKey, x, cache.type, false));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the data to render for this tile
|
||||
* @param {string} type data type to require
|
||||
* @param {boolean?} [copy=true] whether to force copy retrieval
|
||||
* @param {boolean} [copy=true] whether to force copy retrieval
|
||||
* @return {*|undefined} data in the desired type, or undefined if a conversion is ongoing
|
||||
*/
|
||||
getData: function(type, copy = true) {
|
||||
@ -439,10 +462,10 @@ $.Tile.prototype = {
|
||||
/**
|
||||
* Get the original data data for this tile
|
||||
* @param {string} type data type to require
|
||||
* @param {boolean?} [copy=this.loaded] whether to force copy retrieval
|
||||
* @param {boolean} [copy=this.loaded] whether to force copy retrieval
|
||||
* @return {*|undefined} data in the desired type, or undefined if a conversion is ongoing
|
||||
*/
|
||||
getOriginalData: function(type, copy = true) {
|
||||
getOriginalData: function(type, copy = false) {
|
||||
if (!this.tiledImage) {
|
||||
return null; //async can access outside its lifetime
|
||||
}
|
||||
@ -457,12 +480,10 @@ $.Tile.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Set cache data
|
||||
* Set main cache data
|
||||
* @param {*} value
|
||||
* @param {?string} type data type to require
|
||||
* @param {boolean} [preserveOriginalData=true] if true and cacheKey === originalCacheKey,
|
||||
* then stores the underlying data as 'original' and changes the cacheKey to point
|
||||
* to a new data. This makes the Tile assigned to two cache objects.
|
||||
*/
|
||||
setData: function(value, type, preserveOriginalData = true) {
|
||||
if (!this.tiledImage) {
|
||||
@ -473,8 +494,9 @@ $.Tile.prototype = {
|
||||
//caches equality means we have only one cache:
|
||||
// change current pointer to a new cache and create it: new tiles will
|
||||
// not arrive at this data, but at originalCacheKey state
|
||||
// todo setting cache key makes the notification trigger ensure we do not do unnecessary stuff
|
||||
this.cacheKey = "mod://" + this.originalCacheKey;
|
||||
return this.setCache(this.cacheKey, value, type)._promise;
|
||||
return this.addCache(this.cacheKey, value, type)._promise;
|
||||
}
|
||||
//else overwrite cache
|
||||
const cache = this.getCache(this.cacheKey);
|
||||
@ -487,7 +509,7 @@ $.Tile.prototype = {
|
||||
|
||||
/**
|
||||
* Read tile cache data object (CacheRecord)
|
||||
* @param {string?} [key=this.cacheKey] cache key to read that belongs to this tile
|
||||
* @param {string} [key=this.cacheKey] cache key to read that belongs to this tile
|
||||
* @return {OpenSeadragon.CacheRecord}
|
||||
*/
|
||||
getCache: function(key = this.cacheKey) {
|
||||
@ -495,23 +517,25 @@ $.Tile.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: set cache might be misleading name since we do not update data,
|
||||
* this should be either changed or method renamed...
|
||||
* Set tile cache, possibly multiple with custom key
|
||||
* @param {string} key cache key, must be unique (we recommend re-using this.cacheTile
|
||||
* value and extend it with some another unique content, by default overrides the existing
|
||||
* main cache used for drawing, if not existing.
|
||||
* @param {*} data data to cache - this data will be sent to the TileSource API for refinement.
|
||||
* @param {*} data data to cache - this data will be IGNORED if cache already exists!
|
||||
* @param {?string} type data type, will be guessed if not provided
|
||||
* @param [_safely=true] private
|
||||
* @returns {OpenSeadragon.CacheRecord|null} - The cache record the tile was attached to.
|
||||
*/
|
||||
setCache: function(key, data, type = undefined, _safely = true) {
|
||||
addCache: function(key, data, type = undefined, _safely = true) {
|
||||
if (!this.tiledImage) {
|
||||
return null; //async can access outside its lifetime
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
if (!this.tiledImage.__typeWarningReported) {
|
||||
$.console.warn(this, "[Tile.setCache] called without type specification. " +
|
||||
$.console.warn(this, "[Tile.addCache] called without type specification. " +
|
||||
"Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
|
||||
this.tiledImage.__typeWarningReported = true;
|
||||
}
|
||||
@ -523,20 +547,17 @@ $.Tile.prototype = {
|
||||
// Need to get the supported type for rendering out of the active drawer.
|
||||
const supportedTypes = this.tiledImage.viewer.drawer.getSupportedDataFormats();
|
||||
const conversion = $.convertor.getConversionPath(type, supportedTypes);
|
||||
$.console.assert(conversion, "[Tile.setCache] data was set for the default tile cache we are unable" +
|
||||
$.console.assert(conversion, "[Tile.addCache] data was set for the default tile cache we are unable" +
|
||||
"to render. Make sure OpenSeadragon.convertor was taught to convert to (one of): " + type);
|
||||
}
|
||||
|
||||
if (!this.__cutoff) {
|
||||
//todo consider caching this on a tiled image level..
|
||||
this.__cutoff = this.tiledImage.source.getClosestLevel();
|
||||
}
|
||||
const cachedItem = this.tiledImage._tileCache.cacheTile({
|
||||
data: data,
|
||||
dataType: type,
|
||||
tile: this,
|
||||
cacheKey: key,
|
||||
cutoff: this.__cutoff,
|
||||
//todo consider caching this on a tiled image level
|
||||
cutoff: this.__cutoff || this.tiledImage.source.getClosestLevel(),
|
||||
});
|
||||
const havingRecord = this._caches[key];
|
||||
if (havingRecord !== cachedItem) {
|
||||
@ -561,12 +582,12 @@ $.Tile.prototype = {
|
||||
* @param {string} key cache key, required
|
||||
* @param {boolean} [freeIfUnused=true] set to false if zombie should be created
|
||||
*/
|
||||
unsetCache: function(key, freeIfUnused = true) {
|
||||
removeCache: function(key, freeIfUnused = true) {
|
||||
if (this.cacheKey === key) {
|
||||
if (this.cacheKey !== this.originalCacheKey) {
|
||||
this.cacheKey = this.originalCacheKey;
|
||||
} else {
|
||||
$.console.warn("[Tile.unsetCache] trying to remove the only cache that is used to draw the tile!");
|
||||
$.console.warn("[Tile.removeCache] trying to remove the only cache that is used to draw the tile!");
|
||||
}
|
||||
}
|
||||
if (this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused)) {
|
||||
@ -637,7 +658,7 @@ $.Tile.prototype = {
|
||||
this.imgElement = null;
|
||||
this.loaded = false;
|
||||
this.loading = false;
|
||||
this.cacheKey = this.originalCacheKey;
|
||||
this._cKey = this._ocKey;
|
||||
}
|
||||
};
|
||||
|
||||
|
337
src/tilecache.js
337
src/tilecache.js
@ -34,9 +34,12 @@
|
||||
|
||||
(function( $ ){
|
||||
|
||||
const DRAWER_INTERNAL_CACHE = Symbol("DRAWER_INTERNAL_CACHE");
|
||||
|
||||
/**
|
||||
* Cached Data Record, the cache object.
|
||||
* Keeps only latest object type required.
|
||||
* @class CacheRecord
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Cached Data Record, the cache object. Keeps only latest object type required.
|
||||
*
|
||||
* This class acts like the Maybe type:
|
||||
* - it has 'loaded' flag indicating whether the tile data is ready
|
||||
@ -44,16 +47,6 @@
|
||||
*
|
||||
* Furthermore, it has a 'getData' function that returns a promise resolving
|
||||
* with the value on the desired type passed to the function.
|
||||
*
|
||||
* @typedef {{
|
||||
* destroy: function,
|
||||
* revive: function,
|
||||
* save: function,
|
||||
* getDataAs: function,
|
||||
* transformTo: function,
|
||||
* data: ?,
|
||||
* loaded: boolean
|
||||
* }} OpenSeadragon.CacheRecord
|
||||
*/
|
||||
$.CacheRecord = class {
|
||||
constructor() {
|
||||
@ -82,7 +75,7 @@
|
||||
|
||||
/**
|
||||
* Await ongoing process so that we get cache ready on callback.
|
||||
* @returns {null|*}
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
await() {
|
||||
if (!this._promise) { //if not cache loaded, do not fail
|
||||
@ -128,31 +121,104 @@
|
||||
|
||||
/**
|
||||
* Access the cache record data indirectly. Preferred way of data access. Asynchronous.
|
||||
* @param {string?} [type=this.type]
|
||||
* @param {boolean?} [copy=true] if false and same type is retrieved as the cache type,
|
||||
* @param {string} [type=this.type]
|
||||
* @param {boolean} [copy=true] if false and same type is retrieved as the cache type,
|
||||
* copy is not performed: note that this is potentially dangerous as it might
|
||||
* introduce race conditions (you get a cache data direct reference you modify,
|
||||
* but others might also access it, for example drawers to draw the viewport).
|
||||
* introduce race conditions (you get a cache data direct reference you modify).
|
||||
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
|
||||
*/
|
||||
getDataAs(type = this._type, copy = true) {
|
||||
const referenceTile = this._tiles[0];
|
||||
if (this.loaded && type === this._type) {
|
||||
return copy ? $.convertor.copy(referenceTile, this._data, type) : this._promise;
|
||||
if (this.loaded) {
|
||||
if (type === this._type) {
|
||||
return copy ? $.convertor.copy(referenceTile, this._data, type) : this._promise;
|
||||
}
|
||||
return this._getDataAsUnsafe(referenceTile, this._data, type, copy);
|
||||
}
|
||||
return this._promise.then(data => this._getDataAsUnsafe(referenceTile, data, type, copy));
|
||||
}
|
||||
|
||||
_getDataAsUnsafe(referenceTile, data, type, copy) {
|
||||
//might get destroyed in meanwhile
|
||||
if (this._destroyed) {
|
||||
return undefined;
|
||||
}
|
||||
if (type !== this._type) {
|
||||
return $.convertor.convert(referenceTile, data, this._type, type);
|
||||
}
|
||||
if (copy) { //convert does not copy data if same type, do explicitly
|
||||
return $.convertor.copy(referenceTile, data, type);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Access of the data by drawers, synchronous function.
|
||||
*
|
||||
* When drawers access data, they can choose to access this data as internal copy
|
||||
*
|
||||
* @param {Array<string>} supportedTypes required data (or one of) type(s)
|
||||
* @param {boolean} keepInternalCopy if true, the cache keeps internally the drawer data
|
||||
* until 'setData' is called
|
||||
* todo: keep internal copy is not configurable and always enforced -> set as option for osd?
|
||||
* @returns {any|undefined} desired data if available, undefined if conversion must be done
|
||||
*/
|
||||
getDataForRendering(supportedTypes, keepInternalCopy = true) {
|
||||
if (this.loaded && supportedTypes.includes(this.type)) {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
return this._promise.then(data => {
|
||||
//might get destroyed in meanwhile
|
||||
if (this._destroyed) {
|
||||
return undefined;
|
||||
}
|
||||
if (type !== this._type) {
|
||||
return $.convertor.convert(referenceTile, data, this._type, type);
|
||||
}
|
||||
if (copy) { //convert does not copy data if same type, do explicitly
|
||||
return $.convertor.copy(referenceTile, data, type);
|
||||
}
|
||||
return data;
|
||||
let internalCache = this[DRAWER_INTERNAL_CACHE];
|
||||
if (keepInternalCopy && !internalCache) {
|
||||
this.prepareForRendering(supportedTypes, keepInternalCopy);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (internalCache) {
|
||||
internalCache.withTemporaryTileRef(this._tiles[0]);
|
||||
} else {
|
||||
internalCache = this;
|
||||
}
|
||||
|
||||
// Cache in the process of loading, no-op
|
||||
if (!internalCache.loaded) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!supportedTypes.includes(internalCache.type)) {
|
||||
internalCache.transformTo(supportedTypes.length > 1 ? supportedTypes : supportedTypes[0]);
|
||||
return undefined; // type is NOT compatible
|
||||
}
|
||||
|
||||
return internalCache.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param supportedTypes
|
||||
* @param keepInternalCopy
|
||||
* @return {OpenSeadragon.Promise<OpenSeadragon.SimpleCacheRecord|OpenSeadragon.CacheRecord>}
|
||||
*/
|
||||
prepareForRendering(supportedTypes, keepInternalCopy = true) {
|
||||
const referenceTile = this._tiles[0];
|
||||
// if not internal copy and we have no data, bypass rendering
|
||||
if (!this.loaded) {
|
||||
return $.Promise.resolve(this);
|
||||
}
|
||||
|
||||
// we can get here only if we want to render incompatible type
|
||||
let internalCache = this[DRAWER_INTERNAL_CACHE] = new $.SimpleCacheRecord();
|
||||
const conversionPath = $.convertor.getConversionPath(this.type, supportedTypes);
|
||||
if (!conversionPath) {
|
||||
$.console.error(`[getDataForRendering] Conversion conversion ${this.type} ---> ${supportedTypes} cannot be done!`);
|
||||
return $.Promise.resolve(this);
|
||||
}
|
||||
internalCache.withTemporaryTileRef(referenceTile);
|
||||
const selectedFormat = conversionPath[conversionPath.length - 1].target.value;
|
||||
return $.convertor.convert(referenceTile, this.data, this.type, selectedFormat).then(data => {
|
||||
internalCache.setDataAs(data, selectedFormat);
|
||||
return internalCache;
|
||||
});
|
||||
}
|
||||
|
||||
@ -161,7 +227,7 @@
|
||||
* Does nothing if the type equals to the current type. Asynchronous.
|
||||
* @param {string|[string]} type if array provided, the system will
|
||||
* try to optimize for the best type to convert to.
|
||||
* @return {OpenSeadragon.Promise<?>|*}
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
transformTo(type = this._type) {
|
||||
if (!this.loaded ||
|
||||
@ -198,6 +264,18 @@
|
||||
return this._promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* If cache ceases to be the primary one, free data
|
||||
* @private
|
||||
*/
|
||||
destroyInternalCache() {
|
||||
const internal = this[DRAWER_INTERNAL_CACHE];
|
||||
if (internal) {
|
||||
internal.destroy();
|
||||
delete this[DRAWER_INTERNAL_CACHE];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial state, prepare for usage.
|
||||
* Must not be called on active cache, e.g. first call destroy().
|
||||
@ -219,31 +297,30 @@
|
||||
delete this._conversionJobQueue;
|
||||
this._destroyed = true;
|
||||
|
||||
//make sure this gets destroyed even if loaded=false
|
||||
// make sure this gets destroyed even if loaded=false
|
||||
if (this.loaded) {
|
||||
$.convertor.destroy(this._data, this._type);
|
||||
this._tiles = null;
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
this._promise = null;
|
||||
this._destroySelfUnsafe(this._data, this._type);
|
||||
} else {
|
||||
const oldType = this._type;
|
||||
this._promise.then(x => {
|
||||
//ensure old data destroyed
|
||||
$.convertor.destroy(x, oldType);
|
||||
//might get revived...
|
||||
if (!this._destroyed) {
|
||||
return;
|
||||
}
|
||||
this._tiles = null;
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
this._promise = null;
|
||||
});
|
||||
this._promise.then(x => this._destroySelfUnsafe(x, oldType));
|
||||
}
|
||||
this.loaded = false;
|
||||
}
|
||||
|
||||
_destroySelfUnsafe(data, type) {
|
||||
// ensure old data destroyed
|
||||
$.convertor.destroy(data, type);
|
||||
this.destroyInternalCache();
|
||||
// might've got revived in meanwhile if async ...
|
||||
if (!this._destroyed) {
|
||||
return;
|
||||
}
|
||||
this._tiles = null;
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
this._promise = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tile dependency on this record
|
||||
* @param tile
|
||||
@ -342,6 +419,12 @@
|
||||
this._type = type;
|
||||
this._data = data;
|
||||
this._promise = $.Promise.resolve(data);
|
||||
const internal = this[DRAWER_INTERNAL_CACHE];
|
||||
if (internal) {
|
||||
// TODO: if update will be greedy uncomment (see below)
|
||||
//internal.withTemporaryTileRef(this._tiles[0]);
|
||||
internal.setDataAs(data, type);
|
||||
}
|
||||
this._triggerNeedsDraw();
|
||||
return this._promise;
|
||||
}
|
||||
@ -350,6 +433,12 @@
|
||||
this._type = type;
|
||||
this._data = data;
|
||||
this._promise = $.Promise.resolve(data);
|
||||
const internal = this[DRAWER_INTERNAL_CACHE];
|
||||
if (internal) {
|
||||
// TODO: if update will be greedy uncomment (see below)
|
||||
//internal.withTemporaryTileRef(this._tiles[0]);
|
||||
internal.setDataAs(data, type);
|
||||
}
|
||||
this._triggerNeedsDraw();
|
||||
return x;
|
||||
});
|
||||
@ -366,7 +455,7 @@
|
||||
referenceTile = this._tiles[0],
|
||||
conversionPath = convertor.getConversionPath(from, to);
|
||||
if (!conversionPath) {
|
||||
$.console.error(`[OpenSeadragon.convertor.convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
||||
$.console.error(`[CacheRecord._convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
||||
return; //no-op
|
||||
}
|
||||
|
||||
@ -381,21 +470,14 @@
|
||||
return $.Promise.resolve(x);
|
||||
}
|
||||
let edge = conversionPath[i];
|
||||
return $.Promise.resolve(edge.transform(referenceTile, x)).then(
|
||||
y => {
|
||||
if (!y) {
|
||||
$.console.error(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting using %s)`, edge);
|
||||
//try to recover using original data, but it returns inconsistent type (the log be hopefully enough)
|
||||
_this._data = from;
|
||||
_this._type = from;
|
||||
_this.loaded = true;
|
||||
return originalData;
|
||||
}
|
||||
//node.value holds the type string
|
||||
convertor.destroy(x, edge.origin.value);
|
||||
return convert(y, i + 1);
|
||||
}
|
||||
);
|
||||
let y = edge.transform(referenceTile, x);
|
||||
if (y === undefined) {
|
||||
_this.loaded = false;
|
||||
throw `[CacheRecord._convert] data mid result undefined value (while converting using ${edge}})`;
|
||||
}
|
||||
convertor.destroy(x, edge.origin.value);
|
||||
const result = $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||
return result.then(res => convert(res, i + 1));
|
||||
};
|
||||
|
||||
this.loaded = false;
|
||||
@ -406,6 +488,129 @@
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class SimpleCacheRecord
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Simple cache record without robust support for async access. Meant for internal use only.
|
||||
*
|
||||
* This class acts like the Maybe type:
|
||||
* - it has 'loaded' flag indicating whether the tile data is ready
|
||||
* - it has 'data' property that has value if loaded=true
|
||||
*
|
||||
* This class supposes synchronous access, no collision of transform calls.
|
||||
* It also does not record tiles nor allows cache/tile sharing.
|
||||
* @private
|
||||
*/
|
||||
$.SimpleCacheRecord = class {
|
||||
constructor(preferredTypes) {
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
this.loaded = false;
|
||||
this.format = Array.isArray(preferredTypes) ? preferredTypes : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync access to the data
|
||||
* @returns {any}
|
||||
*/
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync access to the current type
|
||||
* @returns {string}
|
||||
*/
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called before transformTo or setDataAs. To keep
|
||||
* compatible api with CacheRecord where tile refs are known.
|
||||
* @param {OpenSeadragon.Tile} referenceTile reference tile for conversion
|
||||
*/
|
||||
withTemporaryTileRef(referenceTile) {
|
||||
this._temporaryTileRef = referenceTile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform cache to desired type and get the data after conversion.
|
||||
* Does nothing if the type equals to the current type. Asynchronous.
|
||||
* @param {string|[string]} type if array provided, the system will
|
||||
* try to optimize for the best type to convert to.
|
||||
* @returns {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
transformTo(type) {
|
||||
$.console.assert(this._temporaryTileRef, "SimpleCacheRecord needs tile reference set before update operation!");
|
||||
const convertor = $.convertor,
|
||||
conversionPath = convertor.getConversionPath(this._type, type);
|
||||
if (!conversionPath) {
|
||||
$.console.error(`[SimpleCacheRecord.transformTo] Conversion conversion ${this._type} ---> ${type} cannot be done!`);
|
||||
return $.Promise.resolve(); //no-op
|
||||
}
|
||||
|
||||
const stepCount = conversionPath.length,
|
||||
_this = this,
|
||||
convert = (x, i) => {
|
||||
if (i >= stepCount) {
|
||||
_this._data = x;
|
||||
_this.loaded = true;
|
||||
_this._temporaryTileRef = null;
|
||||
return $.Promise.resolve(x);
|
||||
}
|
||||
let edge = conversionPath[i];
|
||||
try {
|
||||
// no test for y - less robust approach
|
||||
let y = edge.transform(this._temporaryTileRef, x);
|
||||
convertor.destroy(x, edge.origin.value);
|
||||
const result = $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||
return result.then(res => convert(res, i + 1));
|
||||
} catch (e) {
|
||||
_this.loaded = false;
|
||||
_this._temporaryTileRef = null;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
this.loaded = false;
|
||||
// Read target type from the conversion path: [edge.target] = Vertex, its value=type
|
||||
this._type = conversionPath[stepCount - 1].target.value;
|
||||
const promise = convert(this._data, 0);
|
||||
this._data = undefined;
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free all the data and call data destructors if defined.
|
||||
*/
|
||||
destroy() {
|
||||
$.convertor.destroy(this._data, this._type);
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely overwrite the cache data and return the old data
|
||||
* @private
|
||||
*/
|
||||
setDataAs(data, type) {
|
||||
// no check for state, users must ensure compatibility manually
|
||||
$.convertor.destroy(this._data, this._data);
|
||||
this._type = type;
|
||||
this._data = data;
|
||||
this.loaded = true;
|
||||
// TODO: if done greedily, we transform each plugin set call
|
||||
// pros: we can show midresults
|
||||
// cons: unecessary work
|
||||
// might be solved by introducing explicit tile update pipeline (already attemps)
|
||||
// --> flag that knows which update is last
|
||||
// if (this.format && !this.format.includes(type)) {
|
||||
// this.transformTo(this.format);
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class TileCache
|
||||
* @memberof OpenSeadragon
|
||||
|
@ -1885,34 +1885,27 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
*/
|
||||
_tryFindTileCacheRecord: function(tile) {
|
||||
if (!tile.cacheKey) {
|
||||
tile.cacheKey = "";
|
||||
tile.originalCacheKey = "";
|
||||
}
|
||||
|
||||
let record = this._tileCache.getCacheRecord(tile.cacheKey);
|
||||
|
||||
if (record) {
|
||||
//setup without calling tile loaded event! tile cache is ready for usage,
|
||||
tile.loading = true;
|
||||
tile.loaded = false;
|
||||
//set data as null, cache already has data, it does not overwrite
|
||||
this._setTileLoaded(tile, null, null, null, record.type,
|
||||
this.callTileLoadedWithCachedData);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tile.cacheKey !== tile.originalCacheKey) {
|
||||
//we found original data: this data will be used to re-execute the pipeline
|
||||
record = this._tileCache.getCacheRecord(tile.originalCacheKey);
|
||||
let record = this._tileCache.getCacheRecord(tile.originalCacheKey);
|
||||
if (record) {
|
||||
tile.loading = true;
|
||||
tile.loaded = false;
|
||||
//set data as null, cache already has data, it does not overwrite
|
||||
this._setTileLoaded(tile, null, null, null, record.type);
|
||||
this._setTileLoaded(tile, record.data, null, null, record.type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let record = this._tileCache.getCacheRecord(tile.cacheKey);
|
||||
if (record) {
|
||||
// setup without calling tile loaded event! tile cache is ready for usage,
|
||||
tile.loading = true;
|
||||
tile.loaded = false;
|
||||
// we could send null as data (cache not re-created), but deprecated events access the data
|
||||
this._setTileLoaded(tile, record.data, null, null, record.type,
|
||||
this.callTileLoadedWithCachedData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
@ -2103,8 +2096,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
*/
|
||||
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) {
|
||||
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
|
||||
// -> reason why it is not in the constructor
|
||||
tile.setCache(tile.cacheKey, data, dataType, false);
|
||||
// does nothing if tile.cacheKey already present
|
||||
tile.addCache(tile.cacheKey, data, dataType, false);
|
||||
|
||||
let resolver = null,
|
||||
increment = 0,
|
||||
@ -2127,11 +2120,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
const cache = tile.getCache(tile.cacheKey),
|
||||
requiredTypes = _this.viewer.drawer.getSupportedDataFormats();
|
||||
if (!cache) {
|
||||
$.console.warn("Tile %s not cached at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
||||
$.console.warn("Tile %s not cached or not loaded at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
||||
resolver(tile);
|
||||
} else if (!requiredTypes.includes(cache.type)) {
|
||||
//initiate conversion as soon as possible if incompatible with the drawer
|
||||
cache.transformTo(requiredTypes).then(_ => {
|
||||
cache.prepareForRendering(requiredTypes).then(cacheRef => {
|
||||
if (!cacheRef) {
|
||||
return cache.transformTo(requiredTypes);
|
||||
}
|
||||
return cacheRef;
|
||||
}).then(_ => {
|
||||
tile.loading = false;
|
||||
tile.loaded = true;
|
||||
resolver(tile);
|
||||
|
@ -99,11 +99,22 @@
|
||||
|
||||
// Unique type per drawer: uploads texture to unique webgl context.
|
||||
this._dataType = `${Date.now()}_TEX_2D`;
|
||||
this._supportedFormats = [];
|
||||
this._setupTextureHandlers(this._dataType);
|
||||
|
||||
this.context = this._outputContext; // API required by tests
|
||||
}
|
||||
|
||||
}
|
||||
get defaultOptions() {
|
||||
return {
|
||||
// use detached cache: our type conversion will not collide (and does not have to preserve CPU data ref)
|
||||
detachedCache: true
|
||||
};
|
||||
}
|
||||
|
||||
getSupportedDataFormats() {
|
||||
return this._supportedFormats;
|
||||
}
|
||||
|
||||
// Public API required by all Drawer implementations
|
||||
/**
|
||||
@ -315,7 +326,7 @@
|
||||
);
|
||||
return;
|
||||
}
|
||||
const textureInfo = this.getCompatibleData(tile);
|
||||
const textureInfo = this.getDataToDraw(tile, true);
|
||||
if (!textureInfo) {
|
||||
return;
|
||||
}
|
||||
@ -830,8 +841,7 @@
|
||||
// TextureInfo stored in the cache
|
||||
return {
|
||||
texture: texture,
|
||||
position: position,
|
||||
cpuData: data,
|
||||
position: position
|
||||
};
|
||||
};
|
||||
const tex2DCompatibleDestructor = textureInfo => {
|
||||
@ -839,22 +849,16 @@
|
||||
this._gl.deleteTexture(textureInfo.texture);
|
||||
}
|
||||
};
|
||||
const dataRetrieval = (tile, data) => {
|
||||
return data.cpuData;
|
||||
};
|
||||
|
||||
// Differentiate type also based on type used to upload data: we can support bidirectional conversion.
|
||||
const c2dTexType = thisType + ":context2d",
|
||||
imageTexType = thisType + ":image";
|
||||
|
||||
this.declareSupportedDataFormats(imageTexType, c2dTexType);
|
||||
this._supportedFormats.push(c2dTexType, imageTexType);
|
||||
|
||||
// We should be OK uploading any of these types. The complexity is selected to be O(3n), should be
|
||||
// more than linear pass over pixels
|
||||
$.convertor.learn("context2d", c2dTexType, tex2DCompatibleLoader, 1, 3);
|
||||
$.convertor.learn("context2d", c2dTexType, (t, d) => tex2DCompatibleLoader(t, d.canvas), 1, 3);
|
||||
$.convertor.learn("image", imageTexType, tex2DCompatibleLoader, 1, 3);
|
||||
$.convertor.learn(c2dTexType, "context2d", dataRetrieval, 1, 3);
|
||||
$.convertor.learn(imageTexType, "image", dataRetrieval, 1, 3);
|
||||
|
||||
$.convertor.learnDestroy(c2dTexType, tex2DCompatibleDestructor);
|
||||
$.convertor.learnDestroy(imageTexType, tex2DCompatibleDestructor);
|
||||
|
@ -83,14 +83,13 @@
|
||||
|
||||
if (processors.length === 0) {
|
||||
//restore the original data
|
||||
const context = await tile.getOriginalData('context2d',
|
||||
false);
|
||||
const context = await tile.getOriginalData('context2d', false);
|
||||
tile.setData(context, 'context2d');
|
||||
tile._filterIncrement = self.filterIncrement;
|
||||
return;
|
||||
}
|
||||
|
||||
const contextCopy = await tile.getOriginalData('context2d');
|
||||
const contextCopy = await tile.getOriginalData('context2d', true);
|
||||
const currentIncrement = self.filterIncrement;
|
||||
for (let i = 0; i < processors.length; i++) {
|
||||
if (self.filterIncrement !== currentIncrement) {
|
||||
|
@ -252,15 +252,15 @@
|
||||
|
||||
//load data
|
||||
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
|
||||
tile00.setCache(tile00.cacheKey, 0, T_A, false);
|
||||
tile00.addCache(tile00.cacheKey, 0, T_A, false);
|
||||
const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0);
|
||||
tile01.setCache(tile01.cacheKey, 0, T_B, false);
|
||||
tile01.addCache(tile01.cacheKey, 0, T_B, false);
|
||||
const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1);
|
||||
tile10.setCache(tile10.cacheKey, 0, T_C, false);
|
||||
tile10.addCache(tile10.cacheKey, 0, T_C, false);
|
||||
const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1);
|
||||
tile11.setCache(tile11.cacheKey, 0, T_C, false);
|
||||
tile11.addCache(tile11.cacheKey, 0, T_C, false);
|
||||
const tile12 = createFakeTile('foo.jpg', fakeTiledImage1);
|
||||
tile12.setCache(tile12.cacheKey, 0, T_A, false);
|
||||
tile12.addCache(tile12.cacheKey, 0, T_A, false);
|
||||
|
||||
const collideGetSet = async (tile, type) => {
|
||||
const value = await tile.getData(type, false);
|
||||
@ -446,15 +446,15 @@
|
||||
|
||||
//load data
|
||||
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
|
||||
tile00.setCache(tile00.cacheKey, 0, T_A, false);
|
||||
tile00.addCache(tile00.cacheKey, 0, T_A, false);
|
||||
const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0);
|
||||
tile01.setCache(tile01.cacheKey, 0, T_B, false);
|
||||
tile01.addCache(tile01.cacheKey, 0, T_B, false);
|
||||
const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1);
|
||||
tile10.setCache(tile10.cacheKey, 0, T_C, false);
|
||||
tile10.addCache(tile10.cacheKey, 0, T_C, false);
|
||||
const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1);
|
||||
tile11.setCache(tile11.cacheKey, 0, T_C, false);
|
||||
tile11.addCache(tile11.cacheKey, 0, T_C, false);
|
||||
const tile12 = createFakeTile('foo.jpg', fakeTiledImage1);
|
||||
tile12.setCache(tile12.cacheKey, 0, T_A, false);
|
||||
tile12.addCache(tile12.cacheKey, 0, T_A, false);
|
||||
|
||||
//test set/get data in async env
|
||||
(async function() {
|
||||
@ -471,7 +471,7 @@
|
||||
test.equal(theTileKey, tile00.originalCacheKey, "Original cache key preserved.");
|
||||
|
||||
//now add artifically another record
|
||||
tile00.setCache("my_custom_cache", 128, T_C);
|
||||
tile00.addCache("my_custom_cache", 128, T_C);
|
||||
test.equal(tileCache.numTilesLoaded(), 5, "We still loaded only 5 tiles.");
|
||||
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
||||
test.equal(c00.getTileCount(), 2, "The cache still has only two tiles attached.");
|
||||
@ -483,32 +483,32 @@
|
||||
test.equal(tile12.getCacheSize(), 1, "Related tile cache did not increase.");
|
||||
|
||||
//add and delete cache nothing changes
|
||||
tile00.setCache("my_custom_cache2", 128, T_C);
|
||||
tile00.unsetCache("my_custom_cache2");
|
||||
tile00.addCache("my_custom_cache2", 128, T_C);
|
||||
tile00.removeCache("my_custom_cache2");
|
||||
test.equal(tileCache.numTilesLoaded(), 5, "We still loaded only 5 tiles.");
|
||||
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
||||
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
||||
|
||||
//delete cache as a zombie
|
||||
tile00.setCache("my_custom_cache2", 17, T_C);
|
||||
tile00.addCache("my_custom_cache2", 17, T_C);
|
||||
//direct access shoes correct value although we set key!
|
||||
const myCustomCache2Data = tile00.getCache("my_custom_cache2").data;
|
||||
test.equal(myCustomCache2Data, 17, "Previously defined cache does not intervene.");
|
||||
test.equal(tileCache.numCachesLoaded(), 6, "The cache size is 6.");
|
||||
//keep zombie
|
||||
tile00.unsetCache("my_custom_cache2", false);
|
||||
tile00.removeCache("my_custom_cache2", false);
|
||||
test.equal(tileCache.numCachesLoaded(), 6, "The cache is 5 + 1 zombie, no change.");
|
||||
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
||||
|
||||
//revive zombie
|
||||
tile01.setCache("my_custom_cache2", 18, T_C);
|
||||
tile01.addCache("my_custom_cache2", 18, T_C);
|
||||
const myCustomCache2OtherData = tile01.getCache("my_custom_cache2").data;
|
||||
test.equal(myCustomCache2OtherData, myCustomCache2Data, "Caches are equal because revived.");
|
||||
//again, keep zombie
|
||||
tile01.unsetCache("my_custom_cache2", false);
|
||||
tile01.removeCache("my_custom_cache2", false);
|
||||
|
||||
//first create additional cache so zombie is not the youngest
|
||||
tile01.setCache("some weird cache", 11, T_A);
|
||||
tile01.addCache("some weird cache", 11, T_A);
|
||||
test.ok(tile01.cacheKey === tile01.originalCacheKey, "Custom cache does not touch tile cache keys.");
|
||||
|
||||
//insertion aadditional cache clears the zombie first although it is not the youngest one
|
||||
@ -528,12 +528,12 @@
|
||||
test.equal(tile12.getCache().data, 42, "The value is not 43 as setData triggers cache share!");
|
||||
|
||||
//triggers insertion - deletion of zombie cache 'my_custom_cache2'
|
||||
tile00.setCache("trigger-max-cache-handler", 5, T_C);
|
||||
tile00.addCache("trigger-max-cache-handler", 5, T_C);
|
||||
//reset CAP
|
||||
tileCache._maxCacheItemCount = OpenSeadragon.DEFAULT_SETTINGS.maxImageCacheCount;
|
||||
|
||||
//try to revive zombie will fail: the zombie was deleted, we will find 18
|
||||
tile01.setCache("my_custom_cache2", 18, T_C);
|
||||
tile01.addCache("my_custom_cache2", 18, T_C);
|
||||
const myCustomCache2RecreatedData = tile01.getCache("my_custom_cache2").data;
|
||||
test.notEqual(myCustomCache2RecreatedData, myCustomCache2Data, "Caches are not equal because created.");
|
||||
test.equal(myCustomCache2RecreatedData, 18, "Cache data is actually as set to 18.");
|
||||
|
Loading…
Reference in New Issue
Block a user