diff --git a/changelog.txt b/changelog.txt index f8d2cfec..22739c33 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,30 @@ OPENSEADRAGON CHANGELOG ======================= +6.0.0: (draft) +* NEW BEHAVIOR: OpenSeadragon Data Pipeline Overhaul + * DEPRECATION: Properties on tile that manage drawer data, or store data to draw: Tile.[element|imgElement|style|context2D|getImage|getCanvasContext] and transitively Tile.getScaleForEdgeSmoothing + * DEPRECATION: TileSource data lifecycle handlers: system manages these automatically: TileSource.[createTileCache|destroyTileCache|getTileCacheData|getTileCacheDataAsImage|getTileCacheDataAsContext2D] + * Tiles data is driven by caches: tiles can have multiple caches and cache can reference multiple tiles. + * Data types & conversion pipeline: caches support automated conversion between types, and call optionally destructors. These are asynchronous. + * Data conversion reasoning: the system keeps costs of convertors and seeks the cheapest conversion to a target format (using Dijkstra). + * Async support: events can now await handlers. Added OpenSeadragon.Promise proxy object. This object supports also synchronous mode. + * Drawers define what data they are able to work with, and receive automatically data from one of the declared types. + * Drawers now store data only inside cache, and provide optional type convertors to move data into a format they can work with. + * TileSource equality operator. TileSource destructor support. TileSources now must output type of the data they download [context.finish]. + * Zombies: data can outlive tiles, and be kept in the system to wait if they are not suddenly needed. Turned on automatically with TiledImage addition with `replace: true` and equality test success. + * ImagesLoadedPerFrame is boosted 10 times when system is fresh (reset / open) and then declines back to original value. + * CacheRecord supports 'internal cache' of 'SimpleCache' type. This cache can be used by drawers to hide complex types used for rendering. Such caches are stored internally on CacheRecord objects. + * CacheRecord drives asynchronous data management and ensures correct behavior through awaiting Promises. + * TileCache adds new methods for cache modification: renameCache, cloneCache, injectCache, replaceCache, restoreTilesThatShareOriginalCache, safeUnloadCache, unloadCacheForTile and more. Used internally within invalidation events + * Tiles have up to two 'originalCacheKey' and 'cacheKey' caches, which keep original data and target drawn data (if modified). + * Invalidation Pipeline: New event 'tile-invalidated' and requestInvalidate methods on World and TiledImage. Tiles get methods to modify data to draw, system prepares data for drawing and swaps them with the current main tile cache. + * New test suites for the new cache system, conversion pipeline and invalidation events. + * New testing/demo utilities (MockSeadragon, DrawerSwitcher for switching drawers in demos, getBuiltInDrawersForTest for testing all drawers), serialization guard in tests to remove circular references. + * New demos, demonstrating the new pipeline. New demos for older plugins to show how compatible new version is. + * Misc: updated CSS for dev server, new dev & test commands. + + 5.0.1: (in progress...) * Improved overlay handling so it plays better with other libraries (#2582 @BeebBenjamin) diff --git a/src/tile.js b/src/tile.js index 936fceb0..b615dce3 100644 --- a/src/tile.js +++ b/src/tile.js @@ -516,8 +516,16 @@ $.Tile.prototype = { }, /** - * Create tile cache for given data object. NOTE: if the existing cache already exists, + * Create tile cache for given data object. + * + * Using `setAsMain` updates also main tile cache key - the main cache key used to draw this tile. + * In that case, the cache should be ready to be rendered immediatelly (converted to one of the supported formats + * of the currently employed drawer). + * + * NOTE: if the existing cache already exists, * data parameter is ignored and inherited from the existing cache object. + * WARNING: if you override main tile cache key to point to a different cache, the invalidation routine + * will no longer work. If you need to modify tile main data, prefer to use invalidation routine instead. * * @param {string} key cache key, if unique, new cache object is created, else existing cache attached * @param {*} data this data will be IGNORED if cache already exists; therefore if @@ -758,6 +766,14 @@ $.Tile.prototype = { delete this._caches[oldKey]; }, + /** + * Check if two tiles are data-equal + * @param {OpenSeadragon.Tile} tile + */ + equals(tile) { + return this._ocKey === this._ocKey; + }, + /** * Removes tile from the system: it will still be present in the * OSD memory, but marked as loaded=false, and its data will be erased. diff --git a/src/tilecache.js b/src/tilecache.js index 2b7575a5..099e77c8 100644 --- a/src/tilecache.js +++ b/src/tilecache.js @@ -176,7 +176,7 @@ * Returns undefined if the data is not ready for rendering. * @private */ - getDataForRendering(drawer, tileToDraw ) { + getDataForRendering(drawer, tileToDraw) { const supportedTypes = drawer.getSupportedDataFormats(), keepInternalCopy = drawer.options.usePrivateCache; if (this.loaded && supportedTypes.includes(this.type)) { @@ -823,9 +823,14 @@ if (cacheRecord) { // zombies should not be (yet) destroyed, but if we encounter one... if (cacheRecord._destroyed) { + // if destroyed, invalidation routine will get triggered for us automatically cacheRecord.revive(); } else { - // if zombie ready, do not overwrite its data + // if zombie ready, do not overwrite its data, in that case try to call + // we need to trigger invalidation routine, data was not part of the system! + if (typeof data === 'function') { + options.data(); + } delete options.data; } delete this._zombiesLoaded[cacheKey]; diff --git a/src/viewer.js b/src/viewer.js index f3be8102..482e127a 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2732,7 +2732,8 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal waitUntilReady(new $TileSource(options), tileSource); } } else { - //can assume it's already a tile source implementation + //can assume it's already a tile source implementation, force inheritance + tileSource = $.extend({}, $.TileSource.prototype, tileSource); waitUntilReady(tileSource, tileSource); } }); diff --git a/src/webgldrawer.js b/src/webgldrawer.js index 536c264d..6a13f555 100644 --- a/src/webgldrawer.js +++ b/src/webgldrawer.js @@ -240,6 +240,17 @@ if(!this._backupCanvasDrawer){ this._backupCanvasDrawer = this.viewer.requestDrawer('canvas', {mainDrawer: false}); this._backupCanvasDrawer.canvas.style.setProperty('visibility', 'hidden'); + this._backupCanvasDrawer.getSupportedDataFormats = () => this._supportedFormats; + this._backupCanvasDrawer.getDataToDraw = (tile) => { + const cache = tile.getCache(tile.cacheKey); + if (!cache) { + $.console.warn("Attempt to draw tile %s when not cached!", tile); + return undefined; + } + const dataCache = cache.getDataForRendering(this, tile); + // Use CPU Data for the drawer instead + return dataCache && dataCache.cpuData; + }; } return this._backupCanvasDrawer; @@ -926,7 +937,8 @@ // TextureInfo stored in the cache return { texture: texture, - position: position + position: position, + cpuData: data // Reference to the outer cache data, used to draw if webgl canont be used }; }; const tex2DCompatibleDestructor = textureInfo => {