Add auto recognition of the need for tiledImage draw call. Fix ajax-headers test: did not finish because we don't call tile-loaded on cached tiles by default.

This commit is contained in:
Aiosa 2023-11-27 12:12:54 +01:00
parent 2c67860c61
commit 90ce0669c5
5 changed files with 86 additions and 26 deletions

View File

@ -194,7 +194,7 @@ $.DataTypeConvertor = class {
const canvas = document.createElement( 'canvas' );
canvas.width = imageData.width;
canvas.height = imageData.height;
const context = canvas.getContext('2d');
const context = canvas.getContext('2d', { willReadFrequently: true });
context.drawImage( imageData, 0, 0 );
return context;
};

View File

@ -469,7 +469,7 @@ $.Tile.prototype = {
},
/**
* Get the default data for this tile
* Get the data to render for this tile
* @param {string} type data type to require
* @param {boolean?} [copy=this.loaded] whether to force copy retrieval
* @return {*|undefined} data in the desired type, or undefined if a conversion is ongoing
@ -484,6 +484,22 @@ $.Tile.prototype = {
return cache.getDataAs(type, copy);
},
/**
* 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
* @return {*|undefined} data in the desired type, or undefined if a conversion is ongoing
*/
getOriginalData: function(type, copy = true) {
//we return the data synchronously immediatelly (undefined if conversion happens)
const cache = this.getCache(this.originalCacheKey);
if (!cache) {
$.console.error("[Tile::getData] There is no cache available for tile with key " + this.originalCacheKey);
return undefined;
}
return cache.getDataAs(type, copy);
},
/**
* Set cache data
* @param {*} value
@ -539,7 +555,8 @@ $.Tile.prototype = {
type = $.convertor.guessType(data);
}
if (_safely && key === this.cacheKey) {
const writesToRenderingCache = key === this.cacheKey;
if (writesToRenderingCache && _safely) {
//todo later, we could have drawers register their supported rendering type
// and OpenSeadragon would check compatibility automatically, now we render
// using two main types so we check their ability
@ -609,25 +626,53 @@ $.Tile.prototype = {
drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
var position = this.position.times($.pixelDensityRatio),
size = this.size.times($.pixelDensityRatio),
rendered = this.getCanvasContext();
size = this.size.times($.pixelDensityRatio);
if (!rendered) {
$.console.warn(
'[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached',
this.toString());
return;
}
const _this = this;
// This gives the application a chance to make image manipulation
// changes as we are rendering the image
drawingHandler({context: context, tile: this, get rendered() {
$.console.warn("[tile-drawing rendered] property is deprecated. Use Tile data API.");
const context = _this.getCanvasContext();
if (!context) {
$.console.warn(
'[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached',
_this.toString());
return undefined;
}
if ( !this.loaded || !rendered ){
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
if ( !_this.loaded || !context ){
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
_this.toString()
);
return undefined;
}
return _this.getCanvasContext();
}});
//Now really get the tile data
const cache = this.getCache(this.cacheKey);
if (!cache) {
$.console.error(
"Attempting to draw tile %s when it's main cache key has no associated cache record!",
this.toString()
);
return;
}
if (cache.type !== "context2d") {
//cache not ready to render, wait
cache.transformTo("context2d");
return;
}
if ( !cache.loaded ){
//cache not ready to render, wait
return;
}
const rendered = cache.data;
context.save();
context.globalAlpha = this.opacity;
@ -665,10 +710,6 @@ $.Tile.prototype = {
);
}
// This gives the application a chance to make image manipulation
// changes as we are rendering the image
drawingHandler({context: context, tile: this, rendered: rendered});
var sourceWidth, sourceHeight;
if (this.sourceBounds) {
sourceWidth = Math.min(this.sourceBounds.width, rendered.canvas.width);

View File

@ -65,7 +65,7 @@ $.CacheRecord = class {
* Might be undefined if this.loaded = false.
* You can access the data in synchronous way, but the data might not be available.
* If you want to access the data indirectly (await), use this.transformTo or this.getDataAs
* @return {any}
* @returns {any}
*/
get data() {
return this._data;
@ -74,7 +74,7 @@ $.CacheRecord = class {
/**
* Read the cache type. The type can dynamically change, but should be consistent at
* one point in the time. For available types see the OpenSeadragon.Convertor, or the tutorials.
* @return {string}
* @returns {string}
*/
get type() {
return this._type;
@ -315,6 +315,12 @@ $.CacheRecord = class {
});
}
_triggerNeedsDraw() {
for (let tile of this._tiles) {
tile.tiledImage._needsDraw = true;
}
}
/**
* Safely overwrite the cache data and return the old data
* @private
@ -330,6 +336,7 @@ $.CacheRecord = class {
this._type = type;
this._data = data;
this._promise = $.Promise.resolve(data);
this._triggerNeedsDraw();
return this._promise;
}
return this._promise.then(x => {
@ -337,6 +344,7 @@ $.CacheRecord = class {
this._type = type;
this._data = data;
this._promise = $.Promise.resolve(data);
this._triggerNeedsDraw();
return x;
});
}
@ -481,6 +489,9 @@ $.TileCache = class {
}
cacheRecord.addTile(theTile, options.data, options.dataType);
if (cacheKey === theTile.cacheKey) {
theTile.tiledImage._needsDraw = true;
}
// Note that just because we're unloading a tile doesn't necessarily mean
// we're unloading its cache records. With repeated calls it should sort itself out, though.

View File

@ -49,7 +49,15 @@
loadTilesWithAjax: true,
ajaxHeaders: {
'X-Viewer-Header': 'ViewerHeaderValue'
}
},
// TODO: this test proves that tile cacheKey does not change
// with headers change, which by default are part of the key,
// but we cannot automatically update them since users might
// manually change it long before... or can we? The tile gets
// reloaded, so user code should get re-executed. IMHO,
// with tile propagation the best would be throw away old tile
// and start anew
callTileLoadedWithCachedData: true
});
},
afterEach: function() {

View File

@ -213,7 +213,7 @@
const dummyRect = new OpenSeadragon.Rect(0, 0, 0, 0, 0);
const dummyTile = new OpenSeadragon.Tile(0, 0, 0, dummyRect, true, "",
undefined, true, null, dummyRect, "", "key");
dummyTile.tiledImage = {};
const cache = new OpenSeadragon.CacheRecord();
cache.addTile(dummyTile, "/test/data/A.png", "__TEST__url");
@ -262,7 +262,7 @@
const dummyRect = new OpenSeadragon.Rect(0, 0, 0, 0, 0);
const dummyTile = new OpenSeadragon.Tile(0, 0, 0, dummyRect, true, "",
undefined, true, null, dummyRect, "", "key");
dummyTile.tiledImage = {};
const cache = new OpenSeadragon.CacheRecord();
cache.testGetSet = async function(type) {
const value = await cache.getDataAs(type, false);
@ -330,7 +330,7 @@
const dummyRect = new OpenSeadragon.Rect(0, 0, 0, 0, 0);
const dummyTile = new OpenSeadragon.Tile(0, 0, 0, dummyRect, true, "",
undefined, true, null, dummyRect, "", "key");
dummyTile.tiledImage = {};
const cache = new OpenSeadragon.CacheRecord();
cache.addTile(dummyTile, "/test/data/A.png", "__TEST__url");
cache.getDataAs("__TEST__longConversionProcessForTesting").then(convertedData => {
@ -374,7 +374,7 @@
const dummyRect = new OpenSeadragon.Rect(0, 0, 0, 0, 0);
const dummyTile = new OpenSeadragon.Tile(0, 0, 0, dummyRect, true, "",
undefined, true, null, dummyRect, "", "key");
dummyTile.tiledImage = {};
const cache = new OpenSeadragon.CacheRecord();
cache.addTile(dummyTile, "/test/data/A.png", "__TEST__url");
cache.transformTo("__TEST__longConversionProcessForTesting").then(_ => {