2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
(function( $ ){
|
|
|
|
|
2012-01-24 17:03:50 +04:00
|
|
|
// the max number of images we should keep in memory
|
|
|
|
var QUOTA = 100,
|
2012-01-12 03:22:13 +04:00
|
|
|
// the most shrunk a tile should be
|
|
|
|
MIN_PIXEL_RATIO = 0.5,
|
|
|
|
//TODO: make TIMEOUT configurable
|
|
|
|
TIMEOUT = 5000,
|
|
|
|
|
2012-01-18 03:30:41 +04:00
|
|
|
BROWSER = $.Browser.vendor,
|
|
|
|
BROWSER_VERSION = $.Browser.version,
|
2012-01-12 03:22:13 +04:00
|
|
|
|
|
|
|
SUBPIXEL_RENDERING = (
|
2012-01-18 03:30:41 +04:00
|
|
|
( BROWSER == $.BROWSERS.FIREFOX ) ||
|
|
|
|
( BROWSER == $.BROWSERS.OPERA ) ||
|
|
|
|
( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) ||
|
|
|
|
( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 )
|
2012-01-12 03:22:13 +04:00
|
|
|
),
|
|
|
|
|
2012-01-24 17:03:50 +04:00
|
|
|
USE_CANVAS = $.isFunction( document.createElement( "canvas" ).getContext ) &&
|
2012-01-12 03:22:13 +04:00
|
|
|
SUBPIXEL_RENDERING;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
$.Drawer = function(source, viewport, elmt) {
|
|
|
|
|
2012-01-18 03:30:41 +04:00
|
|
|
this.container = $.getElement( elmt );
|
|
|
|
this.canvas = $.makeNeutralElement( USE_CANVAS ? "canvas" : "div" );
|
2012-01-24 07:48:45 +04:00
|
|
|
this.context = USE_CANVAS ? this.canvas.getContext( "2d" ) : null;
|
2012-01-12 03:22:13 +04:00
|
|
|
this.viewport = viewport;
|
|
|
|
this.source = source;
|
|
|
|
this.config = this.viewport.config;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-12 03:22:13 +04:00
|
|
|
this.downloading = 0;
|
|
|
|
this.imageLoaderLimit = this.config.imageLoaderLimit;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-24 07:48:45 +04:00
|
|
|
//this.profiler = new $.Profiler();
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-12 03:22:13 +04:00
|
|
|
this.minLevel = source.minLevel;
|
|
|
|
this.maxLevel = source.maxLevel;
|
|
|
|
this.tileSize = source.tileSize;
|
|
|
|
this.tileOverlap = source.tileOverlap;
|
|
|
|
this.normHeight = source.dimensions.y / source.dimensions.x;
|
|
|
|
|
|
|
|
// 1d dictionary [level] --> Point
|
|
|
|
this.cacheNumTiles = {};
|
|
|
|
// 1d dictionary [level] --> Point
|
|
|
|
this.cachePixelRatios = {};
|
|
|
|
// 3d dictionary [level][x][y] --> Tile
|
|
|
|
this.tilesMatrix = {};
|
|
|
|
// unordered list of Tiles with loaded images
|
|
|
|
this.tilesLoaded = [];
|
|
|
|
// 3d dictionary [level][x][y] --> Boolean
|
|
|
|
this.coverage = {};
|
|
|
|
|
|
|
|
// unordered list of Overlays added
|
|
|
|
this.overlays = [];
|
|
|
|
// unordered list of Tiles drawn last frame
|
|
|
|
this.lastDrawn = [];
|
|
|
|
this.lastResetTime = 0;
|
|
|
|
this.midUpdate = false;
|
|
|
|
this.updateAgain = true;
|
|
|
|
|
|
|
|
this.elmt = this.container;
|
|
|
|
|
|
|
|
this.canvas.style.width = "100%";
|
|
|
|
this.canvas.style.height = "100%";
|
|
|
|
this.canvas.style.position = "absolute";
|
2012-01-12 03:25:35 +04:00
|
|
|
|
2012-01-12 03:22:13 +04:00
|
|
|
// explicit left-align
|
|
|
|
this.container.style.textAlign = "left";
|
|
|
|
this.container.appendChild(this.canvas);
|
2011-12-06 07:50:25 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
$.Drawer.prototype = {
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
getPixelRatio: function( level ) {
|
|
|
|
if ( !this.cachePixelRatios[ level ] ) {
|
|
|
|
this.cachePixelRatios[ level ] = this.source.getPixelRatio( level );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
return this.cachePixelRatios[ level ];
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_getTile: function( level, x, y, time, numTilesX, numTilesY ) {
|
|
|
|
var xMod,
|
|
|
|
yMod,
|
|
|
|
bounds,
|
|
|
|
exists,
|
|
|
|
url,
|
|
|
|
tile;
|
|
|
|
|
|
|
|
if ( !this.tilesMatrix[ level ] ) {
|
|
|
|
this.tilesMatrix[ level ] = {};
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.tilesMatrix[ level ][ x ] ) {
|
|
|
|
this.tilesMatrix[ level ][ x ] = {};
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.tilesMatrix[ level ][ x ][ y ] ) {
|
|
|
|
xMod = ( numTilesX + ( x % numTilesX ) ) % numTilesX;
|
|
|
|
yMod = ( numTilesY + ( y % numTilesY ) ) % numTilesY;
|
|
|
|
bounds = this.source.getTileBounds( level, xMod, yMod );
|
|
|
|
exists = this.source.tileExists( level, xMod, yMod );
|
|
|
|
url = this.source.getTileUrl( level, xMod, yMod );
|
|
|
|
|
|
|
|
bounds.x += 1.0 * ( x - xMod ) / numTilesX;
|
|
|
|
bounds.y += this.normHeight * ( y - yMod ) / numTilesY;
|
|
|
|
|
|
|
|
this.tilesMatrix[ level ][ x ][ y ] = new $.Tile(
|
|
|
|
level,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
bounds,
|
|
|
|
exists,
|
|
|
|
url
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
tile = this.tilesMatrix[ level ][ x ][ y ];
|
2011-12-06 07:50:25 +04:00
|
|
|
tile.lastTouchTime = time;
|
|
|
|
|
|
|
|
return tile;
|
|
|
|
},
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_loadTile: function( tile, time ) {
|
2011-12-28 03:17:24 +04:00
|
|
|
tile.loading = this.loadImage(
|
|
|
|
tile.url,
|
2012-01-18 03:30:41 +04:00
|
|
|
$.createCallback(
|
2011-12-28 03:17:24 +04:00
|
|
|
null,
|
2012-01-19 05:15:54 +04:00
|
|
|
$.delegate( this, this._onTileLoad ),
|
2011-12-28 03:17:24 +04:00
|
|
|
tile,
|
|
|
|
time
|
|
|
|
)
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-24 07:48:45 +04:00
|
|
|
_onTileLoad: function( tile, time, image ) {
|
2012-01-19 05:15:54 +04:00
|
|
|
var insertionIndex,
|
|
|
|
cutoff,
|
|
|
|
worstTile,
|
|
|
|
worstTime,
|
|
|
|
worstLevel,
|
|
|
|
worstTileIndex,
|
|
|
|
prevTile,
|
|
|
|
prevTime,
|
|
|
|
prevLevel,
|
|
|
|
i;
|
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
tile.loading = false;
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( this.midUpdate ) {
|
2012-01-24 07:48:45 +04:00
|
|
|
$.Debug.warn( "Tile load callback in middle of drawing routine." );
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
2012-01-19 05:15:54 +04:00
|
|
|
} else if ( !image ) {
|
2012-01-24 07:48:45 +04:00
|
|
|
$.Debug.log( "Tile %s failed to load: %s", tile, tile.url );
|
2011-12-06 07:50:25 +04:00
|
|
|
tile.exists = false;
|
|
|
|
return;
|
2012-01-19 05:15:54 +04:00
|
|
|
} else if ( time < this.lastResetTime ) {
|
2012-01-24 07:48:45 +04:00
|
|
|
$.Debug.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tile.loaded = true;
|
2012-01-19 05:15:54 +04:00
|
|
|
tile.image = image;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
insertionIndex = this.tilesLoaded.length;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( this.tilesLoaded.length >= QUOTA ) {
|
|
|
|
cutoff = Math.ceil( Math.log( this.tileSize ) / Math.log( 2 ) );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
worstTile = null;
|
|
|
|
worstTileIndex = -1;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
for ( i = this.tilesLoaded.length - 1; i >= 0; i-- ) {
|
|
|
|
prevTile = this.tilesLoaded[ i ];
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( prevTile.level <= this.cutoff || prevTile.beingDrawn ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
continue;
|
2012-01-19 05:15:54 +04:00
|
|
|
} else if ( !worstTile ) {
|
|
|
|
worstTile = prevTile;
|
|
|
|
worstTileIndex = i;
|
2011-12-06 07:50:25 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
prevTime = prevTile.lastTouchTime;
|
|
|
|
worstTime = worstTile.lastTouchTime;
|
|
|
|
prevLevel = prevTile.level;
|
|
|
|
worstLevel = worstTile.level;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( prevTime < worstTime ||
|
|
|
|
( prevTime == worstTime && prevLevel > worstLevel ) ) {
|
|
|
|
worstTile = prevTile;
|
|
|
|
worstTileIndex = i;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( worstTile && worstTileIndex >= 0 ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
worstTile.unload();
|
|
|
|
insertionIndex = worstTileIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
this.tilesLoaded[ insertionIndex ] = tile;
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
_clearTiles: function() {
|
2012-01-12 03:22:13 +04:00
|
|
|
this.tilesMatrix = {};
|
|
|
|
this.tilesLoaded = [];
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the given tile provides coverage to lower-level tiles of
|
|
|
|
* lower resolution representing the same content. If neither x nor y is
|
|
|
|
* given, returns true if the entire visible level provides coverage.
|
|
|
|
*
|
|
|
|
* Note that out-of-bounds tiles provide coverage in this sense, since
|
|
|
|
* there's no content that they would need to cover. Tiles at non-existent
|
|
|
|
* levels that are within the image bounds, however, do not.
|
|
|
|
*/
|
2012-01-19 05:15:54 +04:00
|
|
|
_providesCoverage: function( level, x, y ) {
|
|
|
|
var rows,
|
|
|
|
cols,
|
|
|
|
i, j;
|
|
|
|
|
|
|
|
if ( !this.coverage[ level ] ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( x === undefined || y === undefined ) {
|
|
|
|
rows = this.coverage[ level ];
|
|
|
|
for ( i in rows ) {
|
|
|
|
if ( rows.hasOwnProperty( i ) ) {
|
|
|
|
cols = rows[ i ];
|
|
|
|
for ( j in cols ) {
|
|
|
|
if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
return (
|
|
|
|
this.coverage[ level ][ x] === undefined ||
|
|
|
|
this.coverage[ level ][ x ][ y ] === undefined ||
|
|
|
|
this.coverage[ level ][ x ][ y ] === true
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the given tile is completely covered by higher-level
|
|
|
|
* tiles of higher resolution representing the same content. If neither x
|
|
|
|
* nor y is given, returns true if the entire visible level is covered.
|
|
|
|
*/
|
2012-01-19 05:15:54 +04:00
|
|
|
_isCovered: function( level, x, y ) {
|
|
|
|
if ( x === undefined || y === undefined ) {
|
|
|
|
return this._providesCoverage( level + 1 );
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
2012-01-19 05:15:54 +04:00
|
|
|
return (
|
|
|
|
this._providesCoverage( level + 1, 2 * x, 2 * y ) &&
|
|
|
|
this._providesCoverage( level + 1, 2 * x, 2 * y + 1 ) &&
|
|
|
|
this._providesCoverage( level + 1, 2 * x + 1, 2 * y ) &&
|
|
|
|
this._providesCoverage( level + 1, 2 * x + 1, 2 * y + 1 )
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets whether the given tile provides coverage or not.
|
|
|
|
*/
|
2012-01-19 05:15:54 +04:00
|
|
|
_setCoverage: function( level, x, y, covers ) {
|
|
|
|
if ( !this.coverage[ level ] ) {
|
2012-01-24 07:48:45 +04:00
|
|
|
$.Debug.warn(
|
|
|
|
"Setting coverage for a tile before its level's coverage has been reset: %s",
|
|
|
|
level
|
2012-01-19 05:15:54 +04:00
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.coverage[ level ][ x ] ) {
|
|
|
|
this.coverage[ level ][ x ] = {};
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
this.coverage[ level ][ x ][ y ] = covers;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets coverage information for the given level. This should be called
|
|
|
|
* after every draw routine. Note that at the beginning of the next draw
|
|
|
|
* routine, coverage for every visible tile should be explicitly set.
|
|
|
|
*/
|
2012-01-19 05:15:54 +04:00
|
|
|
_resetCoverage: function( level ) {
|
|
|
|
this.coverage[ level ] = {};
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_compareTiles: function( prevBest, tile ) {
|
|
|
|
if ( !prevBest ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( tile.visibility > prevBest.visibility ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return tile;
|
2012-01-19 05:15:54 +04:00
|
|
|
} else if ( tile.visibility == prevBest.visibility ) {
|
|
|
|
if ( tile.distance < prevBest.distance ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return prevBest;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_getOverlayIndex: function( elmt ) {
|
|
|
|
var i;
|
|
|
|
for ( i = this.overlays.length - 1; i >= 0; i-- ) {
|
|
|
|
if ( this.overlays[ i ].elmt == elmt ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
_updateActual: function() {
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = false;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
var tile,
|
2012-01-19 05:15:54 +04:00
|
|
|
level,
|
|
|
|
viewportSize = this.viewport.getContainerSize(),
|
|
|
|
viewportWidth = viewportSize.x,
|
|
|
|
viewportHeight = viewportSize.y,
|
|
|
|
viewportBounds = this.viewport.getBounds( true ),
|
|
|
|
viewportTL = viewportBounds.getTopLeft(),
|
|
|
|
viewportBR = viewportBounds.getBottomRight(),
|
|
|
|
haveDrawn = false,
|
2012-01-19 06:52:22 +04:00
|
|
|
best = null,
|
2012-01-19 05:15:54 +04:00
|
|
|
currentTime = new Date().getTime(),
|
|
|
|
zeroRatioC = this.viewport.deltaPixelsFromPoints(
|
|
|
|
this.source.getPixelRatio( 0 ),
|
|
|
|
true
|
|
|
|
).x,
|
|
|
|
lowestLevel = Math.max(
|
|
|
|
this.minLevel,
|
|
|
|
Math.floor(
|
|
|
|
Math.log( this.config.minZoomImageRatio ) /
|
|
|
|
Math.log( 2 )
|
|
|
|
)
|
|
|
|
),
|
|
|
|
highestLevel = Math.min(
|
|
|
|
this.maxLevel,
|
|
|
|
Math.floor(
|
|
|
|
Math.log( zeroRatioC / MIN_PIXEL_RATIO ) /
|
|
|
|
Math.log( 2 )
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
while ( this.lastDrawn.length > 0 ) {
|
|
|
|
tile = this.lastDrawn.pop();
|
2011-12-06 07:50:25 +04:00
|
|
|
tile.beingDrawn = false;
|
|
|
|
}
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
this.canvas.innerHTML = "";
|
2012-01-12 03:22:13 +04:00
|
|
|
if ( USE_CANVAS ) {
|
2012-01-19 05:15:54 +04:00
|
|
|
this.canvas.width = viewportWidth;
|
|
|
|
this.canvas.height = viewportHeight;
|
|
|
|
this.context.clearRect( 0, 0, viewportWidth, viewportHeight );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.config.wrapHorizontal &&
|
|
|
|
( viewportBR.x < 0 || viewportTL.x > 1 ) ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
2012-01-19 06:52:22 +04:00
|
|
|
} else if
|
|
|
|
( !this.config.wrapVertical &&
|
2012-01-19 05:15:54 +04:00
|
|
|
( viewportBR.y < 0 || viewportTL.y > this.normHeight ) ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.config.wrapHorizontal ) {
|
|
|
|
viewportTL.x = Math.max( viewportTL.x, 0 );
|
|
|
|
viewportBR.x = Math.min( viewportBR.x, 1 );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !this.config.wrapVertical ) {
|
|
|
|
viewportTL.y = Math.max( viewportTL.y, 0 );
|
|
|
|
viewportBR.y = Math.min( viewportBR.y, this.normHeight );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
lowestLevel = Math.min( lowestLevel, highestLevel );
|
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-19 05:15:54 +04:00
|
|
|
for ( level = highestLevel; level >= lowestLevel; level-- ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
2012-01-24 07:48:45 +04:00
|
|
|
best = this._drawLevel(
|
|
|
|
level,
|
|
|
|
lowestLevel,
|
|
|
|
viewportTL,
|
|
|
|
viewportBR,
|
|
|
|
currentTime,
|
|
|
|
best
|
|
|
|
);
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
|
|
|
if ( this._providesCoverage( level ) ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
|
|
|
this._drawTiles();
|
|
|
|
this._drawOverlays();
|
2012-01-19 05:15:54 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//TODO
|
|
|
|
if ( best ) {
|
|
|
|
this._loadTile( best, currentTime );
|
|
|
|
// because we haven't finished drawing, so
|
|
|
|
this.updateAgain = true;
|
|
|
|
}
|
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
_drawLevel: function( level, lowestLevel, viewportTL, viewportBR, currentTime, best ){
|
|
|
|
var x, y,
|
|
|
|
levelOpacity,
|
|
|
|
levelVisibility,
|
|
|
|
drawTile,
|
|
|
|
tile,
|
|
|
|
tileTL,
|
|
|
|
tileBR,
|
|
|
|
numTiles,
|
|
|
|
numTilesX,
|
|
|
|
numTilesY,
|
|
|
|
renderPixelRatioC,
|
|
|
|
renderPixelRatioT,
|
|
|
|
levelOpacity,
|
|
|
|
levelVisibility,
|
|
|
|
haveDrawn = false,
|
|
|
|
drawLevel = false,
|
|
|
|
viewportCenter = this.viewport.pixelFromPoint( this.viewport.getCenter() ),
|
|
|
|
zeroRatioT = this.viewport.deltaPixelsFromPoints(
|
|
|
|
this.source.getPixelRatio( 0 ),
|
|
|
|
false
|
|
|
|
).x,
|
|
|
|
optimalRatio = this.config.immediateRender ?
|
|
|
|
1 :
|
|
|
|
zeroRatioT;
|
|
|
|
|
|
|
|
//Avoid calculations for draw if we have already drawn this
|
|
|
|
renderPixelRatioC = this.viewport.deltaPixelsFromPoints(
|
|
|
|
this.source.getPixelRatio( level ),
|
|
|
|
true
|
|
|
|
).x;
|
|
|
|
|
|
|
|
if ( ( !haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO ) ||
|
|
|
|
( level == lowestLevel ) ) {
|
|
|
|
drawLevel = true;
|
|
|
|
haveDrawn = true;
|
|
|
|
} else if ( !haveDrawn ) {
|
|
|
|
return best;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
//OK, a new drawing so do your calculations
|
|
|
|
tileTL = this.source.getTileAtPoint( level, viewportTL );
|
|
|
|
tileBR = this.source.getTileAtPoint( level, viewportBR );
|
|
|
|
numTiles = numberOfTiles( this, level );
|
|
|
|
numTilesX = numTiles.x;
|
|
|
|
numTilesY = numTiles.y;
|
|
|
|
|
|
|
|
renderPixelRatioT = this.viewport.deltaPixelsFromPoints(
|
|
|
|
this.source.getPixelRatio( level ),
|
|
|
|
false
|
|
|
|
).x;
|
|
|
|
|
|
|
|
levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 );
|
|
|
|
levelVisibility = optimalRatio / Math.abs(
|
|
|
|
optimalRatio - renderPixelRatioT
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
this._resetCoverage( level );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
if ( !this.config.wrapHorizontal ) {
|
|
|
|
tileBR.x = Math.min( tileBR.x, numTilesX - 1 );
|
|
|
|
}
|
|
|
|
if ( !this.config.wrapVertical ) {
|
|
|
|
tileBR.y = Math.min( tileBR.y, numTilesY - 1 );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
for ( x = tileTL.x; x <= tileBR.x; x++ ) {
|
|
|
|
for ( y = tileTL.y; y <= tileBR.y; y++ ) {
|
2012-01-24 07:48:45 +04:00
|
|
|
|
|
|
|
tile = this._getTile(
|
2012-01-19 06:52:22 +04:00
|
|
|
level,
|
|
|
|
x, y,
|
|
|
|
currentTime,
|
|
|
|
numTilesX,
|
|
|
|
numTilesY
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
this._setCoverage( level, x, y, false );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
if ( !tile.exists ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-24 07:48:45 +04:00
|
|
|
drawTile = drawLevel;
|
2012-01-19 06:52:22 +04:00
|
|
|
if ( haveDrawn && !drawTile ) {
|
|
|
|
if ( this._isCovered( level, x, y ) ) {
|
|
|
|
this._setCoverage( level, x, y, true );
|
2012-01-19 05:15:54 +04:00
|
|
|
} else {
|
2012-01-19 06:52:22 +04:00
|
|
|
drawTile = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
if ( !drawTile ) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
this._positionTile(
|
|
|
|
tile,
|
|
|
|
viewportCenter,
|
|
|
|
levelVisibility
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
if ( tile.loaded ) {
|
|
|
|
|
|
|
|
updateAgain = this._blendTile(
|
|
|
|
tile,
|
|
|
|
x, y,
|
|
|
|
level,
|
|
|
|
levelOpacity,
|
|
|
|
currentTime
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 06:52:22 +04:00
|
|
|
} else if ( tile.Loading ) {
|
|
|
|
//TODO: .Loading is never defined... did they mean .loading?
|
|
|
|
// but they didnt do anything so what is this block if
|
|
|
|
// if it does nothing.
|
|
|
|
} else {
|
|
|
|
best = this._compareTiles( best, tile );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return best;
|
2012-01-19 05:15:54 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_positionTile: function( tile, viewportCenter, levelVisibility ){
|
|
|
|
var boundsTL = tile.bounds.getTopLeft(),
|
|
|
|
boundsSize = tile.bounds.getSize(),
|
|
|
|
positionC = this.viewport.pixelFromPoint( boundsTL, true ),
|
|
|
|
sizeC = this.viewport.deltaPixelsFromPoints( boundsSize, true ),
|
|
|
|
positionT = this.viewport.pixelFromPoint( boundsTL, false ),
|
|
|
|
sizeT = this.viewport.deltaPixelsFromPoints( boundsSize, false ),
|
|
|
|
tileCenter = positionT.plus( sizeT.divide( 2 ) ),
|
|
|
|
tileDistance = viewportCenter.distanceTo( tileCenter );
|
|
|
|
|
|
|
|
if ( !this.tileOverlap ) {
|
|
|
|
sizeC = sizeC.plus( new $.Point( 1, 1 ) );
|
|
|
|
}
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
tile.position = positionC;
|
|
|
|
tile.size = sizeC;
|
|
|
|
tile.distance = tileDistance;
|
|
|
|
tile.visibility = levelVisibility;
|
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_blendTile: function( tile, x, y, level, levelOpacity, currentTime ){
|
|
|
|
var blendTimeMillis = 1000 * this.config.blendTime,
|
|
|
|
deltaTime,
|
|
|
|
opacity;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( !tile.blendStart ) {
|
|
|
|
tile.blendStart = currentTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
deltaTime = currentTime - tile.blendStart;
|
|
|
|
opacity = Math.min( 1, deltaTime / blendTimeMillis );
|
|
|
|
|
|
|
|
if ( this.config.alwaysBlend ) {
|
|
|
|
opacity *= levelOpacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
tile.opacity = opacity;
|
|
|
|
|
|
|
|
this.lastDrawn.push( tile );
|
|
|
|
|
|
|
|
if ( opacity == 1 ) {
|
|
|
|
this._setCoverage( level, x, y, true );
|
|
|
|
} else if ( deltaTime < blendTimeMillis ) {
|
|
|
|
return true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
_drawTiles: function(){
|
|
|
|
var i,
|
|
|
|
tile;
|
|
|
|
|
|
|
|
for ( i = this.lastDrawn.length - 1; i >= 0; i-- ) {
|
|
|
|
tile = this.lastDrawn[ i ];
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-12 03:22:13 +04:00
|
|
|
if ( USE_CANVAS ) {
|
2012-01-19 05:15:54 +04:00
|
|
|
tile.drawCanvas( this.context );
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
2012-01-19 05:15:54 +04:00
|
|
|
tile.drawHTML( this.canvas );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
tile.beingDrawn = true;
|
|
|
|
}
|
2012-01-19 05:15:54 +04:00
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_drawOverlays: function(){
|
|
|
|
var i,
|
|
|
|
length = this.overlays.length;
|
|
|
|
for ( i = 0; i < length; i++ ) {
|
|
|
|
this._drawOverlay( this.overlays[ i ] );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
_drawOverlay: function( overlay ){
|
|
|
|
|
|
|
|
var bounds = overlay.bounds;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
overlay.position = this.viewport.pixelFromPoint(
|
|
|
|
bounds.getTopLeft(),
|
|
|
|
true
|
|
|
|
);
|
|
|
|
overlay.size = this.viewport.deltaPixelsFromPoints(
|
|
|
|
bounds.getSize(),
|
|
|
|
true
|
|
|
|
);
|
|
|
|
overlay.drawHTML( this.container );
|
|
|
|
},
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
addOverlay: function( element, location, placement ) {
|
|
|
|
element = $.getElement( element );
|
|
|
|
|
|
|
|
if ( this._getOverlayIndex( element ) >= 0 ) {
|
|
|
|
// they're trying to add a duplicate overlay
|
|
|
|
return;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
this.overlays.push( new $.Overlay( element, location, placement ) );
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
updateOverlay: function( element, location, placement ) {
|
|
|
|
var i;
|
|
|
|
|
|
|
|
element = $.getElement( element );
|
|
|
|
i = this._getOverlayIndex( element );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( i >= 0 ) {
|
|
|
|
this.overlays[ i ].update( location, placement );
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
removeOverlay: function( element ) {
|
|
|
|
var i;
|
|
|
|
|
|
|
|
element = $.getElement( element );
|
|
|
|
i = this._getOverlayIndex( element );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-19 05:15:54 +04:00
|
|
|
if ( i >= 0 ) {
|
|
|
|
this.overlays[ i ].destroy();
|
|
|
|
this.overlays.splice( i, 1 );
|
2012-01-12 03:22:13 +04:00
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
clearOverlays: function() {
|
2012-01-19 05:15:54 +04:00
|
|
|
while ( this.overlays.length > 0 ) {
|
2012-01-12 03:22:13 +04:00
|
|
|
this.overlays.pop().destroy();
|
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
needsUpdate: function() {
|
2012-01-12 03:22:13 +04:00
|
|
|
return this.updateAgain;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
numTilesLoaded: function() {
|
2012-01-12 03:22:13 +04:00
|
|
|
return this.tilesLoaded.length;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
reset: function() {
|
|
|
|
this._clearTiles();
|
2012-01-12 03:22:13 +04:00
|
|
|
this.lastResetTime = new Date().getTime();
|
|
|
|
this.updateAgain = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
update: function() {
|
2012-01-12 03:22:13 +04:00
|
|
|
//this.profiler.beginUpdate();
|
|
|
|
this.midUpdate = true;
|
2011-12-06 07:50:25 +04:00
|
|
|
this._updateActual();
|
2012-01-12 03:22:13 +04:00
|
|
|
this.midUpdate = false;
|
|
|
|
//this.profiler.endUpdate();
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2011-12-28 03:17:24 +04:00
|
|
|
loadImage: function(src, callback) {
|
|
|
|
var _this = this,
|
|
|
|
loading = false,
|
|
|
|
image,
|
|
|
|
jobid,
|
|
|
|
complete;
|
|
|
|
|
|
|
|
if ( !this.imageLoaderLimit || this.downloading < this.imageLoaderLimit ) {
|
|
|
|
|
|
|
|
this.downloading++;
|
|
|
|
|
|
|
|
image = new Image();
|
|
|
|
|
|
|
|
complete = function( imagesrc ){
|
|
|
|
_this.downloading--;
|
|
|
|
if (typeof ( callback ) == "function") {
|
|
|
|
try {
|
|
|
|
callback( image );
|
|
|
|
} catch ( e ) {
|
|
|
|
$.Debug.error(
|
2012-01-24 07:48:45 +04:00
|
|
|
"%s while executing %s callback: %s",
|
|
|
|
e.name,
|
|
|
|
src,
|
|
|
|
e.message,
|
2011-12-28 03:17:24 +04:00
|
|
|
e
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
image.onload = function(){
|
|
|
|
finishLoadingImage( image, complete, true );
|
|
|
|
};
|
|
|
|
|
|
|
|
image.onabort = image.onerror = function(){
|
|
|
|
finishLoadingImage( image, complete, false );
|
|
|
|
};
|
|
|
|
|
|
|
|
jobid = window.setTimeout( function(){
|
|
|
|
finishLoadingImage( image, complete, false, jobid );
|
|
|
|
}, TIMEOUT );
|
|
|
|
|
|
|
|
loading = true;
|
|
|
|
image.src = src;
|
|
|
|
}
|
|
|
|
|
|
|
|
return loading;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-28 03:17:24 +04:00
|
|
|
function finishLoadingImage( image, callback, successful, jobid ){
|
|
|
|
|
|
|
|
image.onload = null;
|
|
|
|
image.onabort = null;
|
|
|
|
image.onerror = null;
|
|
|
|
|
|
|
|
if ( jobid ) {
|
|
|
|
window.clearTimeout( jobid );
|
|
|
|
}
|
|
|
|
window.setTimeout( function() {
|
|
|
|
callback( image.src, successful ? image : null);
|
|
|
|
}, 1 );
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2012-01-12 03:32:17 +04:00
|
|
|
function numberOfTiles( drawer, level ){
|
|
|
|
|
|
|
|
if ( !drawer.cacheNumTiles[ level ] ) {
|
|
|
|
drawer.cacheNumTiles[ level ] = drawer.source.getNumTiles( level );
|
|
|
|
}
|
|
|
|
|
|
|
|
return drawer.cacheNumTiles[ level ];
|
|
|
|
};
|
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
}( OpenSeadragon ));
|