Avoid creating unnecessary images during rendering
This commit is contained in:
@@ -287,11 +287,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame =
|
||||
/** @type {Array.<ol.Tile>} */
|
||||
var tilesToClear = [];
|
||||
|
||||
var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) {
|
||||
return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED;
|
||||
}, tileSource, pixelRatio, projection);
|
||||
var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource,
|
||||
tilesToDrawByZ, getTileIfLoaded);
|
||||
var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
|
||||
|
||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||
|
||||
|
||||
@@ -128,11 +128,7 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame =
|
||||
var tilesToDrawByZ = {};
|
||||
tilesToDrawByZ[z] = {};
|
||||
|
||||
var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) {
|
||||
return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED;
|
||||
}, tileSource, pixelRatio, projection);
|
||||
var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource,
|
||||
tilesToDrawByZ, getTileIfLoaded);
|
||||
var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
|
||||
|
||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||
|
||||
|
||||
@@ -85,6 +85,33 @@ ol.renderer.Layer.prototype.forEachLayerAtPixel =
|
||||
ol.renderer.Layer.prototype.hasFeatureAtCoordinate = goog.functions.FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* Create a function that adds loaded tiles to the tile lookup.
|
||||
* @param {ol.source.Tile} source Tile source.
|
||||
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
||||
* tiles by zoom level.
|
||||
* @return {function(number, ol.TileRange):boolean} A function that can be
|
||||
* called with a zoom level and a tile range to add loaded tiles to the
|
||||
* lookup.
|
||||
* @protected
|
||||
*/
|
||||
ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
|
||||
/**
|
||||
* @param {number} zoom Zoom level.
|
||||
* @param {ol.TileRange} tileRange Tile range.
|
||||
* @return {boolean} The tile range is fully loaded.
|
||||
*/
|
||||
return function(zoom, tileRange) {
|
||||
return source.forEachLoadedTile(zoom, tileRange, function(tile) {
|
||||
if (!tiles[zoom]) {
|
||||
tiles[zoom] = {};
|
||||
}
|
||||
tiles[zoom][tile.tileCoord.toString()] = tile;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @return {ol.layer.Layer} Layer.
|
||||
|
||||
@@ -94,6 +94,40 @@ ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a function that adds loaded tiles to the tile lookup.
|
||||
* @param {ol.source.Tile} source Tile source.
|
||||
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
||||
* tiles by zoom level.
|
||||
* @return {function(number, ol.TileRange):boolean} A function that can be
|
||||
* called with a zoom level and a tile range to add loaded tiles to the
|
||||
* lookup.
|
||||
* @protected
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder =
|
||||
function(source, tiles) {
|
||||
var mapRenderer = this.mapRenderer;
|
||||
|
||||
/**
|
||||
* @param {number} zoom Zoom level.
|
||||
* @param {ol.TileRange} tileRange Tile range.
|
||||
* @return {boolean} The tile range is fully loaded.
|
||||
*/
|
||||
return function(zoom, tileRange) {
|
||||
return source.forEachLoadedTile(zoom, tileRange, function(tile) {
|
||||
var loaded = mapRenderer.isTileTextureLoaded(tile);
|
||||
if (loaded) {
|
||||
if (!tiles[zoom]) {
|
||||
tiles[zoom] = {};
|
||||
}
|
||||
tiles[zoom][tile.tileCoord.toString()] = tile;
|
||||
}
|
||||
return loaded;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -190,12 +224,8 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame =
|
||||
var tilesToDrawByZ = {};
|
||||
tilesToDrawByZ[z] = {};
|
||||
|
||||
var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) {
|
||||
return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED &&
|
||||
mapRenderer.isTileTextureLoaded(tile);
|
||||
}, tileSource, pixelRatio, projection);
|
||||
var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource,
|
||||
tilesToDrawByZ, getTileIfLoaded);
|
||||
var findLoadedTiles = this.createLoadedTileFinder(
|
||||
tileSource, tilesToDrawByZ);
|
||||
|
||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||
var allTilesLoaded = true;
|
||||
|
||||
@@ -6,6 +6,7 @@ goog.require('ol.Attribution');
|
||||
goog.require('ol.Extent');
|
||||
goog.require('ol.TileCache');
|
||||
goog.require('ol.TileRange');
|
||||
goog.require('ol.TileState');
|
||||
goog.require('ol.source.Source');
|
||||
goog.require('ol.tilecoord');
|
||||
goog.require('ol.tilegrid.TileGrid');
|
||||
@@ -92,41 +93,33 @@ ol.source.Tile.prototype.expireCache = function(usedTiles) {
|
||||
|
||||
|
||||
/**
|
||||
* Look for loaded tiles over a given tile range and zoom level. Adds
|
||||
* properties to the provided lookup representing key/tile pairs for already
|
||||
* loaded tiles.
|
||||
*
|
||||
* @param {Object.<number, Object.<string, ol.Tile>>} loadedTilesByZ A lookup of
|
||||
* loaded tiles by zoom level.
|
||||
* @param {function(number, number, number): ol.Tile} getTileIfLoaded A function
|
||||
* that returns the tile only if it is fully loaded.
|
||||
* @param {number} z Zoom level.
|
||||
* @param {ol.TileRange} tileRange Tile range.
|
||||
* @param {function(ol.Tile):(boolean|undefined)} callback Called with each
|
||||
* loaded tile. If the callback returns `false`, the tile will not be
|
||||
* considered loaded.
|
||||
* @return {boolean} The tile range is fully covered with loaded tiles.
|
||||
*/
|
||||
ol.source.Tile.prototype.findLoadedTiles = function(loadedTilesByZ,
|
||||
getTileIfLoaded, z, tileRange) {
|
||||
// FIXME this could be more efficient about filling partial holes
|
||||
var fullyCovered = true;
|
||||
var tile, tileCoordKey, x, y;
|
||||
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||
ol.source.Tile.prototype.forEachLoadedTile = function(z, tileRange, callback) {
|
||||
var covered = true;
|
||||
var tile, tileCoordKey, loaded;
|
||||
for (var x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||
for (var y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||
tileCoordKey = this.getKeyZXY(z, x, y);
|
||||
if (loadedTilesByZ[z] && loadedTilesByZ[z][tileCoordKey]) {
|
||||
continue;
|
||||
}
|
||||
tile = getTileIfLoaded(z, x, y);
|
||||
if (!goog.isNull(tile)) {
|
||||
if (!loadedTilesByZ[z]) {
|
||||
loadedTilesByZ[z] = {};
|
||||
loaded = false;
|
||||
if (this.tileCache.containsKey(tileCoordKey)) {
|
||||
tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
|
||||
loaded = tile.getState() === ol.TileState.LOADED;
|
||||
if (loaded) {
|
||||
loaded = (callback(tile) !== false);
|
||||
}
|
||||
loadedTilesByZ[z][tileCoordKey] = tile;
|
||||
} else {
|
||||
fullyCovered = false;
|
||||
}
|
||||
if (!loaded) {
|
||||
covered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullyCovered;
|
||||
return covered;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -12,176 +12,105 @@ describe('ol.source.Tile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#findLoadedTiles()', function() {
|
||||
describe('#forEachLoadedTile()', function() {
|
||||
|
||||
it('adds no tiles if none are already loaded', function() {
|
||||
// a source with no loaded tiles
|
||||
var callback;
|
||||
beforeEach(function() {
|
||||
callback = sinon.spy();
|
||||
});
|
||||
|
||||
it('does not call the callback if no tiles are loaded', function() {
|
||||
var source = new ol.test.source.TileMock({});
|
||||
|
||||
var loadedTilesByZ = {};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 3);
|
||||
var zoom = 3;
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
source.findLoadedTiles(loadedTilesByZ, getTileIfLoaded, 3, range);
|
||||
|
||||
var keys = goog.object.getKeys(loadedTilesByZ);
|
||||
expect(keys.length).to.be(0);
|
||||
source.forEachLoadedTile(zoom, range, callback);
|
||||
expect(callback.callCount).to.be(0);
|
||||
});
|
||||
|
||||
it('adds loaded tiles to the lookup (z: 0)', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({
|
||||
'0/0/0': true,
|
||||
'1/0/0': true
|
||||
});
|
||||
|
||||
var loadedTilesByZ = {};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 0);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
source.findLoadedTiles(loadedTilesByZ, getTileIfLoaded, 0, range);
|
||||
var keys = goog.object.getKeys(loadedTilesByZ);
|
||||
expect(keys.length).to.be(1);
|
||||
var tile = loadedTilesByZ['0']['0/0/0'];
|
||||
expect(tile).to.be.a(ol.Tile);
|
||||
expect(tile.state).to.be(ol.TileState.LOADED);
|
||||
});
|
||||
|
||||
it('adds loaded tiles to the lookup (z: 1)', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({
|
||||
'0/0/0': true,
|
||||
'1/0/0': true
|
||||
});
|
||||
|
||||
var loadedTilesByZ = {};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 1);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
source.findLoadedTiles(loadedTilesByZ, getTileIfLoaded, 1, range);
|
||||
var keys = goog.object.getKeys(loadedTilesByZ);
|
||||
expect(keys.length).to.be(1);
|
||||
var tile = loadedTilesByZ['1']['1/0/0'];
|
||||
expect(tile).to.be.a(ol.Tile);
|
||||
expect(tile.state).to.be(ol.TileState.LOADED);
|
||||
});
|
||||
|
||||
it('returns true when all tiles are already loaded', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({
|
||||
'1/0/0': true,
|
||||
'1/0/1': true,
|
||||
'1/1/0': true,
|
||||
'1/1/1': true
|
||||
});
|
||||
|
||||
var loadedTilesByZ = {};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 1);
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
var loaded = source.findLoadedTiles(
|
||||
loadedTilesByZ, getTileIfLoaded, 1, range);
|
||||
expect(loaded).to.be(true);
|
||||
});
|
||||
|
||||
it('returns true when all tiles are already loaded (part 2)', function() {
|
||||
// a source with no loaded tiles
|
||||
it('does not call getTile() if no tiles are loaded', function() {
|
||||
var source = new ol.test.source.TileMock({});
|
||||
|
||||
var loadedTilesByZ = {
|
||||
'1': {
|
||||
'1/0/0': true,
|
||||
'1/0/1': true,
|
||||
'1/1/0': true,
|
||||
'1/1/1': true,
|
||||
'1/1/2': true
|
||||
}
|
||||
};
|
||||
sinon.spy(source, 'getTile');
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 1);
|
||||
var zoom = 3;
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
var loaded = source.findLoadedTiles(
|
||||
loadedTilesByZ, getTileIfLoaded, 1, range);
|
||||
expect(loaded).to.be(true);
|
||||
source.forEachLoadedTile(zoom, range, callback);
|
||||
expect(source.getTile.callCount).to.be(0);
|
||||
source.getTile.restore();
|
||||
});
|
||||
|
||||
it('returns false when all tiles are already loaded', function() {
|
||||
// a source with no loaded tiles
|
||||
|
||||
it('calls callback for each loaded tile', function() {
|
||||
var source = new ol.test.source.TileMock({
|
||||
'1/0/0': true,
|
||||
'1/0/1': true,
|
||||
'1/1/0': true,
|
||||
'1/1/1': false
|
||||
'1/0/0': ol.TileState.LOADED,
|
||||
'1/0/1': ol.TileState.LOADED,
|
||||
'1/1/0': ol.TileState.LOADING,
|
||||
'1/1/1': ol.TileState.LOADED
|
||||
});
|
||||
|
||||
var loadedTilesByZ = {};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 1);
|
||||
var zoom = 1;
|
||||
var range = new ol.TileRange(0, 1, 0, 1);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
var loaded = source.findLoadedTiles(
|
||||
loadedTilesByZ, getTileIfLoaded, 1, range);
|
||||
expect(loaded).to.be(false);
|
||||
source.forEachLoadedTile(zoom, range, callback);
|
||||
expect(callback.callCount).to.be(3);
|
||||
});
|
||||
|
||||
it('returns false when all tiles are already loaded (part 2)', function() {
|
||||
it('returns true if range is fully loaded', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({});
|
||||
var source = new ol.test.source.TileMock({
|
||||
'1/0/0': ol.TileState.LOADED,
|
||||
'1/0/1': ol.TileState.LOADED,
|
||||
'1/1/0': ol.TileState.LOADED,
|
||||
'1/1/1': ol.TileState.LOADED
|
||||
});
|
||||
|
||||
var loadedTilesByZ = {
|
||||
'1': {
|
||||
'1/0/0': true,
|
||||
'1/0/1': true,
|
||||
'1/1/0': true,
|
||||
'1/1/1': false
|
||||
}
|
||||
};
|
||||
var grid = source.getTileGrid();
|
||||
var extent = [-180, -180, 180, 180];
|
||||
var range = grid.getTileRangeForExtentAndZ(extent, 1);
|
||||
var zoom = 1;
|
||||
var range = new ol.TileRange(0, 1, 0, 1);
|
||||
|
||||
function getTileIfLoaded(z, x, y) {
|
||||
var tile = source.getTile(z, x, y);
|
||||
return (!goog.isNull(tile) && tile.getState() === ol.TileState.LOADED) ?
|
||||
tile : null;
|
||||
}
|
||||
var loaded = source.findLoadedTiles(
|
||||
loadedTilesByZ, getTileIfLoaded, 1, range);
|
||||
expect(loaded).to.be(false);
|
||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
||||
return true;
|
||||
});
|
||||
expect(covered).to.be(true);
|
||||
});
|
||||
|
||||
it('returns false if range is not fully loaded', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({
|
||||
'1/0/0': ol.TileState.LOADED,
|
||||
'1/0/1': ol.TileState.LOADED,
|
||||
'1/1/0': ol.TileState.LOADING,
|
||||
'1/1/1': ol.TileState.LOADED
|
||||
});
|
||||
|
||||
var zoom = 1;
|
||||
var range = new ol.TileRange(0, 1, 0, 1);
|
||||
|
||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
||||
return true;
|
||||
});
|
||||
expect(covered).to.be(false);
|
||||
});
|
||||
|
||||
it('allows callback to override loaded check', function() {
|
||||
// a source with no loaded tiles
|
||||
var source = new ol.test.source.TileMock({
|
||||
'1/0/0': ol.TileState.LOADED,
|
||||
'1/0/1': ol.TileState.LOADED,
|
||||
'1/1/0': ol.TileState.LOADED,
|
||||
'1/1/1': ol.TileState.LOADED
|
||||
});
|
||||
|
||||
var zoom = 1;
|
||||
var range = new ol.TileRange(0, 1, 0, 1);
|
||||
|
||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
||||
return false;
|
||||
});
|
||||
expect(covered).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -196,9 +125,10 @@ describe('ol.source.Tile', function() {
|
||||
*
|
||||
* @constructor
|
||||
* @extends {ol.source.Tile}
|
||||
* @param {Object.<string, boolean>} loaded Lookup of already loaded tiles.
|
||||
* @param {Object.<string, ol.TileState>} tileStates Lookup of tile key to
|
||||
* tile state.
|
||||
*/
|
||||
ol.test.source.TileMock = function(loaded) {
|
||||
ol.test.source.TileMock = function(tileStates) {
|
||||
var tileGrid = new ol.tilegrid.TileGrid({
|
||||
resolutions: [360 / 256, 180 / 256, 90 / 256, 45 / 256],
|
||||
origin: [-180, -180],
|
||||
@@ -210,11 +140,9 @@ ol.test.source.TileMock = function(loaded) {
|
||||
tileGrid: tileGrid
|
||||
});
|
||||
|
||||
/**
|
||||
* @type {Object.<string, boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.loaded_ = loaded;
|
||||
for (var key in tileStates) {
|
||||
this.tileCache.set(key, new ol.Tile(key.split('/'), tileStates[key]));
|
||||
}
|
||||
|
||||
};
|
||||
goog.inherits(ol.test.source.TileMock, ol.source.Tile);
|
||||
@@ -224,9 +152,14 @@ goog.inherits(ol.test.source.TileMock, ol.source.Tile);
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.test.source.TileMock.prototype.getTile = function(z, x, y) {
|
||||
var key = ol.tilecoord.getKeyZXY(z, x, y);
|
||||
var tileState = this.loaded_[key] ? ol.TileState.LOADED : ol.TileState.IDLE;
|
||||
return new ol.Tile([z, x, y], tileState);
|
||||
var key = this.getKeyZXY(z, x, y);
|
||||
if (this.tileCache.containsKey(key)) {
|
||||
return /** @type {!ol.Tile} */ (this.tileCache.get(key));
|
||||
} else {
|
||||
var tile = new ol.Tile(key, ol.TileState.IDLE);
|
||||
this.tileCache.set(key, tile);
|
||||
return tile;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -243,8 +176,8 @@ describe('ol.test.source.TileMock', function() {
|
||||
describe('#getTile()', function() {
|
||||
it('returns a tile with state based on constructor arg', function() {
|
||||
var source = new ol.test.source.TileMock({
|
||||
'0/0/0': true,
|
||||
'1/0/0': true
|
||||
'0/0/0': ol.TileState.LOADED,
|
||||
'1/0/0': ol.TileState.LOADED
|
||||
});
|
||||
var tile;
|
||||
|
||||
@@ -270,9 +203,9 @@ describe('ol.test.source.TileMock', function() {
|
||||
|
||||
goog.require('goog.object');
|
||||
goog.require('ol.Tile');
|
||||
goog.require('ol.TileRange');
|
||||
goog.require('ol.TileState');
|
||||
goog.require('ol.proj');
|
||||
goog.require('ol.source.Source');
|
||||
goog.require('ol.source.Tile');
|
||||
goog.require('ol.tilecoord');
|
||||
goog.require('ol.tilegrid.TileGrid');
|
||||
|
||||
Reference in New Issue
Block a user