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
@ -50,8 +50,6 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.declareSupportedDataFormats("context2d");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML element (canvas) that this drawer uses for drawing
|
* The HTML element (canvas) that this drawer uses for drawing
|
||||||
* @member {Element} canvas
|
* @member {Element} canvas
|
||||||
@ -100,6 +98,10 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
return 'canvas';
|
return 'canvas';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSupportedDataFormats() {
|
||||||
|
return ["context2d"];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
||||||
* @returns {Element} the canvas to draw 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
|
// Note: Disabled on iOS devices per default as it causes a native crash
|
||||||
useSketch = true;
|
useSketch = true;
|
||||||
|
|
||||||
const context = tile.length && this.getCompatibleData(tile);
|
const context = tile.length && this.getDataToDraw(tile);
|
||||||
if (context) {
|
if (context) {
|
||||||
sketchScale = context.canvas.width / (tile.size.x * $.pixelDensityRatio);
|
sketchScale = context.canvas.width / (tile.size.x * $.pixelDensityRatio);
|
||||||
} else {
|
} else {
|
||||||
@ -572,7 +574,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rendered = this.getCompatibleData(tile);
|
const rendered = this.getDataToDraw(tile);
|
||||||
if (!rendered) {
|
if (!rendered) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -354,8 +354,8 @@ $.DataTypeConvertor = class {
|
|||||||
}
|
}
|
||||||
let edge = conversionPath[i];
|
let edge = conversionPath[i];
|
||||||
let y = edge.transform(tile, x);
|
let y = edge.transform(tile, x);
|
||||||
if (!y) {
|
if (y === undefined) {
|
||||||
$.console.warn(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting to %s)`, edge.target);
|
$.console.error(`[OpenSeadragon.convertor.convert] data mid result undefined value (while converting using %s)`, edge);
|
||||||
return $.Promise.resolve();
|
return $.Promise.resolve();
|
||||||
}
|
}
|
||||||
//node.value holds the type string
|
//node.value holds the type string
|
||||||
@ -389,8 +389,8 @@ $.DataTypeConvertor = class {
|
|||||||
/**
|
/**
|
||||||
* Destroy the data item given.
|
* Destroy the data item given.
|
||||||
* @param {string} type data type
|
* @param {string} type data type
|
||||||
* @param {?} data
|
* @param {any} data
|
||||||
* @return {OpenSeadragon.Promise<?>|undefined} promise resolution with data passed from constructor, or undefined
|
* @return {OpenSeadragon.Promise<any>|undefined} promise resolution with data passed from constructor, or undefined
|
||||||
* if not such conversion exists
|
* if not such conversion exists
|
||||||
*/
|
*/
|
||||||
destroy(data, type) {
|
destroy(data, type) {
|
||||||
|
@ -34,6 +34,13 @@
|
|||||||
|
|
||||||
(function( $ ){
|
(function( $ ){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
const OpenSeadragon = $; // (re)alias back to OpenSeadragon for JSDoc
|
||||||
/**
|
/**
|
||||||
* @class OpenSeadragon.DrawerBase
|
* @class OpenSeadragon.DrawerBase
|
||||||
@ -54,7 +61,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
this.viewer = options.viewer;
|
this.viewer = options.viewer;
|
||||||
this.viewport = options.viewport;
|
this.viewport = options.viewport;
|
||||||
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
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 );
|
this.container = $.getElement( options.element );
|
||||||
|
|
||||||
@ -80,10 +87,24 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
this._checkInterfaceImplementation();
|
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
|
// protect the canvas member with a getter
|
||||||
get canvas(){
|
get canvas(){
|
||||||
return this._renderingTarget;
|
return this._renderingTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
get element(){
|
get element(){
|
||||||
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
|
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
|
||||||
return this.container;
|
return this.container;
|
||||||
@ -98,24 +119,13 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
return undefined;
|
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
|
* Retrieve data types
|
||||||
|
* @abstract
|
||||||
* @return {[string]}
|
* @return {[string]}
|
||||||
*/
|
*/
|
||||||
getSupportedDataFormats() {
|
getSupportedDataFormats() {
|
||||||
if (!this._formats || this._formats.length < 1) {
|
throw "Drawer.getSupportedDataFormats must define its supported rendering data types!";
|
||||||
$.console.error("A drawer must define its supported rendering data types using declareSupportedDataFormats!");
|
|
||||||
}
|
|
||||||
return this._formats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,26 +134,15 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
* value, the rendering _MUST NOT_ proceed. It should
|
* value, the rendering _MUST NOT_ proceed. It should
|
||||||
* await next animation frames and check again for availability.
|
* await next animation frames and check again for availability.
|
||||||
* @param {OpenSeadragon.Tile} tile
|
* @param {OpenSeadragon.Tile} tile
|
||||||
|
* @return {any|null|false} null if cache not available
|
||||||
*/
|
*/
|
||||||
getCompatibleData(tile) {
|
getDataToDraw(tile) {
|
||||||
const cache = tile.getCache(tile.cacheKey);
|
const cache = tile.getCache(tile.cacheKey);
|
||||||
if (!cache) {
|
if (!cache) {
|
||||||
|
$.console.warn("Attempt to draw tile %s when not cached!", tile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return cache.getDataForRendering(this.getSupportedDataFormats(), this.options.detachedCache);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,6 +229,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
_checkInterfaceImplementation(){
|
_checkInterfaceImplementation(){
|
||||||
|
// TODO: is this necessary? why not throw just in the method itself?
|
||||||
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
||||||
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,6 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
constructor(options){
|
constructor(options){
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.declareSupportedDataFormats("image");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML element (div) that this drawer uses for drawing
|
* The HTML element (div) that this drawer uses for drawing
|
||||||
* @member {Element} canvas
|
* @member {Element} canvas
|
||||||
@ -87,6 +85,10 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
return 'html';
|
return 'html';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSupportedDataFormats() {
|
||||||
|
return ["image"];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
* @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.
|
// content during animation of the container size.
|
||||||
|
|
||||||
if ( !tile.element ) {
|
if ( !tile.element ) {
|
||||||
const image = this.getCompatibleData(tile);
|
const image = this.getDataToDraw(tile);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return;
|
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
|
* @memberof OpenSeadragon
|
||||||
* @property {Object} webgl - options if the WebGLDrawer is used. No options are currently supported.
|
* @property {BaseDrawerOptions} [webgl] - options if the WebGLDrawer is used.
|
||||||
* @property {Object} canvas - options if the CanvasDrawer is used. No options are currently supported.
|
* @property {BaseDrawerOptions} [canvas] - options if the CanvasDrawer is used.
|
||||||
* @property {Object} html - options if the HTMLDrawer is used. No options are currently supported.
|
* @property {BaseDrawerOptions} [html] - options if the HTMLDrawer is used.
|
||||||
* @property {Object} custom - options if a custom drawer is used. No options are currently supported.
|
* @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.
|
* keys and booleans as values.
|
||||||
*/
|
*/
|
||||||
setImageFormatsSupported: function(formats) {
|
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
|
// eslint-disable-next-line no-use-before-define
|
||||||
$.extend(FILEFORMATS, formats);
|
$.extend(FILEFORMATS, formats);
|
||||||
},
|
},
|
||||||
|
105
src/tile.js
105
src/tile.js
@ -46,7 +46,7 @@
|
|||||||
* this tile failed to load? )
|
* this tile failed to load? )
|
||||||
* @param {String|Function} url The URL of this tile's image or a function that returns a url.
|
* @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
|
* @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 {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 {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
|
* @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.");
|
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
|
||||||
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* The unique main cache key for this tile. Created automatically
|
this._cKey = cacheKey || "";
|
||||||
* from the given tiledImage.source.getTileHashKey(...) implementation.
|
this._ocKey = cacheKey || "";
|
||||||
* @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;
|
|
||||||
/**
|
/**
|
||||||
* Is this tile loaded?
|
* Is this tile loaded?
|
||||||
* @member {Boolean} loaded
|
* @member {Boolean} loaded
|
||||||
@ -304,6 +290,43 @@ $.Tile.prototype = {
|
|||||||
return this.level + "/" + this.x + "_" + this.y;
|
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.
|
* The Image object for this tile.
|
||||||
* @member {Object} image
|
* @member {Object} image
|
||||||
@ -405,21 +428,21 @@ $.Tile.prototype = {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
set cacheImageRecord(value) {
|
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];
|
const cache = this._caches[this.cacheKey];
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
this.unsetCache(this.cacheKey);
|
this.removeCache(this.cacheKey);
|
||||||
} else {
|
} else {
|
||||||
const _this = this;
|
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
|
* Get the data to render for this tile
|
||||||
* @param {string} type data type to require
|
* @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
|
* @return {*|undefined} data in the desired type, or undefined if a conversion is ongoing
|
||||||
*/
|
*/
|
||||||
getData: function(type, copy = true) {
|
getData: function(type, copy = true) {
|
||||||
@ -439,10 +462,10 @@ $.Tile.prototype = {
|
|||||||
/**
|
/**
|
||||||
* Get the original data data for this tile
|
* Get the original data data for this tile
|
||||||
* @param {string} type data type to require
|
* @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
|
* @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) {
|
if (!this.tiledImage) {
|
||||||
return null; //async can access outside its lifetime
|
return null; //async can access outside its lifetime
|
||||||
}
|
}
|
||||||
@ -457,12 +480,10 @@ $.Tile.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set cache data
|
* Set main cache data
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {?string} type data type to require
|
* @param {?string} type data type to require
|
||||||
* @param {boolean} [preserveOriginalData=true] if true and cacheKey === originalCacheKey,
|
* @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) {
|
setData: function(value, type, preserveOriginalData = true) {
|
||||||
if (!this.tiledImage) {
|
if (!this.tiledImage) {
|
||||||
@ -473,8 +494,9 @@ $.Tile.prototype = {
|
|||||||
//caches equality means we have only one cache:
|
//caches equality means we have only one cache:
|
||||||
// change current pointer to a new cache and create it: new tiles will
|
// change current pointer to a new cache and create it: new tiles will
|
||||||
// not arrive at this data, but at originalCacheKey state
|
// 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;
|
this.cacheKey = "mod://" + this.originalCacheKey;
|
||||||
return this.setCache(this.cacheKey, value, type)._promise;
|
return this.addCache(this.cacheKey, value, type)._promise;
|
||||||
}
|
}
|
||||||
//else overwrite cache
|
//else overwrite cache
|
||||||
const cache = this.getCache(this.cacheKey);
|
const cache = this.getCache(this.cacheKey);
|
||||||
@ -487,7 +509,7 @@ $.Tile.prototype = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read tile cache data object (CacheRecord)
|
* 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}
|
* @return {OpenSeadragon.CacheRecord}
|
||||||
*/
|
*/
|
||||||
getCache: function(key = this.cacheKey) {
|
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
|
* Set tile cache, possibly multiple with custom key
|
||||||
* @param {string} key cache key, must be unique (we recommend re-using this.cacheTile
|
* @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
|
* value and extend it with some another unique content, by default overrides the existing
|
||||||
* main cache used for drawing, if not 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 {?string} type data type, will be guessed if not provided
|
||||||
* @param [_safely=true] private
|
* @param [_safely=true] private
|
||||||
* @returns {OpenSeadragon.CacheRecord|null} - The cache record the tile was attached to.
|
* @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) {
|
if (!this.tiledImage) {
|
||||||
return null; //async can access outside its lifetime
|
return null; //async can access outside its lifetime
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
if (!this.tiledImage.__typeWarningReported) {
|
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.");
|
"Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
|
||||||
this.tiledImage.__typeWarningReported = true;
|
this.tiledImage.__typeWarningReported = true;
|
||||||
}
|
}
|
||||||
@ -523,20 +547,17 @@ $.Tile.prototype = {
|
|||||||
// Need to get the supported type for rendering out of the active drawer.
|
// Need to get the supported type for rendering out of the active drawer.
|
||||||
const supportedTypes = this.tiledImage.viewer.drawer.getSupportedDataFormats();
|
const supportedTypes = this.tiledImage.viewer.drawer.getSupportedDataFormats();
|
||||||
const conversion = $.convertor.getConversionPath(type, supportedTypes);
|
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);
|
"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({
|
const cachedItem = this.tiledImage._tileCache.cacheTile({
|
||||||
data: data,
|
data: data,
|
||||||
dataType: type,
|
dataType: type,
|
||||||
tile: this,
|
tile: this,
|
||||||
cacheKey: key,
|
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];
|
const havingRecord = this._caches[key];
|
||||||
if (havingRecord !== cachedItem) {
|
if (havingRecord !== cachedItem) {
|
||||||
@ -561,12 +582,12 @@ $.Tile.prototype = {
|
|||||||
* @param {string} key cache key, required
|
* @param {string} key cache key, required
|
||||||
* @param {boolean} [freeIfUnused=true] set to false if zombie should be created
|
* @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 === key) {
|
||||||
if (this.cacheKey !== this.originalCacheKey) {
|
if (this.cacheKey !== this.originalCacheKey) {
|
||||||
this.cacheKey = this.originalCacheKey;
|
this.cacheKey = this.originalCacheKey;
|
||||||
} else {
|
} 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)) {
|
if (this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused)) {
|
||||||
@ -637,7 +658,7 @@ $.Tile.prototype = {
|
|||||||
this.imgElement = null;
|
this.imgElement = null;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.cacheKey = this.originalCacheKey;
|
this._cKey = this._ocKey;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
295
src/tilecache.js
295
src/tilecache.js
@ -34,9 +34,12 @@
|
|||||||
|
|
||||||
(function( $ ){
|
(function( $ ){
|
||||||
|
|
||||||
|
const DRAWER_INTERNAL_CACHE = Symbol("DRAWER_INTERNAL_CACHE");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached Data Record, the cache object.
|
* @class CacheRecord
|
||||||
* Keeps only latest object type required.
|
* @memberof OpenSeadragon
|
||||||
|
* @classdesc Cached Data Record, the cache object. Keeps only latest object type required.
|
||||||
*
|
*
|
||||||
* This class acts like the Maybe type:
|
* This class acts like the Maybe type:
|
||||||
* - it has 'loaded' flag indicating whether the tile data is ready
|
* - 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
|
* Furthermore, it has a 'getData' function that returns a promise resolving
|
||||||
* with the value on the desired type passed to the function.
|
* 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 {
|
$.CacheRecord = class {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -82,7 +75,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Await ongoing process so that we get cache ready on callback.
|
* Await ongoing process so that we get cache ready on callback.
|
||||||
* @returns {null|*}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
await() {
|
await() {
|
||||||
if (!this._promise) { //if not cache loaded, do not fail
|
if (!this._promise) { //if not cache loaded, do not fail
|
||||||
@ -128,20 +121,24 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Access the cache record data indirectly. Preferred way of data access. Asynchronous.
|
* Access the cache record data indirectly. Preferred way of data access. Asynchronous.
|
||||||
* @param {string?} [type=this.type]
|
* @param {string} [type=this.type]
|
||||||
* @param {boolean?} [copy=true] if false and same type is retrieved as the cache 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
|
* 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,
|
* 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).
|
|
||||||
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
|
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
|
||||||
*/
|
*/
|
||||||
getDataAs(type = this._type, copy = true) {
|
getDataAs(type = this._type, copy = true) {
|
||||||
const referenceTile = this._tiles[0];
|
const referenceTile = this._tiles[0];
|
||||||
if (this.loaded && type === this._type) {
|
if (this.loaded) {
|
||||||
|
if (type === this._type) {
|
||||||
return copy ? $.convertor.copy(referenceTile, this._data, type) : this._promise;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
return this._promise.then(data => {
|
_getDataAsUnsafe(referenceTile, data, type, copy) {
|
||||||
//might get destroyed in meanwhile
|
//might get destroyed in meanwhile
|
||||||
if (this._destroyed) {
|
if (this._destroyed) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -153,6 +150,75 @@
|
|||||||
return $.convertor.copy(referenceTile, data, type);
|
return $.convertor.copy(referenceTile, data, type);
|
||||||
}
|
}
|
||||||
return data;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
* Does nothing if the type equals to the current type. Asynchronous.
|
||||||
* @param {string|[string]} type if array provided, the system will
|
* @param {string|[string]} type if array provided, the system will
|
||||||
* try to optimize for the best type to convert to.
|
* try to optimize for the best type to convert to.
|
||||||
* @return {OpenSeadragon.Promise<?>|*}
|
* @return {OpenSeadragon.Promise<?>}
|
||||||
*/
|
*/
|
||||||
transformTo(type = this._type) {
|
transformTo(type = this._type) {
|
||||||
if (!this.loaded ||
|
if (!this.loaded ||
|
||||||
@ -198,6 +264,18 @@
|
|||||||
return this._promise;
|
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.
|
* Set initial state, prepare for usage.
|
||||||
* Must not be called on active cache, e.g. first call destroy().
|
* Must not be called on active cache, e.g. first call destroy().
|
||||||
@ -221,17 +299,19 @@
|
|||||||
|
|
||||||
// make sure this gets destroyed even if loaded=false
|
// make sure this gets destroyed even if loaded=false
|
||||||
if (this.loaded) {
|
if (this.loaded) {
|
||||||
$.convertor.destroy(this._data, this._type);
|
this._destroySelfUnsafe(this._data, this._type);
|
||||||
this._tiles = null;
|
|
||||||
this._data = null;
|
|
||||||
this._type = null;
|
|
||||||
this._promise = null;
|
|
||||||
} else {
|
} else {
|
||||||
const oldType = this._type;
|
const oldType = this._type;
|
||||||
this._promise.then(x => {
|
this._promise.then(x => this._destroySelfUnsafe(x, oldType));
|
||||||
|
}
|
||||||
|
this.loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroySelfUnsafe(data, type) {
|
||||||
// ensure old data destroyed
|
// ensure old data destroyed
|
||||||
$.convertor.destroy(x, oldType);
|
$.convertor.destroy(data, type);
|
||||||
//might get revived...
|
this.destroyInternalCache();
|
||||||
|
// might've got revived in meanwhile if async ...
|
||||||
if (!this._destroyed) {
|
if (!this._destroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -239,9 +319,6 @@
|
|||||||
this._data = null;
|
this._data = null;
|
||||||
this._type = null;
|
this._type = null;
|
||||||
this._promise = null;
|
this._promise = null;
|
||||||
});
|
|
||||||
}
|
|
||||||
this.loaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -342,6 +419,12 @@
|
|||||||
this._type = type;
|
this._type = type;
|
||||||
this._data = data;
|
this._data = data;
|
||||||
this._promise = $.Promise.resolve(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();
|
this._triggerNeedsDraw();
|
||||||
return this._promise;
|
return this._promise;
|
||||||
}
|
}
|
||||||
@ -350,6 +433,12 @@
|
|||||||
this._type = type;
|
this._type = type;
|
||||||
this._data = data;
|
this._data = data;
|
||||||
this._promise = $.Promise.resolve(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();
|
this._triggerNeedsDraw();
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
@ -366,7 +455,7 @@
|
|||||||
referenceTile = this._tiles[0],
|
referenceTile = this._tiles[0],
|
||||||
conversionPath = convertor.getConversionPath(from, to);
|
conversionPath = convertor.getConversionPath(from, to);
|
||||||
if (!conversionPath) {
|
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
|
return; //no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,21 +470,14 @@
|
|||||||
return $.Promise.resolve(x);
|
return $.Promise.resolve(x);
|
||||||
}
|
}
|
||||||
let edge = conversionPath[i];
|
let edge = conversionPath[i];
|
||||||
return $.Promise.resolve(edge.transform(referenceTile, x)).then(
|
let y = edge.transform(referenceTile, x);
|
||||||
y => {
|
if (y === undefined) {
|
||||||
if (!y) {
|
_this.loaded = false;
|
||||||
$.console.error(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting using %s)`, edge);
|
throw `[CacheRecord._convert] data mid result undefined value (while converting using ${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);
|
convertor.destroy(x, edge.origin.value);
|
||||||
return convert(y, i + 1);
|
const result = $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||||
}
|
return result.then(res => convert(res, i + 1));
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.loaded = false;
|
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
|
* @class TileCache
|
||||||
* @memberof OpenSeadragon
|
* @memberof OpenSeadragon
|
||||||
|
@ -1885,34 +1885,27 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @param {OpenSeadragon.Tile} tile
|
* @param {OpenSeadragon.Tile} tile
|
||||||
*/
|
*/
|
||||||
_tryFindTileCacheRecord: function(tile) {
|
_tryFindTileCacheRecord: function(tile) {
|
||||||
if (!tile.cacheKey) {
|
if (tile.cacheKey !== tile.originalCacheKey) {
|
||||||
tile.cacheKey = "";
|
//we found original data: this data will be used to re-execute the pipeline
|
||||||
tile.originalCacheKey = "";
|
let record = this._tileCache.getCacheRecord(tile.originalCacheKey);
|
||||||
|
if (record) {
|
||||||
|
tile.loading = true;
|
||||||
|
tile.loaded = false;
|
||||||
|
this._setTileLoaded(tile, record.data, null, null, record.type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let record = this._tileCache.getCacheRecord(tile.cacheKey);
|
let record = this._tileCache.getCacheRecord(tile.cacheKey);
|
||||||
|
|
||||||
if (record) {
|
if (record) {
|
||||||
// setup without calling tile loaded event! tile cache is ready for usage,
|
// setup without calling tile loaded event! tile cache is ready for usage,
|
||||||
tile.loading = true;
|
tile.loading = true;
|
||||||
tile.loaded = false;
|
tile.loaded = false;
|
||||||
//set data as null, cache already has data, it does not overwrite
|
// we could send null as data (cache not re-created), but deprecated events access the data
|
||||||
this._setTileLoaded(tile, null, null, null, record.type,
|
this._setTileLoaded(tile, record.data, null, null, record.type,
|
||||||
this.callTileLoadedWithCachedData);
|
this.callTileLoadedWithCachedData);
|
||||||
return true;
|
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);
|
|
||||||
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);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2103,8 +2096,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
*/
|
*/
|
||||||
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) {
|
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) {
|
||||||
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
|
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
|
||||||
// -> reason why it is not in the constructor
|
// does nothing if tile.cacheKey already present
|
||||||
tile.setCache(tile.cacheKey, data, dataType, false);
|
tile.addCache(tile.cacheKey, data, dataType, false);
|
||||||
|
|
||||||
let resolver = null,
|
let resolver = null,
|
||||||
increment = 0,
|
increment = 0,
|
||||||
@ -2127,11 +2120,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
const cache = tile.getCache(tile.cacheKey),
|
const cache = tile.getCache(tile.cacheKey),
|
||||||
requiredTypes = _this.viewer.drawer.getSupportedDataFormats();
|
requiredTypes = _this.viewer.drawer.getSupportedDataFormats();
|
||||||
if (!cache) {
|
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);
|
resolver(tile);
|
||||||
} else if (!requiredTypes.includes(cache.type)) {
|
} else if (!requiredTypes.includes(cache.type)) {
|
||||||
//initiate conversion as soon as possible if incompatible with the drawer
|
//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.loading = false;
|
||||||
tile.loaded = true;
|
tile.loaded = true;
|
||||||
resolver(tile);
|
resolver(tile);
|
||||||
|
@ -99,10 +99,21 @@
|
|||||||
|
|
||||||
// Unique type per drawer: uploads texture to unique webgl context.
|
// Unique type per drawer: uploads texture to unique webgl context.
|
||||||
this._dataType = `${Date.now()}_TEX_2D`;
|
this._dataType = `${Date.now()}_TEX_2D`;
|
||||||
|
this._supportedFormats = [];
|
||||||
this._setupTextureHandlers(this._dataType);
|
this._setupTextureHandlers(this._dataType);
|
||||||
|
|
||||||
this.context = this._outputContext; // API required by tests
|
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
|
// Public API required by all Drawer implementations
|
||||||
@ -315,7 +326,7 @@
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const textureInfo = this.getCompatibleData(tile);
|
const textureInfo = this.getDataToDraw(tile, true);
|
||||||
if (!textureInfo) {
|
if (!textureInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -830,8 +841,7 @@
|
|||||||
// TextureInfo stored in the cache
|
// TextureInfo stored in the cache
|
||||||
return {
|
return {
|
||||||
texture: texture,
|
texture: texture,
|
||||||
position: position,
|
position: position
|
||||||
cpuData: data,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const tex2DCompatibleDestructor = textureInfo => {
|
const tex2DCompatibleDestructor = textureInfo => {
|
||||||
@ -839,22 +849,16 @@
|
|||||||
this._gl.deleteTexture(textureInfo.texture);
|
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.
|
// Differentiate type also based on type used to upload data: we can support bidirectional conversion.
|
||||||
const c2dTexType = thisType + ":context2d",
|
const c2dTexType = thisType + ":context2d",
|
||||||
imageTexType = thisType + ":image";
|
imageTexType = thisType + ":image";
|
||||||
|
this._supportedFormats.push(c2dTexType, imageTexType);
|
||||||
this.declareSupportedDataFormats(imageTexType, c2dTexType);
|
|
||||||
|
|
||||||
// We should be OK uploading any of these types. The complexity is selected to be O(3n), should be
|
// 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
|
// 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("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(c2dTexType, tex2DCompatibleDestructor);
|
||||||
$.convertor.learnDestroy(imageTexType, tex2DCompatibleDestructor);
|
$.convertor.learnDestroy(imageTexType, tex2DCompatibleDestructor);
|
||||||
|
@ -83,14 +83,13 @@
|
|||||||
|
|
||||||
if (processors.length === 0) {
|
if (processors.length === 0) {
|
||||||
//restore the original data
|
//restore the original data
|
||||||
const context = await tile.getOriginalData('context2d',
|
const context = await tile.getOriginalData('context2d', false);
|
||||||
false);
|
|
||||||
tile.setData(context, 'context2d');
|
tile.setData(context, 'context2d');
|
||||||
tile._filterIncrement = self.filterIncrement;
|
tile._filterIncrement = self.filterIncrement;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextCopy = await tile.getOriginalData('context2d');
|
const contextCopy = await tile.getOriginalData('context2d', true);
|
||||||
const currentIncrement = self.filterIncrement;
|
const currentIncrement = self.filterIncrement;
|
||||||
for (let i = 0; i < processors.length; i++) {
|
for (let i = 0; i < processors.length; i++) {
|
||||||
if (self.filterIncrement !== currentIncrement) {
|
if (self.filterIncrement !== currentIncrement) {
|
||||||
|
@ -252,15 +252,15 @@
|
|||||||
|
|
||||||
//load data
|
//load data
|
||||||
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
|
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);
|
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);
|
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);
|
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);
|
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 collideGetSet = async (tile, type) => {
|
||||||
const value = await tile.getData(type, false);
|
const value = await tile.getData(type, false);
|
||||||
@ -446,15 +446,15 @@
|
|||||||
|
|
||||||
//load data
|
//load data
|
||||||
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
|
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);
|
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);
|
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);
|
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);
|
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
|
//test set/get data in async env
|
||||||
(async function() {
|
(async function() {
|
||||||
@ -471,7 +471,7 @@
|
|||||||
test.equal(theTileKey, tile00.originalCacheKey, "Original cache key preserved.");
|
test.equal(theTileKey, tile00.originalCacheKey, "Original cache key preserved.");
|
||||||
|
|
||||||
//now add artifically another record
|
//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.numTilesLoaded(), 5, "We still loaded only 5 tiles.");
|
||||||
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
||||||
test.equal(c00.getTileCount(), 2, "The cache still has only two tiles attached.");
|
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.");
|
test.equal(tile12.getCacheSize(), 1, "Related tile cache did not increase.");
|
||||||
|
|
||||||
//add and delete cache nothing changes
|
//add and delete cache nothing changes
|
||||||
tile00.setCache("my_custom_cache2", 128, T_C);
|
tile00.addCache("my_custom_cache2", 128, T_C);
|
||||||
tile00.unsetCache("my_custom_cache2");
|
tile00.removeCache("my_custom_cache2");
|
||||||
test.equal(tileCache.numTilesLoaded(), 5, "We still loaded only 5 tiles.");
|
test.equal(tileCache.numTilesLoaded(), 5, "We still loaded only 5 tiles.");
|
||||||
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
test.equal(tileCache.numCachesLoaded(), 5, "The cache has now 5 items.");
|
||||||
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
||||||
|
|
||||||
//delete cache as a zombie
|
//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!
|
//direct access shoes correct value although we set key!
|
||||||
const myCustomCache2Data = tile00.getCache("my_custom_cache2").data;
|
const myCustomCache2Data = tile00.getCache("my_custom_cache2").data;
|
||||||
test.equal(myCustomCache2Data, 17, "Previously defined cache does not intervene.");
|
test.equal(myCustomCache2Data, 17, "Previously defined cache does not intervene.");
|
||||||
test.equal(tileCache.numCachesLoaded(), 6, "The cache size is 6.");
|
test.equal(tileCache.numCachesLoaded(), 6, "The cache size is 6.");
|
||||||
//keep zombie
|
//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(tileCache.numCachesLoaded(), 6, "The cache is 5 + 1 zombie, no change.");
|
||||||
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
test.equal(tile00.getCacheSize(), 3, "The tile has three cache objects.");
|
||||||
|
|
||||||
//revive zombie
|
//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;
|
const myCustomCache2OtherData = tile01.getCache("my_custom_cache2").data;
|
||||||
test.equal(myCustomCache2OtherData, myCustomCache2Data, "Caches are equal because revived.");
|
test.equal(myCustomCache2OtherData, myCustomCache2Data, "Caches are equal because revived.");
|
||||||
//again, keep zombie
|
//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
|
//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.");
|
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
|
//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!");
|
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'
|
//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
|
//reset CAP
|
||||||
tileCache._maxCacheItemCount = OpenSeadragon.DEFAULT_SETTINGS.maxImageCacheCount;
|
tileCache._maxCacheItemCount = OpenSeadragon.DEFAULT_SETTINGS.maxImageCacheCount;
|
||||||
|
|
||||||
//try to revive zombie will fail: the zombie was deleted, we will find 18
|
//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;
|
const myCustomCache2RecreatedData = tile01.getCache("my_custom_cache2").data;
|
||||||
test.notEqual(myCustomCache2RecreatedData, myCustomCache2Data, "Caches are not equal because created.");
|
test.notEqual(myCustomCache2RecreatedData, myCustomCache2Data, "Caches are not equal because created.");
|
||||||
test.equal(myCustomCache2RecreatedData, 18, "Cache data is actually as set to 18.");
|
test.equal(myCustomCache2RecreatedData, 18, "Cache data is actually as set to 18.");
|
||||||
|
Loading…
Reference in New Issue
Block a user