work-in-progress refactor of Drawer.prototype._updateActual

This commit is contained in:
thatcher 2012-01-18 20:15:54 -05:00
parent b3d1fcdbb4
commit f325804ff6
3 changed files with 762 additions and 528 deletions

View File

@ -314,8 +314,8 @@ OpenSeadragon = window.OpenSeadragon || (function(){
$.extend( $, { $.extend( $, {
getElement: function( element ) { getElement: function( element ) {
if (typeof ( element ) == "string") { if ( typeof ( element ) == "string") {
element = document.getElementById( element ); element = document.getElementById( element );
} }
return element; return element;
@ -330,11 +330,13 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementPosition: function( element ) { getElementPosition: function( element ) {
var element = $.getElement( element ); var result = new $.Point(),
isFixed,
offsetParent;
var isFixed = $.getElementStyle( element ).position == "fixed", element = $.getElement( element );
offsetParent = $.getOffsetParent( element, isFixed ), isFixed = $.getElementStyle( element ).position == "fixed";
result = new $.Point(); offsetParent = $.getOffsetParent( element, isFixed );
while ( offsetParent ) { while ( offsetParent ) {
@ -354,7 +356,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementSize: function( element ) { getElementSize: function( element ) {
var element = $.getElement( element ); element = $.getElement( element );
return new $.Point( return new $.Point(
element.clientWidth, element.clientWidth,
@ -363,7 +365,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementStyle: function( element ) { getElementStyle: function( element ) {
var element = $.getElement( element ); element = $.getElement( element );
if ( element.currentStyle ) { if ( element.currentStyle ) {
return element.currentStyle; return element.currentStyle;
@ -379,15 +381,14 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getMousePosition: function( event ) { getMousePosition: function( event ) {
var event = $.getEvent( event );
var result = new $.Point(); var result = new $.Point();
event = $.getEvent( event );
if ( typeof( event.pageX ) == "number" ) { if ( typeof( event.pageX ) == "number" ) {
result.x = event.pageX; result.x = event.pageX;
result.y = event.pageY; result.y = event.pageY;
} else if ( typeof( event.clientX ) == "number" ) { } else if ( typeof( event.clientX ) == "number" ) {
result.x = result.x =
event.clientX + event.clientX +
document.body.scrollLeft + document.body.scrollLeft +
@ -446,18 +447,19 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
imageFormatSupported: function( extension ) { imageFormatSupported: function( extension ) {
var extension = extension ? extension : ""; extension = extension ? extension : "";
return !!FILEFORMATS[ extension.toLowerCase() ]; return !!FILEFORMATS[ extension.toLowerCase() ];
}, },
makeCenteredNode: function( element ) { makeCenteredNode: function( element ) {
var element = $.getElement( element );
var div = $.makeNeutralElement( "div" ), var div = $.makeNeutralElement( "div" ),
html = [], html = [],
innerDiv, innerDiv,
innerDivs; innerDivs;
element = $.getElement( element );
//TODO: I dont understand the use of # inside the style attributes //TODO: I dont understand the use of # inside the style attributes
// below. Invetigate the results of the constructed html in // below. Invetigate the results of the constructed html in
// the browser and clean up the mark-up to make this clearer. // the browser and clean up the mark-up to make this clearer.
@ -534,12 +536,13 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
setElementOpacity: function( element, opacity, usesAlpha ) { setElementOpacity: function( element, opacity, usesAlpha ) {
var element = $.getElement( element );
var previousFilter, var previousFilter,
ieOpacity, ieOpacity,
ieFilter; ieFilter;
element = $.getElement( element );
if ( usesAlpha && !$.Browser.alpha ) { if ( usesAlpha && !$.Browser.alpha ) {
opacity = Math.round( opacity ); opacity = Math.round( opacity );
} }
@ -573,7 +576,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
addEvent: function( element, eventName, handler, useCapture ) { addEvent: function( element, eventName, handler, useCapture ) {
var element = $.getElement( element ); element = $.getElement( element );
//TODO: Why do this if/else on every method call instead of just //TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic // defining this function once based on the same logic
@ -592,7 +595,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
removeEvent: function( element, eventName, handler, useCapture ) { removeEvent: function( element, eventName, handler, useCapture ) {
var element = $.getElement( element ); element = $.getElement( element );
//TODO: Why do this if/else on every method call instead of just //TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic // defining this function once based on the same logic
@ -611,7 +614,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
cancelEvent: function( event ) { cancelEvent: function( event ) {
var event = $.getEvent( event ); event = $.getEvent( event );
if ( event.preventDefault ) { if ( event.preventDefault ) {
event.preventDefault(); // W3C for preventing default event.preventDefault(); // W3C for preventing default
@ -622,7 +625,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
stopEvent: function( event ) { stopEvent: function( event ) {
var event = $.getEvent( event ); event = $.getEvent( event );
if ( event.stopPropagation ) { if ( event.stopPropagation ) {
event.stopPropagation(); // W3C for stopping propagation event.stopPropagation(); // W3C for stopping propagation
@ -3498,49 +3501,62 @@ $.Drawer = function(source, viewport, elmt) {
$.Drawer.prototype = { $.Drawer.prototype = {
getPixelRatio: function(level) { getPixelRatio: function( level ) {
if (!this.cachePixelRatios[level]) { if ( !this.cachePixelRatios[ level ] ) {
this.cachePixelRatios[level] = this.source.getPixelRatio(level); this.cachePixelRatios[ level ] = this.source.getPixelRatio( level );
} }
return this.cachePixelRatios[level]; return this.cachePixelRatios[ level ];
}, },
_getTile: function(level, x, y, time, numTilesX, numTilesY) { _getTile: function( level, x, y, time, numTilesX, numTilesY ) {
if (!this.tilesMatrix[level]) { var xMod,
this.tilesMatrix[level] = {}; yMod,
bounds,
exists,
url,
tile;
if ( !this.tilesMatrix[ level ] ) {
this.tilesMatrix[ level ] = {};
} }
if (!this.tilesMatrix[level][x]) { if ( !this.tilesMatrix[ level ][ x ] ) {
this.tilesMatrix[level][x] = {}; this.tilesMatrix[ level ][ x ] = {};
} }
if (!this.tilesMatrix[level][x][y]) { if ( !this.tilesMatrix[ level ][ x ][ y ] ) {
var xMod = (numTilesX + (x % numTilesX)) % numTilesX; xMod = ( numTilesX + ( x % numTilesX ) ) % numTilesX;
var yMod = (numTilesY + (y % numTilesY)) % numTilesY; yMod = ( numTilesY + ( y % numTilesY ) ) % numTilesY;
var bounds = this.source.getTileBounds(level, xMod, yMod); bounds = this.source.getTileBounds( level, xMod, yMod );
var exists = this.source.tileExists(level, xMod, yMod); exists = this.source.tileExists( level, xMod, yMod );
var url = this.source.getTileUrl(level, xMod, yMod); url = this.source.getTileUrl( level, xMod, yMod );
bounds.x += 1.0 * (x - xMod) / numTilesX; bounds.x += 1.0 * ( x - xMod ) / numTilesX;
bounds.y += this.normHeight * (y - yMod) / numTilesY; bounds.y += this.normHeight * ( y - yMod ) / numTilesY;
this.tilesMatrix[level][x][y] = new $.Tile(level, x, y, bounds, exists, url); this.tilesMatrix[ level ][ x ][ y ] = new $.Tile(
level,
x,
y,
bounds,
exists,
url
);
} }
var tile = this.tilesMatrix[level][x][y]; tile = this.tilesMatrix[ level ][ x ][ y ];
tile.lastTouchTime = time; tile.lastTouchTime = time;
return tile; return tile;
}, },
_loadTile: function(tile, time) { _loadTile: function( tile, time ) {
tile.loading = this.loadImage( tile.loading = this.loadImage(
tile.url, tile.url,
$.createCallback( $.createCallback(
null, null,
$.delegate(this, this._onTileLoad), $.delegate( this, this._onTileLoad ),
tile, tile,
time time
) )
@ -3548,61 +3564,72 @@ $.Drawer.prototype = {
}, },
_onTileLoad: function(tile, time, image) { _onTileLoad: function(tile, time, image) {
var insertionIndex,
cutoff,
worstTile,
worstTime,
worstLevel,
worstTileIndex,
prevTile,
prevTime,
prevLevel,
i;
tile.loading = false; tile.loading = false;
if (this.midUpdate) { if ( this.midUpdate ) {
$.Debug.error("Tile load callback in middle of drawing routine."); $.Debug.error( "Tile load callback in middle of drawing routine." );
return; return;
} else if (!image) { } else if ( !image ) {
$.Debug.log("Tile " + tile + " failed to load: " + tile.url); $.Debug.log( "Tile " + tile + " failed to load: " + tile.url );
tile.exists = false; tile.exists = false;
return; return;
} else if (time < this.lastResetTime) { } else if ( time < this.lastResetTime ) {
$.Debug.log("Ignoring tile " + tile + " loaded before reset: " + tile.url); $.Debug.log( "Ignoring tile " + tile + " loaded before reset: " + tile.url );
return; return;
} }
tile.loaded = true; tile.loaded = true;
tile.image = image; tile.image = image;
var insertionIndex = this.tilesLoaded.length; insertionIndex = this.tilesLoaded.length;
if (this.tilesLoaded.length >= QUOTA) { if ( this.tilesLoaded.length >= QUOTA ) {
var cutoff = Math.ceil(Math.log(this.tileSize) / Math.log(2)); cutoff = Math.ceil( Math.log( this.tileSize ) / Math.log( 2 ) );
var worstTile = null; worstTile = null;
var worstTileIndex = -1; worstTileIndex = -1;
for (var i = this.tilesLoaded.length - 1; i >= 0; i--) { for ( i = this.tilesLoaded.length - 1; i >= 0; i-- ) {
var prevTile = this.tilesLoaded[i]; prevTile = this.tilesLoaded[ i ];
if (prevTile.level <= this.cutoff || prevTile.beingDrawn) { if ( prevTile.level <= this.cutoff || prevTile.beingDrawn ) {
continue; continue;
} else if (!worstTile) { } else if ( !worstTile ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
continue; continue;
} }
var prevTime = prevTile.lastTouchTime; prevTime = prevTile.lastTouchTime;
var worstTime = worstTile.lastTouchTime; worstTime = worstTile.lastTouchTime;
var prevLevel = prevTile.level; prevLevel = prevTile.level;
var worstLevel = worstTile.level; worstLevel = worstTile.level;
if (prevTime < worstTime || if ( prevTime < worstTime ||
(prevTime == worstTime && prevLevel > worstLevel)) { ( prevTime == worstTime && prevLevel > worstLevel ) ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
} }
} }
if (worstTile && worstTileIndex >= 0) { if ( worstTile && worstTileIndex >= 0 ) {
worstTile.unload(); worstTile.unload();
insertionIndex = worstTileIndex; insertionIndex = worstTileIndex;
} }
} }
this.tilesLoaded[insertionIndex] = tile; this.tilesLoaded[ insertionIndex ] = tile;
this.updateAgain = true; this.updateAgain = true;
}, },
@ -3622,18 +3649,22 @@ $.Drawer.prototype = {
* there's no content that they would need to cover. Tiles at non-existent * there's no content that they would need to cover. Tiles at non-existent
* levels that are within the image bounds, however, do not. * levels that are within the image bounds, however, do not.
*/ */
_providesCoverage: function(level, x, y) { _providesCoverage: function( level, x, y ) {
if (!this.coverage[level]) { var rows,
cols,
i, j;
if ( !this.coverage[ level ] ) {
return false; return false;
} }
if (x === undefined || y === undefined) { if ( x === undefined || y === undefined ) {
var rows = this.coverage[level]; rows = this.coverage[ level ];
for (var i in rows) { for ( i in rows ) {
if (rows.hasOwnProperty(i)) { if ( rows.hasOwnProperty( i ) ) {
var cols = rows[i]; cols = rows[ i ];
for (var j in cols) { for ( j in cols ) {
if (cols.hasOwnProperty(j) && !cols[j]) { if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
return false; return false;
} }
} }
@ -3643,9 +3674,11 @@ $.Drawer.prototype = {
return true; return true;
} }
return (this.coverage[level][x] === undefined || return (
this.coverage[level][x][y] === undefined || this.coverage[ level ][ x] === undefined ||
this.coverage[level][x][y] === true); this.coverage[ level ][ x ][ y ] === undefined ||
this.coverage[ level ][ x ][ y ] === true
);
}, },
/** /**
@ -3653,32 +3686,35 @@ $.Drawer.prototype = {
* tiles of higher resolution representing the same content. If neither x * tiles of higher resolution representing the same content. If neither x
* nor y is given, returns true if the entire visible level is covered. * nor y is given, returns true if the entire visible level is covered.
*/ */
_isCovered: function(level, x, y) { _isCovered: function( level, x, y ) {
if (x === undefined || y === undefined) { if ( x === undefined || y === undefined ) {
return this._providesCoverage(level + 1); return this._providesCoverage( level + 1 );
} else { } else {
return (this._providesCoverage(level + 1, 2 * x, 2 * y) && return (
this._providesCoverage(level + 1, 2 * x, 2 * y + 1) && this._providesCoverage( level + 1, 2 * x, 2 * y ) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y) && this._providesCoverage( level + 1, 2 * x, 2 * y + 1 ) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y + 1)); this._providesCoverage( level + 1, 2 * x + 1, 2 * y ) &&
this._providesCoverage( level + 1, 2 * x + 1, 2 * y + 1 )
);
} }
}, },
/** /**
* Sets whether the given tile provides coverage or not. * Sets whether the given tile provides coverage or not.
*/ */
_setCoverage: function(level, x, y, covers) { _setCoverage: function( level, x, y, covers ) {
if (!this.coverage[level]) { if ( !this.coverage[ level ] ) {
$.Debug.error("Setting coverage for a tile before its " + $.Debug.error(
"level's coverage has been reset: " + level); "Setting coverage for a tile before its level's coverage has been reset: " + level
);
return; return;
} }
if (!this.coverage[level][x]) { if ( !this.coverage[ level ][ x ] ) {
this.coverage[level][x] = {}; this.coverage[ level ][ x ] = {};
} }
this.coverage[level][x][y] = covers; this.coverage[ level ][ x ][ y ] = covers;
}, },
/** /**
@ -3686,20 +3722,20 @@ $.Drawer.prototype = {
* after every draw routine. Note that at the beginning of the next draw * after every draw routine. Note that at the beginning of the next draw
* routine, coverage for every visible tile should be explicitly set. * routine, coverage for every visible tile should be explicitly set.
*/ */
_resetCoverage: function(level) { _resetCoverage: function( level ) {
this.coverage[level] = {}; this.coverage[ level ] = {};
}, },
_compareTiles: function(prevBest, tile) { _compareTiles: function( prevBest, tile ) {
if (!prevBest) { if ( !prevBest ) {
return tile; return tile;
} }
if (tile.visibility > prevBest.visibility) { if ( tile.visibility > prevBest.visibility ) {
return tile; return tile;
} else if (tile.visibility == prevBest.visibility) { } else if ( tile.visibility == prevBest.visibility ) {
if (tile.distance < prevBest.distance) { if ( tile.distance < prevBest.distance ) {
return tile; return tile;
} }
} }
@ -3708,9 +3744,10 @@ $.Drawer.prototype = {
}, },
_getOverlayIndex: function(elmt) { _getOverlayIndex: function( elmt ) {
for (var i = this.overlays.length - 1; i >= 0; i--) { var i;
if (this.overlays[i].elmt == elmt) { for ( i = this.overlays.length - 1; i >= 0; i-- ) {
if ( this.overlays[ i ].elmt == elmt ) {
return i; return i;
} }
} }
@ -3722,248 +3759,328 @@ $.Drawer.prototype = {
_updateActual: function() { _updateActual: function() {
this.updateAgain = false; this.updateAgain = false;
var _canvas = this.canvas; var i, x, y,
var _context = this.context; tile,
var _container = this.container; tileTL,
var _lastDrawn = this.lastDrawn; tileBR,
numTiles,
numTilesX,
numTilesY,
level,
drawLevel,
drawTile,
renderPixelRatioC,
renderPixelRatioT,
levelOpacity,
levelVisibility,
viewportSize = this.viewport.getContainerSize(),
viewportWidth = viewportSize.x,
viewportHeight = viewportSize.y,
viewportBounds = this.viewport.getBounds( true ),
viewportTL = viewportBounds.getTopLeft(),
viewportBR = viewportBounds.getBottomRight(),
viewportCenter = this.viewport.pixelFromPoint( this.viewport.getCenter() ),
best = null,
haveDrawn = false,
currentTime = new Date().getTime(),
zeroRatioT = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( 0 ),
false
).x,
zeroRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( 0 ),
true
).x,
optimalRatio = this.config.immediateRender ? 1 : zeroRatioT,
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 )
)
);
while (_lastDrawn.length > 0) {
var tile = _lastDrawn.pop(); while ( this.lastDrawn.length > 0 ) {
tile = this.lastDrawn.pop();
tile.beingDrawn = false; tile.beingDrawn = false;
} }
var viewportSize = this.viewport.getContainerSize();
var viewportWidth = viewportSize.x;
var viewportHeight = viewportSize.y;
_canvas.innerHTML = ""; this.canvas.innerHTML = "";
if ( USE_CANVAS ) { if ( USE_CANVAS ) {
_canvas.width = viewportWidth; this.canvas.width = viewportWidth;
_canvas.height = viewportHeight; this.canvas.height = viewportHeight;
_context.clearRect(0, 0, viewportWidth, viewportHeight); this.context.clearRect( 0, 0, viewportWidth, viewportHeight );
} }
var viewportBounds = this.viewport.getBounds(true); if ( !this.config.wrapHorizontal &&
var viewportTL = viewportBounds.getTopLeft(); ( viewportBR.x < 0 || viewportTL.x > 1 ) ) {
var viewportBR = viewportBounds.getBottomRight();
if (!this.config.wrapHorizontal &&
(viewportBR.x < 0 || viewportTL.x > 1)) {
return; return;
} else if (!this.config.wrapVertical && } else if ( !this.config.wrapVertical &&
(viewportBR.y < 0 || viewportTL.y > this.normHeight)) { ( viewportBR.y < 0 || viewportTL.y > this.normHeight ) ) {
return; return;
} }
if ( !this.config.wrapHorizontal ) {
viewportTL.x = Math.max( viewportTL.x, 0 );
var _abs = Math.abs; viewportBR.x = Math.min( viewportBR.x, 1 );
var _ceil = Math.ceil;
var _floor = Math.floor;
var _log = Math.log;
var _max = Math.max;
var _min = Math.min;
var alwaysBlend = this.config.alwaysBlend;
var blendTimeMillis = 1000 * this.config.blendTime;
var immediateRender = this.config.immediateRender;
var wrapHorizontal = this.config.wrapHorizontal;
var wrapVertical = this.config.wrapVertical;
if (!wrapHorizontal) {
viewportTL.x = _max(viewportTL.x, 0);
viewportBR.x = _min(viewportBR.x, 1);
} }
if (!wrapVertical) { if ( !this.config.wrapVertical ) {
viewportTL.y = _max(viewportTL.y, 0); viewportTL.y = Math.max( viewportTL.y, 0 );
viewportBR.y = _min(viewportBR.y, this.normHeight); viewportBR.y = Math.min( viewportBR.y, this.normHeight );
} }
var best = null; lowestLevel = Math.min( lowestLevel, highestLevel );
var haveDrawn = false;
var currentTime = new Date().getTime();
var viewportCenter = this.viewport.pixelFromPoint(this.viewport.getCenter()); for ( level = highestLevel; level >= lowestLevel; level-- ) {
var zeroRatioT = this.viewport.deltaPixelsFromPoints(this.source.getPixelRatio(0), false).x; drawLevel = false;
var optimalPixelRatio = immediateRender ? 1 : zeroRatioT; // note the .x!
renderPixelRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( level ),
true
).x;
renderPixelRatioT = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( level ),
false
).x;
var lowestLevel = _max(this.minLevel, _floor(_log(this.config.minZoomImageRatio) / _log(2))); if ( ( !haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO ) ||
var zeroRatioC = this.viewport.deltaPixelsFromPoints(this.source.getPixelRatio(0), true).x; ( level == lowestLevel ) ) {
var highestLevel = _min(this.maxLevel,
_floor(_log(zeroRatioC / MIN_PIXEL_RATIO) / _log(2)));
lowestLevel = _min(lowestLevel, highestLevel);
for (var level = highestLevel; level >= lowestLevel; level--) {
var drawLevel = false;
var renderPixelRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio(level), true).x; // note the .x!
if ((!haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO) ||
level == lowestLevel) {
drawLevel = true; drawLevel = true;
haveDrawn = true; haveDrawn = true;
} else if (!haveDrawn) { } else if ( !haveDrawn ) {
continue; continue;
} }
this._resetCoverage(level); this._resetCoverage( level );
var levelOpacity = _min(1, (renderPixelRatioC - 0.5) / 0.5); levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 );
var renderPixelRatioT = this.viewport.deltaPixelsFromPoints( levelVisibility = optimalRatio / Math.abs(
this.source.getPixelRatio(level), false).x; optimalRatio - renderPixelRatioT
var levelVisibility = optimalPixelRatio / );
_abs(optimalPixelRatio - renderPixelRatioT);
var tileTL = this.source.getTileAtPoint(level, viewportTL); tileTL = this.source.getTileAtPoint( level, viewportTL );
var tileBR = this.source.getTileAtPoint(level, viewportBR); tileBR = this.source.getTileAtPoint( level, viewportBR );
var numTiles = numberOfTiles( this, level ); numTiles = numberOfTiles( this, level );
var numTilesX = numTiles.x; numTilesX = numTiles.x;
var numTilesY = numTiles.y; numTilesY = numTiles.y;
if (!wrapHorizontal) {
tileBR.x = _min(tileBR.x, numTilesX - 1); if ( !this.config.wrapHorizontal ) {
tileBR.x = Math.min( tileBR.x, numTilesX - 1 );
} }
if (!wrapVertical) { if ( !this.config.wrapVertical ) {
tileBR.y = _min(tileBR.y, numTilesY - 1); tileBR.y = Math.min( tileBR.y, numTilesY - 1 );
} }
for (var x = tileTL.x; x <= tileBR.x; x++) { for ( x = tileTL.x; x <= tileBR.x; x++ ) {
for (var y = tileTL.y; y <= tileBR.y; y++) { for ( y = tileTL.y; y <= tileBR.y; y++ ) {
var tile = this._getTile(level, x, y, currentTime, numTilesX, numTilesY); drawTile = drawLevel;
var drawTile = drawLevel; tile = this._getTile(
level,
x, y,
currentTime,
numTilesX,
numTilesY
);
this._setCoverage(level, x, y, false); this._setCoverage( level, x, y, false );
if (!tile.exists) { if ( !tile.exists ) {
continue; continue;
} }
if (haveDrawn && !drawTile) { if ( haveDrawn && !drawTile ) {
if (this._isCovered(level, x, y)) { if ( this._isCovered( level, x, y ) ) {
this._setCoverage(level, x, y, true); this._setCoverage( level, x, y, true );
} else { } else {
drawTile = true; drawTile = true;
} }
} }
if (!drawTile) { if ( !drawTile ) {
continue; continue;
} }
var boundsTL = tile.bounds.getTopLeft(); this._positionTile(
var boundsSize = tile.bounds.getSize(); tile,
var positionC = this.viewport.pixelFromPoint(boundsTL, true); viewportCenter,
var sizeC = this.viewport.deltaPixelsFromPoints(boundsSize, true); levelVisibility
);
if (!this.tileOverlap) { if ( tile.loaded ) {
sizeC = sizeC.plus(new $.Point(1, 1));
}
var positionT = this.viewport.pixelFromPoint(boundsTL, false);
var sizeT = this.viewport.deltaPixelsFromPoints(boundsSize, false);
var tileCenter = positionT.plus(sizeT.divide(2));
var tileDistance = viewportCenter.distanceTo(tileCenter);
tile.position = positionC;
tile.size = sizeC;
tile.distance = tileDistance;
tile.visibility = levelVisibility;
if (tile.loaded) {
if (!tile.blendStart) {
tile.blendStart = currentTime;
}
var deltaTime = currentTime - tile.blendStart;
var opacity = _min(1, deltaTime / blendTimeMillis);
if (alwaysBlend) { updateAgain = this._blendTile(
opacity *= levelOpacity; tile,
} x, y,
level,
levelOpacity,
currentTime
);
tile.opacity = opacity; } else if ( tile.Loading ) {
//do nothing
_lastDrawn.push(tile);
if (opacity == 1) {
this._setCoverage(level, x, y, true);
} else if (deltaTime < blendTimeMillis) {
updateAgain = true;
}
} else if (tile.Loading) {
} else { } else {
best = this._compareTiles(best, tile); best = this._compareTiles( best, tile );
} }
} }
} }
if (this._providesCoverage(level)) { if ( this._providesCoverage( level ) ) {
break; break;
} }
} }
for (var i = _lastDrawn.length - 1; i >= 0; i--) { this._drawTiles();
var tile = _lastDrawn[i]; this._drawOverlays();
if ( best ) {
this._loadTile( best, currentTime );
// because we haven't finished drawing, so
this.updateAgain = true;
}
},
_drawLevel: function( ){
},
_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 ) );
}
tile.position = positionC;
tile.size = sizeC;
tile.distance = tileDistance;
tile.visibility = levelVisibility;
},
_blendTile: function( tile, x, y, level, levelOpacity, currentTime ){
var blendTimeMillis = 1000 * this.config.blendTime,
deltaTime,
opacity;
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;
}
return false;
},
_drawTiles: function(){
var i,
tile;
for ( i = this.lastDrawn.length - 1; i >= 0; i-- ) {
tile = this.lastDrawn[ i ];
if ( USE_CANVAS ) { if ( USE_CANVAS ) {
tile.drawCanvas(_context); tile.drawCanvas( this.context );
} else { } else {
tile.drawHTML(_canvas); tile.drawHTML( this.canvas );
} }
tile.beingDrawn = true; tile.beingDrawn = true;
} }
},
var numOverlays = this.overlays.length; _drawOverlays: function(){
for (var i = 0; i < numOverlays; i++) { var i,
var overlay = this.overlays[i]; length = this.overlays.length;
var bounds = overlay.bounds; for ( i = 0; i < length; i++ ) {
this._drawOverlay( this.overlays[ i ] );
overlay.position = this.viewport.pixelFromPoint(bounds.getTopLeft(), true);
overlay.size = this.viewport.deltaPixelsFromPoints(bounds.getSize(), true);
overlay.drawHTML(_container);
}
if (best) {
this._loadTile(best, currentTime);
this.updateAgain = true; // because we haven't finished drawing, so
} }
}, },
_drawOverlay: function( overlay ){
var bounds = overlay.bounds;
addOverlay: function(elmt, loc, placement) { overlay.position = this.viewport.pixelFromPoint(
var elmt = $.getElement(elmt); bounds.getTopLeft(),
true
);
overlay.size = this.viewport.deltaPixelsFromPoints(
bounds.getSize(),
true
);
overlay.drawHTML( this.container );
},
if (this._getOverlayIndex(elmt) >= 0) { addOverlay: function( element, location, placement ) {
return; // they're trying to add a duplicate overlay element = $.getElement( element );
if ( this._getOverlayIndex( element ) >= 0 ) {
// they're trying to add a duplicate overlay
return;
} }
this.overlays.push(new $.Overlay(elmt, loc, placement)); this.overlays.push( new $.Overlay( element, location, placement ) );
this.updateAgain = true; this.updateAgain = true;
}, },
updateOverlay: function(elmt, loc, placement) { updateOverlay: function( element, location, placement ) {
var elmt = $.getElement(elmt); var i;
var i = this._getOverlayIndex(elmt);
if (i >= 0) { element = $.getElement( element );
this.overlays[i].update(loc, placement); i = this._getOverlayIndex( element );
if ( i >= 0 ) {
this.overlays[ i ].update( location, placement );
this.updateAgain = true; this.updateAgain = true;
} }
}, },
removeOverlay: function(elmt) { removeOverlay: function( element ) {
var elmt = $.getElement(elmt); var i;
var i = this._getOverlayIndex(elmt);
if (i >= 0) { element = $.getElement( element );
this.overlays[i].destroy(); i = this._getOverlayIndex( element );
this.overlays.splice(i, 1);
if ( i >= 0 ) {
this.overlays[ i ].destroy();
this.overlays.splice( i, 1 );
this.updateAgain = true; this.updateAgain = true;
} }
}, },
clearOverlays: function() { clearOverlays: function() {
while (this.overlays.length > 0) { while ( this.overlays.length > 0 ) {
this.overlays.pop().destroy(); this.overlays.pop().destroy();
this.updateAgain = true; this.updateAgain = true;
} }

View File

@ -74,49 +74,62 @@ $.Drawer = function(source, viewport, elmt) {
$.Drawer.prototype = { $.Drawer.prototype = {
getPixelRatio: function(level) { getPixelRatio: function( level ) {
if (!this.cachePixelRatios[level]) { if ( !this.cachePixelRatios[ level ] ) {
this.cachePixelRatios[level] = this.source.getPixelRatio(level); this.cachePixelRatios[ level ] = this.source.getPixelRatio( level );
} }
return this.cachePixelRatios[level]; return this.cachePixelRatios[ level ];
}, },
_getTile: function(level, x, y, time, numTilesX, numTilesY) { _getTile: function( level, x, y, time, numTilesX, numTilesY ) {
if (!this.tilesMatrix[level]) { var xMod,
this.tilesMatrix[level] = {}; yMod,
bounds,
exists,
url,
tile;
if ( !this.tilesMatrix[ level ] ) {
this.tilesMatrix[ level ] = {};
} }
if (!this.tilesMatrix[level][x]) { if ( !this.tilesMatrix[ level ][ x ] ) {
this.tilesMatrix[level][x] = {}; this.tilesMatrix[ level ][ x ] = {};
} }
if (!this.tilesMatrix[level][x][y]) { if ( !this.tilesMatrix[ level ][ x ][ y ] ) {
var xMod = (numTilesX + (x % numTilesX)) % numTilesX; xMod = ( numTilesX + ( x % numTilesX ) ) % numTilesX;
var yMod = (numTilesY + (y % numTilesY)) % numTilesY; yMod = ( numTilesY + ( y % numTilesY ) ) % numTilesY;
var bounds = this.source.getTileBounds(level, xMod, yMod); bounds = this.source.getTileBounds( level, xMod, yMod );
var exists = this.source.tileExists(level, xMod, yMod); exists = this.source.tileExists( level, xMod, yMod );
var url = this.source.getTileUrl(level, xMod, yMod); url = this.source.getTileUrl( level, xMod, yMod );
bounds.x += 1.0 * (x - xMod) / numTilesX; bounds.x += 1.0 * ( x - xMod ) / numTilesX;
bounds.y += this.normHeight * (y - yMod) / numTilesY; bounds.y += this.normHeight * ( y - yMod ) / numTilesY;
this.tilesMatrix[level][x][y] = new $.Tile(level, x, y, bounds, exists, url); this.tilesMatrix[ level ][ x ][ y ] = new $.Tile(
level,
x,
y,
bounds,
exists,
url
);
} }
var tile = this.tilesMatrix[level][x][y]; tile = this.tilesMatrix[ level ][ x ][ y ];
tile.lastTouchTime = time; tile.lastTouchTime = time;
return tile; return tile;
}, },
_loadTile: function(tile, time) { _loadTile: function( tile, time ) {
tile.loading = this.loadImage( tile.loading = this.loadImage(
tile.url, tile.url,
$.createCallback( $.createCallback(
null, null,
$.delegate(this, this._onTileLoad), $.delegate( this, this._onTileLoad ),
tile, tile,
time time
) )
@ -124,61 +137,72 @@ $.Drawer.prototype = {
}, },
_onTileLoad: function(tile, time, image) { _onTileLoad: function(tile, time, image) {
var insertionIndex,
cutoff,
worstTile,
worstTime,
worstLevel,
worstTileIndex,
prevTile,
prevTime,
prevLevel,
i;
tile.loading = false; tile.loading = false;
if (this.midUpdate) { if ( this.midUpdate ) {
$.Debug.error("Tile load callback in middle of drawing routine."); $.Debug.error( "Tile load callback in middle of drawing routine." );
return; return;
} else if (!image) { } else if ( !image ) {
$.Debug.log("Tile " + tile + " failed to load: " + tile.url); $.Debug.log( "Tile " + tile + " failed to load: " + tile.url );
tile.exists = false; tile.exists = false;
return; return;
} else if (time < this.lastResetTime) { } else if ( time < this.lastResetTime ) {
$.Debug.log("Ignoring tile " + tile + " loaded before reset: " + tile.url); $.Debug.log( "Ignoring tile " + tile + " loaded before reset: " + tile.url );
return; return;
} }
tile.loaded = true; tile.loaded = true;
tile.image = image; tile.image = image;
var insertionIndex = this.tilesLoaded.length; insertionIndex = this.tilesLoaded.length;
if (this.tilesLoaded.length >= QUOTA) { if ( this.tilesLoaded.length >= QUOTA ) {
var cutoff = Math.ceil(Math.log(this.tileSize) / Math.log(2)); cutoff = Math.ceil( Math.log( this.tileSize ) / Math.log( 2 ) );
var worstTile = null; worstTile = null;
var worstTileIndex = -1; worstTileIndex = -1;
for (var i = this.tilesLoaded.length - 1; i >= 0; i--) { for ( i = this.tilesLoaded.length - 1; i >= 0; i-- ) {
var prevTile = this.tilesLoaded[i]; prevTile = this.tilesLoaded[ i ];
if (prevTile.level <= this.cutoff || prevTile.beingDrawn) { if ( prevTile.level <= this.cutoff || prevTile.beingDrawn ) {
continue; continue;
} else if (!worstTile) { } else if ( !worstTile ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
continue; continue;
} }
var prevTime = prevTile.lastTouchTime; prevTime = prevTile.lastTouchTime;
var worstTime = worstTile.lastTouchTime; worstTime = worstTile.lastTouchTime;
var prevLevel = prevTile.level; prevLevel = prevTile.level;
var worstLevel = worstTile.level; worstLevel = worstTile.level;
if (prevTime < worstTime || if ( prevTime < worstTime ||
(prevTime == worstTime && prevLevel > worstLevel)) { ( prevTime == worstTime && prevLevel > worstLevel ) ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
} }
} }
if (worstTile && worstTileIndex >= 0) { if ( worstTile && worstTileIndex >= 0 ) {
worstTile.unload(); worstTile.unload();
insertionIndex = worstTileIndex; insertionIndex = worstTileIndex;
} }
} }
this.tilesLoaded[insertionIndex] = tile; this.tilesLoaded[ insertionIndex ] = tile;
this.updateAgain = true; this.updateAgain = true;
}, },
@ -198,18 +222,22 @@ $.Drawer.prototype = {
* there's no content that they would need to cover. Tiles at non-existent * there's no content that they would need to cover. Tiles at non-existent
* levels that are within the image bounds, however, do not. * levels that are within the image bounds, however, do not.
*/ */
_providesCoverage: function(level, x, y) { _providesCoverage: function( level, x, y ) {
if (!this.coverage[level]) { var rows,
cols,
i, j;
if ( !this.coverage[ level ] ) {
return false; return false;
} }
if (x === undefined || y === undefined) { if ( x === undefined || y === undefined ) {
var rows = this.coverage[level]; rows = this.coverage[ level ];
for (var i in rows) { for ( i in rows ) {
if (rows.hasOwnProperty(i)) { if ( rows.hasOwnProperty( i ) ) {
var cols = rows[i]; cols = rows[ i ];
for (var j in cols) { for ( j in cols ) {
if (cols.hasOwnProperty(j) && !cols[j]) { if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
return false; return false;
} }
} }
@ -219,9 +247,11 @@ $.Drawer.prototype = {
return true; return true;
} }
return (this.coverage[level][x] === undefined || return (
this.coverage[level][x][y] === undefined || this.coverage[ level ][ x] === undefined ||
this.coverage[level][x][y] === true); this.coverage[ level ][ x ][ y ] === undefined ||
this.coverage[ level ][ x ][ y ] === true
);
}, },
/** /**
@ -229,32 +259,35 @@ $.Drawer.prototype = {
* tiles of higher resolution representing the same content. If neither x * tiles of higher resolution representing the same content. If neither x
* nor y is given, returns true if the entire visible level is covered. * nor y is given, returns true if the entire visible level is covered.
*/ */
_isCovered: function(level, x, y) { _isCovered: function( level, x, y ) {
if (x === undefined || y === undefined) { if ( x === undefined || y === undefined ) {
return this._providesCoverage(level + 1); return this._providesCoverage( level + 1 );
} else { } else {
return (this._providesCoverage(level + 1, 2 * x, 2 * y) && return (
this._providesCoverage(level + 1, 2 * x, 2 * y + 1) && this._providesCoverage( level + 1, 2 * x, 2 * y ) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y) && this._providesCoverage( level + 1, 2 * x, 2 * y + 1 ) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y + 1)); this._providesCoverage( level + 1, 2 * x + 1, 2 * y ) &&
this._providesCoverage( level + 1, 2 * x + 1, 2 * y + 1 )
);
} }
}, },
/** /**
* Sets whether the given tile provides coverage or not. * Sets whether the given tile provides coverage or not.
*/ */
_setCoverage: function(level, x, y, covers) { _setCoverage: function( level, x, y, covers ) {
if (!this.coverage[level]) { if ( !this.coverage[ level ] ) {
$.Debug.error("Setting coverage for a tile before its " + $.Debug.error(
"level's coverage has been reset: " + level); "Setting coverage for a tile before its level's coverage has been reset: " + level
);
return; return;
} }
if (!this.coverage[level][x]) { if ( !this.coverage[ level ][ x ] ) {
this.coverage[level][x] = {}; this.coverage[ level ][ x ] = {};
} }
this.coverage[level][x][y] = covers; this.coverage[ level ][ x ][ y ] = covers;
}, },
/** /**
@ -262,20 +295,20 @@ $.Drawer.prototype = {
* after every draw routine. Note that at the beginning of the next draw * after every draw routine. Note that at the beginning of the next draw
* routine, coverage for every visible tile should be explicitly set. * routine, coverage for every visible tile should be explicitly set.
*/ */
_resetCoverage: function(level) { _resetCoverage: function( level ) {
this.coverage[level] = {}; this.coverage[ level ] = {};
}, },
_compareTiles: function(prevBest, tile) { _compareTiles: function( prevBest, tile ) {
if (!prevBest) { if ( !prevBest ) {
return tile; return tile;
} }
if (tile.visibility > prevBest.visibility) { if ( tile.visibility > prevBest.visibility ) {
return tile; return tile;
} else if (tile.visibility == prevBest.visibility) { } else if ( tile.visibility == prevBest.visibility ) {
if (tile.distance < prevBest.distance) { if ( tile.distance < prevBest.distance ) {
return tile; return tile;
} }
} }
@ -284,9 +317,10 @@ $.Drawer.prototype = {
}, },
_getOverlayIndex: function(elmt) { _getOverlayIndex: function( elmt ) {
for (var i = this.overlays.length - 1; i >= 0; i--) { var i;
if (this.overlays[i].elmt == elmt) { for ( i = this.overlays.length - 1; i >= 0; i-- ) {
if ( this.overlays[ i ].elmt == elmt ) {
return i; return i;
} }
} }
@ -298,248 +332,328 @@ $.Drawer.prototype = {
_updateActual: function() { _updateActual: function() {
this.updateAgain = false; this.updateAgain = false;
var _canvas = this.canvas; var i, x, y,
var _context = this.context; tile,
var _container = this.container; tileTL,
var _lastDrawn = this.lastDrawn; tileBR,
numTiles,
numTilesX,
numTilesY,
level,
drawLevel,
drawTile,
renderPixelRatioC,
renderPixelRatioT,
levelOpacity,
levelVisibility,
viewportSize = this.viewport.getContainerSize(),
viewportWidth = viewportSize.x,
viewportHeight = viewportSize.y,
viewportBounds = this.viewport.getBounds( true ),
viewportTL = viewportBounds.getTopLeft(),
viewportBR = viewportBounds.getBottomRight(),
viewportCenter = this.viewport.pixelFromPoint( this.viewport.getCenter() ),
best = null,
haveDrawn = false,
currentTime = new Date().getTime(),
zeroRatioT = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( 0 ),
false
).x,
zeroRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( 0 ),
true
).x,
optimalRatio = this.config.immediateRender ? 1 : zeroRatioT,
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 )
)
);
while (_lastDrawn.length > 0) {
var tile = _lastDrawn.pop(); while ( this.lastDrawn.length > 0 ) {
tile = this.lastDrawn.pop();
tile.beingDrawn = false; tile.beingDrawn = false;
} }
var viewportSize = this.viewport.getContainerSize();
var viewportWidth = viewportSize.x;
var viewportHeight = viewportSize.y;
_canvas.innerHTML = ""; this.canvas.innerHTML = "";
if ( USE_CANVAS ) { if ( USE_CANVAS ) {
_canvas.width = viewportWidth; this.canvas.width = viewportWidth;
_canvas.height = viewportHeight; this.canvas.height = viewportHeight;
_context.clearRect(0, 0, viewportWidth, viewportHeight); this.context.clearRect( 0, 0, viewportWidth, viewportHeight );
} }
var viewportBounds = this.viewport.getBounds(true); if ( !this.config.wrapHorizontal &&
var viewportTL = viewportBounds.getTopLeft(); ( viewportBR.x < 0 || viewportTL.x > 1 ) ) {
var viewportBR = viewportBounds.getBottomRight();
if (!this.config.wrapHorizontal &&
(viewportBR.x < 0 || viewportTL.x > 1)) {
return; return;
} else if (!this.config.wrapVertical && } else if ( !this.config.wrapVertical &&
(viewportBR.y < 0 || viewportTL.y > this.normHeight)) { ( viewportBR.y < 0 || viewportTL.y > this.normHeight ) ) {
return; return;
} }
if ( !this.config.wrapHorizontal ) {
viewportTL.x = Math.max( viewportTL.x, 0 );
var _abs = Math.abs; viewportBR.x = Math.min( viewportBR.x, 1 );
var _ceil = Math.ceil;
var _floor = Math.floor;
var _log = Math.log;
var _max = Math.max;
var _min = Math.min;
var alwaysBlend = this.config.alwaysBlend;
var blendTimeMillis = 1000 * this.config.blendTime;
var immediateRender = this.config.immediateRender;
var wrapHorizontal = this.config.wrapHorizontal;
var wrapVertical = this.config.wrapVertical;
if (!wrapHorizontal) {
viewportTL.x = _max(viewportTL.x, 0);
viewportBR.x = _min(viewportBR.x, 1);
} }
if (!wrapVertical) { if ( !this.config.wrapVertical ) {
viewportTL.y = _max(viewportTL.y, 0); viewportTL.y = Math.max( viewportTL.y, 0 );
viewportBR.y = _min(viewportBR.y, this.normHeight); viewportBR.y = Math.min( viewportBR.y, this.normHeight );
} }
var best = null; lowestLevel = Math.min( lowestLevel, highestLevel );
var haveDrawn = false;
var currentTime = new Date().getTime();
var viewportCenter = this.viewport.pixelFromPoint(this.viewport.getCenter()); for ( level = highestLevel; level >= lowestLevel; level-- ) {
var zeroRatioT = this.viewport.deltaPixelsFromPoints(this.source.getPixelRatio(0), false).x; drawLevel = false;
var optimalPixelRatio = immediateRender ? 1 : zeroRatioT; // note the .x!
renderPixelRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( level ),
true
).x;
renderPixelRatioT = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio( level ),
false
).x;
var lowestLevel = _max(this.minLevel, _floor(_log(this.config.minZoomImageRatio) / _log(2))); if ( ( !haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO ) ||
var zeroRatioC = this.viewport.deltaPixelsFromPoints(this.source.getPixelRatio(0), true).x; ( level == lowestLevel ) ) {
var highestLevel = _min(this.maxLevel,
_floor(_log(zeroRatioC / MIN_PIXEL_RATIO) / _log(2)));
lowestLevel = _min(lowestLevel, highestLevel);
for (var level = highestLevel; level >= lowestLevel; level--) {
var drawLevel = false;
var renderPixelRatioC = this.viewport.deltaPixelsFromPoints(
this.source.getPixelRatio(level), true).x; // note the .x!
if ((!haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO) ||
level == lowestLevel) {
drawLevel = true; drawLevel = true;
haveDrawn = true; haveDrawn = true;
} else if (!haveDrawn) { } else if ( !haveDrawn ) {
continue; continue;
} }
this._resetCoverage(level); this._resetCoverage( level );
var levelOpacity = _min(1, (renderPixelRatioC - 0.5) / 0.5); levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 );
var renderPixelRatioT = this.viewport.deltaPixelsFromPoints( levelVisibility = optimalRatio / Math.abs(
this.source.getPixelRatio(level), false).x; optimalRatio - renderPixelRatioT
var levelVisibility = optimalPixelRatio / );
_abs(optimalPixelRatio - renderPixelRatioT);
var tileTL = this.source.getTileAtPoint(level, viewportTL); tileTL = this.source.getTileAtPoint( level, viewportTL );
var tileBR = this.source.getTileAtPoint(level, viewportBR); tileBR = this.source.getTileAtPoint( level, viewportBR );
var numTiles = numberOfTiles( this, level ); numTiles = numberOfTiles( this, level );
var numTilesX = numTiles.x; numTilesX = numTiles.x;
var numTilesY = numTiles.y; numTilesY = numTiles.y;
if (!wrapHorizontal) {
tileBR.x = _min(tileBR.x, numTilesX - 1); if ( !this.config.wrapHorizontal ) {
tileBR.x = Math.min( tileBR.x, numTilesX - 1 );
} }
if (!wrapVertical) { if ( !this.config.wrapVertical ) {
tileBR.y = _min(tileBR.y, numTilesY - 1); tileBR.y = Math.min( tileBR.y, numTilesY - 1 );
} }
for (var x = tileTL.x; x <= tileBR.x; x++) { for ( x = tileTL.x; x <= tileBR.x; x++ ) {
for (var y = tileTL.y; y <= tileBR.y; y++) { for ( y = tileTL.y; y <= tileBR.y; y++ ) {
var tile = this._getTile(level, x, y, currentTime, numTilesX, numTilesY); drawTile = drawLevel;
var drawTile = drawLevel; tile = this._getTile(
level,
x, y,
currentTime,
numTilesX,
numTilesY
);
this._setCoverage(level, x, y, false); this._setCoverage( level, x, y, false );
if (!tile.exists) { if ( !tile.exists ) {
continue; continue;
} }
if (haveDrawn && !drawTile) { if ( haveDrawn && !drawTile ) {
if (this._isCovered(level, x, y)) { if ( this._isCovered( level, x, y ) ) {
this._setCoverage(level, x, y, true); this._setCoverage( level, x, y, true );
} else { } else {
drawTile = true; drawTile = true;
} }
} }
if (!drawTile) { if ( !drawTile ) {
continue; continue;
} }
var boundsTL = tile.bounds.getTopLeft(); this._positionTile(
var boundsSize = tile.bounds.getSize(); tile,
var positionC = this.viewport.pixelFromPoint(boundsTL, true); viewportCenter,
var sizeC = this.viewport.deltaPixelsFromPoints(boundsSize, true); levelVisibility
);
if (!this.tileOverlap) { if ( tile.loaded ) {
sizeC = sizeC.plus(new $.Point(1, 1));
}
var positionT = this.viewport.pixelFromPoint(boundsTL, false);
var sizeT = this.viewport.deltaPixelsFromPoints(boundsSize, false);
var tileCenter = positionT.plus(sizeT.divide(2));
var tileDistance = viewportCenter.distanceTo(tileCenter);
tile.position = positionC;
tile.size = sizeC;
tile.distance = tileDistance;
tile.visibility = levelVisibility;
if (tile.loaded) {
if (!tile.blendStart) {
tile.blendStart = currentTime;
}
var deltaTime = currentTime - tile.blendStart;
var opacity = _min(1, deltaTime / blendTimeMillis);
if (alwaysBlend) { updateAgain = this._blendTile(
opacity *= levelOpacity; tile,
} x, y,
level,
levelOpacity,
currentTime
);
tile.opacity = opacity; } else if ( tile.Loading ) {
//do nothing
_lastDrawn.push(tile);
if (opacity == 1) {
this._setCoverage(level, x, y, true);
} else if (deltaTime < blendTimeMillis) {
updateAgain = true;
}
} else if (tile.Loading) {
} else { } else {
best = this._compareTiles(best, tile); best = this._compareTiles( best, tile );
} }
} }
} }
if (this._providesCoverage(level)) { if ( this._providesCoverage( level ) ) {
break; break;
} }
} }
for (var i = _lastDrawn.length - 1; i >= 0; i--) { this._drawTiles();
var tile = _lastDrawn[i]; this._drawOverlays();
if ( best ) {
this._loadTile( best, currentTime );
// because we haven't finished drawing, so
this.updateAgain = true;
}
},
_drawLevel: function( ){
},
_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 ) );
}
tile.position = positionC;
tile.size = sizeC;
tile.distance = tileDistance;
tile.visibility = levelVisibility;
},
_blendTile: function( tile, x, y, level, levelOpacity, currentTime ){
var blendTimeMillis = 1000 * this.config.blendTime,
deltaTime,
opacity;
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;
}
return false;
},
_drawTiles: function(){
var i,
tile;
for ( i = this.lastDrawn.length - 1; i >= 0; i-- ) {
tile = this.lastDrawn[ i ];
if ( USE_CANVAS ) { if ( USE_CANVAS ) {
tile.drawCanvas(_context); tile.drawCanvas( this.context );
} else { } else {
tile.drawHTML(_canvas); tile.drawHTML( this.canvas );
} }
tile.beingDrawn = true; tile.beingDrawn = true;
} }
},
var numOverlays = this.overlays.length; _drawOverlays: function(){
for (var i = 0; i < numOverlays; i++) { var i,
var overlay = this.overlays[i]; length = this.overlays.length;
var bounds = overlay.bounds; for ( i = 0; i < length; i++ ) {
this._drawOverlay( this.overlays[ i ] );
overlay.position = this.viewport.pixelFromPoint(bounds.getTopLeft(), true);
overlay.size = this.viewport.deltaPixelsFromPoints(bounds.getSize(), true);
overlay.drawHTML(_container);
}
if (best) {
this._loadTile(best, currentTime);
this.updateAgain = true; // because we haven't finished drawing, so
} }
}, },
_drawOverlay: function( overlay ){
var bounds = overlay.bounds;
addOverlay: function(elmt, loc, placement) { overlay.position = this.viewport.pixelFromPoint(
var elmt = $.getElement(elmt); bounds.getTopLeft(),
true
);
overlay.size = this.viewport.deltaPixelsFromPoints(
bounds.getSize(),
true
);
overlay.drawHTML( this.container );
},
if (this._getOverlayIndex(elmt) >= 0) { addOverlay: function( element, location, placement ) {
return; // they're trying to add a duplicate overlay element = $.getElement( element );
if ( this._getOverlayIndex( element ) >= 0 ) {
// they're trying to add a duplicate overlay
return;
} }
this.overlays.push(new $.Overlay(elmt, loc, placement)); this.overlays.push( new $.Overlay( element, location, placement ) );
this.updateAgain = true; this.updateAgain = true;
}, },
updateOverlay: function(elmt, loc, placement) { updateOverlay: function( element, location, placement ) {
var elmt = $.getElement(elmt); var i;
var i = this._getOverlayIndex(elmt);
if (i >= 0) { element = $.getElement( element );
this.overlays[i].update(loc, placement); i = this._getOverlayIndex( element );
if ( i >= 0 ) {
this.overlays[ i ].update( location, placement );
this.updateAgain = true; this.updateAgain = true;
} }
}, },
removeOverlay: function(elmt) { removeOverlay: function( element ) {
var elmt = $.getElement(elmt); var i;
var i = this._getOverlayIndex(elmt);
if (i >= 0) { element = $.getElement( element );
this.overlays[i].destroy(); i = this._getOverlayIndex( element );
this.overlays.splice(i, 1);
if ( i >= 0 ) {
this.overlays[ i ].destroy();
this.overlays.splice( i, 1 );
this.updateAgain = true; this.updateAgain = true;
} }
}, },
clearOverlays: function() { clearOverlays: function() {
while (this.overlays.length > 0) { while ( this.overlays.length > 0 ) {
this.overlays.pop().destroy(); this.overlays.pop().destroy();
this.updateAgain = true; this.updateAgain = true;
} }

View File

@ -314,8 +314,8 @@ OpenSeadragon = window.OpenSeadragon || (function(){
$.extend( $, { $.extend( $, {
getElement: function( element ) { getElement: function( element ) {
if (typeof ( element ) == "string") { if ( typeof ( element ) == "string") {
element = document.getElementById( element ); element = document.getElementById( element );
} }
return element; return element;
@ -330,11 +330,13 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementPosition: function( element ) { getElementPosition: function( element ) {
var element = $.getElement( element ); var result = new $.Point(),
isFixed,
offsetParent;
var isFixed = $.getElementStyle( element ).position == "fixed", element = $.getElement( element );
offsetParent = $.getOffsetParent( element, isFixed ), isFixed = $.getElementStyle( element ).position == "fixed";
result = new $.Point(); offsetParent = $.getOffsetParent( element, isFixed );
while ( offsetParent ) { while ( offsetParent ) {
@ -354,7 +356,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementSize: function( element ) { getElementSize: function( element ) {
var element = $.getElement( element ); element = $.getElement( element );
return new $.Point( return new $.Point(
element.clientWidth, element.clientWidth,
@ -363,7 +365,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getElementStyle: function( element ) { getElementStyle: function( element ) {
var element = $.getElement( element ); element = $.getElement( element );
if ( element.currentStyle ) { if ( element.currentStyle ) {
return element.currentStyle; return element.currentStyle;
@ -379,15 +381,14 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
getMousePosition: function( event ) { getMousePosition: function( event ) {
var event = $.getEvent( event );
var result = new $.Point(); var result = new $.Point();
event = $.getEvent( event );
if ( typeof( event.pageX ) == "number" ) { if ( typeof( event.pageX ) == "number" ) {
result.x = event.pageX; result.x = event.pageX;
result.y = event.pageY; result.y = event.pageY;
} else if ( typeof( event.clientX ) == "number" ) { } else if ( typeof( event.clientX ) == "number" ) {
result.x = result.x =
event.clientX + event.clientX +
document.body.scrollLeft + document.body.scrollLeft +
@ -446,18 +447,19 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
imageFormatSupported: function( extension ) { imageFormatSupported: function( extension ) {
var extension = extension ? extension : ""; extension = extension ? extension : "";
return !!FILEFORMATS[ extension.toLowerCase() ]; return !!FILEFORMATS[ extension.toLowerCase() ];
}, },
makeCenteredNode: function( element ) { makeCenteredNode: function( element ) {
var element = $.getElement( element );
var div = $.makeNeutralElement( "div" ), var div = $.makeNeutralElement( "div" ),
html = [], html = [],
innerDiv, innerDiv,
innerDivs; innerDivs;
element = $.getElement( element );
//TODO: I dont understand the use of # inside the style attributes //TODO: I dont understand the use of # inside the style attributes
// below. Invetigate the results of the constructed html in // below. Invetigate the results of the constructed html in
// the browser and clean up the mark-up to make this clearer. // the browser and clean up the mark-up to make this clearer.
@ -534,12 +536,13 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
setElementOpacity: function( element, opacity, usesAlpha ) { setElementOpacity: function( element, opacity, usesAlpha ) {
var element = $.getElement( element );
var previousFilter, var previousFilter,
ieOpacity, ieOpacity,
ieFilter; ieFilter;
element = $.getElement( element );
if ( usesAlpha && !$.Browser.alpha ) { if ( usesAlpha && !$.Browser.alpha ) {
opacity = Math.round( opacity ); opacity = Math.round( opacity );
} }
@ -573,7 +576,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
addEvent: function( element, eventName, handler, useCapture ) { addEvent: function( element, eventName, handler, useCapture ) {
var element = $.getElement( element ); element = $.getElement( element );
//TODO: Why do this if/else on every method call instead of just //TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic // defining this function once based on the same logic
@ -592,7 +595,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
removeEvent: function( element, eventName, handler, useCapture ) { removeEvent: function( element, eventName, handler, useCapture ) {
var element = $.getElement( element ); element = $.getElement( element );
//TODO: Why do this if/else on every method call instead of just //TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic // defining this function once based on the same logic
@ -611,7 +614,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
cancelEvent: function( event ) { cancelEvent: function( event ) {
var event = $.getEvent( event ); event = $.getEvent( event );
if ( event.preventDefault ) { if ( event.preventDefault ) {
event.preventDefault(); // W3C for preventing default event.preventDefault(); // W3C for preventing default
@ -622,7 +625,7 @@ OpenSeadragon = window.OpenSeadragon || (function(){
}, },
stopEvent: function( event ) { stopEvent: function( event ) {
var event = $.getEvent( event ); event = $.getEvent( event );
if ( event.stopPropagation ) { if ( event.stopPropagation ) {
event.stopPropagation(); // W3C for stopping propagation event.stopPropagation(); // W3C for stopping propagation