mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-22 05:06:09 +03:00
Better tile caching for duplicate images
This commit is contained in:
parent
8466a91470
commit
8641279890
28
src/tile.js
28
src/tile.js
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
var TILE_CACHE = {};
|
||||
|
||||
/**
|
||||
* @class Tile
|
||||
* @memberof OpenSeadragon
|
||||
@ -241,16 +241,23 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
rendered,
|
||||
canvas;
|
||||
|
||||
if ( !this.loaded || !( this.image || TILE_CACHE[ this.url ] ) ){
|
||||
if (!this.cacheImageRecord) {
|
||||
$.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached', this.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
rendered = this.cacheImageRecord.getRenderedContext();
|
||||
|
||||
if ( !this.loaded || !( this.image || rendered) ){
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
//context.save();
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
//if we are supposed to be rendering fully opaque rectangle,
|
||||
//ie its done fading or fading is turned off, and if we are drawing
|
||||
@ -268,24 +275,21 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
|
||||
}
|
||||
|
||||
if( !TILE_CACHE[ this.url ] ){
|
||||
if(!rendered){
|
||||
canvas = document.createElement( 'canvas' );
|
||||
canvas.width = this.image.width;
|
||||
canvas.height = this.image.height;
|
||||
rendered = canvas.getContext('2d');
|
||||
rendered.drawImage( this.image, 0, 0 );
|
||||
TILE_CACHE[ this.url ] = rendered;
|
||||
this.cacheImageRecord.setRenderedContext(rendered);
|
||||
//since we are caching the prerendered image on a canvas
|
||||
//allow the image to not be held in memory
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
rendered = TILE_CACHE[ this.url ];
|
||||
|
||||
// This gives the application a chance to make image manipulation changes as we are rendering the image
|
||||
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||
|
||||
//rendered.save();
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
0,
|
||||
@ -297,9 +301,6 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
size.x,
|
||||
size.y
|
||||
);
|
||||
//rendered.restore();
|
||||
|
||||
//context.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -313,9 +314,6 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
if ( this.element && this.element.parentNode ) {
|
||||
this.element.parentNode.removeChild( this.element );
|
||||
}
|
||||
if ( TILE_CACHE[ this.url ]){
|
||||
delete TILE_CACHE[ this.url ];
|
||||
}
|
||||
|
||||
this.element = null;
|
||||
this.imgElement = null;
|
||||
|
@ -43,6 +43,53 @@ var TileRecord = function( options ) {
|
||||
this.tiledImage = options.tiledImage;
|
||||
};
|
||||
|
||||
// private
|
||||
var ImageRecord = function(options) {
|
||||
$.console.assert( options, "[ImageRecord] options is required" );
|
||||
$.console.assert( options.image, "[ImageRecord] options.image is required" );
|
||||
this._image = options.image;
|
||||
this._tiles = [];
|
||||
};
|
||||
|
||||
ImageRecord.prototype = {
|
||||
destroy: function() {
|
||||
this._image = null;
|
||||
this._renderedContext = null;
|
||||
this._tiles = null;
|
||||
},
|
||||
|
||||
getImage: function() {
|
||||
return this._image;
|
||||
},
|
||||
|
||||
getRenderedContext: function() {
|
||||
return this._renderedContext;
|
||||
},
|
||||
|
||||
setRenderedContext: function(renderedContext) {
|
||||
this._renderedContext = renderedContext;
|
||||
},
|
||||
|
||||
addTile: function(tile) {
|
||||
this._tiles.push(tile);
|
||||
},
|
||||
|
||||
removeTile: function(tile) {
|
||||
for (var i = 0; i < this._tiles.length; i++) {
|
||||
if (this._tiles[i] === tile) {
|
||||
this._tiles.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$.console.warn('[ImageRecord.removeTile] trying to remove unknown tile', tile);
|
||||
},
|
||||
|
||||
getTileCount: function() {
|
||||
return this._tiles.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class TileCache
|
||||
* @memberof OpenSeadragon
|
||||
@ -54,8 +101,10 @@ var TileRecord = function( options ) {
|
||||
$.TileCache = function( options ) {
|
||||
options = options || {};
|
||||
|
||||
this._tilesLoaded = [];
|
||||
this._maxImageCacheCount = options.maxImageCacheCount || $.DEFAULT_SETTINGS.maxImageCacheCount;
|
||||
this._tilesLoaded = [];
|
||||
this._imagesLoaded = [];
|
||||
this._imagesLoadedCount = 0;
|
||||
};
|
||||
|
||||
$.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
@ -69,7 +118,10 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
|
||||
/**
|
||||
* Caches the specified tile, removing an old tile if necessary to stay under the
|
||||
* maxImageCacheCount specified on construction.
|
||||
* maxImageCacheCount specified on construction. Note that if multiple tiles reference
|
||||
* the same image, there may be more tiles than maxImageCacheCount; the goal is to keep
|
||||
* the number of images below that number. Note, as well, that even the number of images
|
||||
* may temporarily surpass that number, but should eventually come back down to the max specified.
|
||||
* @param {Object} options - Tile info.
|
||||
* @param {OpenSeadragon.Tile} options.tile - The tile to cache.
|
||||
* @param {OpenSeadragon.TiledImage} options.tiledImage - The TiledImage that owns that tile.
|
||||
@ -80,12 +132,28 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
cacheTile: function( options ) {
|
||||
$.console.assert( options, "[TileCache.cacheTile] options is required" );
|
||||
$.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" );
|
||||
$.console.assert( options.tile.url, "[TileCache.cacheTile] options.tile.url is required" );
|
||||
$.console.assert( options.tile.image, "[TileCache.cacheTile] options.tile.image is required" );
|
||||
$.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
|
||||
|
||||
var cutoff = options.cutoff || 0;
|
||||
var insertionIndex = this._tilesLoaded.length;
|
||||
|
||||
if ( this._tilesLoaded.length >= this._maxImageCacheCount ) {
|
||||
var imageRecord = this._imagesLoaded[options.tile.url];
|
||||
if (!imageRecord) {
|
||||
imageRecord = this._imagesLoaded[options.tile.url] = new ImageRecord({
|
||||
image: options.tile.image
|
||||
});
|
||||
|
||||
this._imagesLoadedCount++;
|
||||
}
|
||||
|
||||
imageRecord.addTile(options.tile);
|
||||
options.tile.cacheImageRecord = imageRecord;
|
||||
|
||||
// Note that just because we're unloading a tile doesn't necessarily mean
|
||||
// we're unloading an image. With repeated calls it should sort itself out, though.
|
||||
if ( this._imagesLoadedCount >= this._maxImageCacheCount ) {
|
||||
var worstTile = null;
|
||||
var worstTileIndex = -1;
|
||||
var prevTile, worstTime, worstLevel, prevTime, prevLevel, prevTileRecord;
|
||||
@ -115,7 +183,7 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
}
|
||||
|
||||
if ( worstTile && worstTileIndex >= 0 ) {
|
||||
worstTile.unload();
|
||||
this._unloadTile(worstTile);
|
||||
insertionIndex = worstTileIndex;
|
||||
}
|
||||
}
|
||||
@ -134,11 +202,29 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
for ( var i = 0; i < this._tilesLoaded.length; ++i ) {
|
||||
tileRecord = this._tilesLoaded[ i ];
|
||||
if ( tileRecord.tiledImage === tiledImage ) {
|
||||
tileRecord.tile.unload();
|
||||
this._unloadTile(tileRecord.tile);
|
||||
this._tilesLoaded.splice( i, 1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getImageRecord: function(url) {
|
||||
return this._imagesLoaded[url];
|
||||
},
|
||||
|
||||
// private
|
||||
_unloadTile: function(tile) {
|
||||
tile.unload();
|
||||
tile.cacheImageRecord = null;
|
||||
|
||||
var imageRecord = this._imagesLoaded[tile.url];
|
||||
imageRecord.removeTile(tile);
|
||||
if (!imageRecord.getTileCount()) {
|
||||
imageRecord.destroy();
|
||||
delete this._imagesLoaded[tile.url];
|
||||
this._imagesLoadedCount--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -485,6 +485,19 @@ function updateTile( tiledImage, drawLevel, haveDrawn, x, y, level, levelOpacity
|
||||
tiledImage
|
||||
);
|
||||
|
||||
if (!tile.loaded) {
|
||||
var imageRecord = tiledImage._tileCache.getImageRecord(tile.url);
|
||||
if (imageRecord) {
|
||||
tile.loaded = true;
|
||||
tile.image = imageRecord.getImage();
|
||||
|
||||
tiledImage._tileCache.cacheTile({
|
||||
tile: tile,
|
||||
tiledImage: tiledImage
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ( tile.loaded ) {
|
||||
var needsUpdate = blendTile(
|
||||
tiledImage,
|
||||
|
@ -9,12 +9,15 @@
|
||||
var testInitialOpen = false;
|
||||
var testOverlays = false;
|
||||
var testMargins = false;
|
||||
var testNavigator = false;
|
||||
var margins;
|
||||
|
||||
var config = {
|
||||
debugMode: true,
|
||||
zoomPerScroll: 1.02,
|
||||
showNavigator: true,
|
||||
showNavigator: testNavigator,
|
||||
wrapHorizontal: true,
|
||||
wrapVertical: true,
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../../build/openseadragon/images/"
|
||||
};
|
||||
@ -83,7 +86,7 @@
|
||||
}
|
||||
|
||||
// this.crossTest3();
|
||||
this.basicTest();
|
||||
this.crossTest2();
|
||||
},
|
||||
|
||||
// ----------
|
||||
@ -119,7 +122,8 @@
|
||||
self.viewer.addTiledImage( options );
|
||||
});
|
||||
|
||||
this.viewer.open("../../data/tall.dzi", {
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
@ -129,12 +133,13 @@
|
||||
// ----------
|
||||
crossTest2: function() {
|
||||
this.viewer.open([
|
||||
// {
|
||||
// tileSource: "../../data/tall.dzi",
|
||||
// x: 1.5,
|
||||
// y: 0,
|
||||
// width: 1
|
||||
// },
|
||||
{
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
}, {
|
||||
tileSource: '../../data/wide.dzi',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
@ -184,7 +189,7 @@
|
||||
self.viewer.world.addHandler('add-item', function() {
|
||||
loaded++;
|
||||
if (loaded === expected) {
|
||||
self.viewer.viewport.goHome();
|
||||
self.viewer.viewport.goHome(true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -208,7 +213,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
this.viewer.open("../../data/testpattern.dzi", {
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
x: startX,
|
||||
y: 0,
|
||||
width: 1
|
||||
@ -217,7 +223,8 @@
|
||||
|
||||
// ----------
|
||||
bigTest: function() {
|
||||
this.viewer.open("../../data/testpattern.dzi", {
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
x: -2,
|
||||
y: -2,
|
||||
width: 6
|
||||
@ -246,7 +253,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
this.viewer.open(dzi, {
|
||||
this.viewer.open({
|
||||
tileSource: dzi,
|
||||
width: 100
|
||||
});
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user