Factor out ImageTile and ImageTileSource

This commit is contained in:
Tom Payne
2013-01-05 16:26:58 +01:00
parent 9194a4b4eb
commit 97d83666a6
10 changed files with 284 additions and 190 deletions

137
src/ol/imagetile.js Normal file
View File

@@ -0,0 +1,137 @@
goog.provide('ol.ImageTile');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('ol.Tile');
goog.require('ol.TileCoord');
goog.require('ol.TileState');
/**
* @constructor
* @extends {ol.Tile}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
*/
ol.ImageTile = function(tileCoord, src, crossOrigin) {
goog.base(this, tileCoord);
/**
* Image URI
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {Image}
*/
this.image_ = new Image();
if (!goog.isNull(crossOrigin)) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Object.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
this.imageListenerKeys_ = null;
};
goog.inherits(ol.ImageTile, ol.Tile);
/**
* @inheritDoc
*/
ol.ImageTile.prototype.getImage = function(opt_context) {
if (goog.isDef(opt_context)) {
var image;
var key = goog.getUid(opt_context);
if (key in this.imageByContext_) {
return this.imageByContext_[key];
} else if (goog.object.isEmpty(this.imageByContext_)) {
image = this.image_;
} else {
image = /** @type {Image} */ (this.image_.cloneNode(false));
}
this.imageByContext_[key] = image;
return image;
} else {
return this.image_;
}
};
/**
* @inheritDoc
*/
ol.ImageTile.prototype.getKey = function() {
return this.src_;
};
/**
* Tracks loading or read errors.
*
* @private
*/
ol.ImageTile.prototype.handleImageError_ = function() {
this.state = ol.TileState.ERROR;
this.unlistenImage_();
};
/**
* Tracks successful image load.
*
* @private
*/
ol.ImageTile.prototype.handleImageLoad_ = function() {
this.state = ol.TileState.LOADED;
this.unlistenImage_();
this.dispatchChangeEvent();
};
/**
* Load not yet loaded URI.
*/
ol.ImageTile.prototype.load = function() {
if (this.state == ol.TileState.IDLE) {
this.state = ol.TileState.LOADING;
goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
this.imageListenerKeys_ = [
goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
this.handleImageError_, false, this),
goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
this.handleImageLoad_, false, this)
];
this.image_.src = this.src_;
}
};
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ol.ImageTile.prototype.unlistenImage_ = function() {
goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
this.imageListenerKeys_ = null;
};

View File

@@ -17,6 +17,7 @@ goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('goog.style');
goog.require('goog.webgl');
goog.require('ol.Tile');
goog.require('ol.layer.Layer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.webgl.FragmentShader');
@@ -219,15 +220,15 @@ ol.renderer.webgl.Map.prototype.addLayer = function(layer) {
/**
* @param {Image} image Image.
* @param {ol.Tile} tile Tile.
* @param {number} magFilter Mag filter.
* @param {number} minFilter Min filter.
*/
ol.renderer.webgl.Map.prototype.bindImageTexture =
function(image, magFilter, minFilter) {
ol.renderer.webgl.Map.prototype.bindTileTexture =
function(tile, magFilter, minFilter) {
var gl = this.getGL();
var imageKey = image.src;
var textureCacheEntry = this.textureCache_[imageKey];
var tileKey = tile.getKey();
var textureCacheEntry = this.textureCache_[tileKey];
if (goog.isDef(textureCacheEntry)) {
gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
if (textureCacheEntry.magFilter != magFilter) {
@@ -244,7 +245,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA,
goog.webgl.UNSIGNED_BYTE, image);
goog.webgl.UNSIGNED_BYTE, tile.getImage());
gl.texParameteri(
goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
gl.texParameteri(
@@ -253,7 +254,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
goog.webgl.CLAMP_TO_EDGE);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
goog.webgl.CLAMP_TO_EDGE);
this.textureCache_[imageKey] = {
this.textureCache_[tileKey] = {
texture: texture,
magFilter: magFilter,
minFilter: minFilter
@@ -485,11 +486,11 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
/**
* @param {Image} image Image.
* @return {boolean} Is image texture loaded.
* @param {ol.Tile} tile Tile.
* @return {boolean} Is tile texture loaded.
*/
ol.renderer.webgl.Map.prototype.isImageTextureLoaded = function(image) {
return image.src in this.textureCache_;
ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
return tile.getKey() in this.textureCache_;
};

View File

@@ -386,9 +386,9 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
var tilesToDrawByZ = {};
/**
* @type {Array.<Image>}
* @type {Array.<ol.Tile>}
*/
var imagesToLoad = [];
var tilesToLoad = [];
var allTilesLoaded = true;
@@ -405,12 +405,11 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
if (tileState == ol.TileState.IDLE) {
tile.load();
} else if (tileState == ol.TileState.LOADED) {
var image = tile.getImage();
if (mapRenderer.isImageTextureLoaded(image)) {
if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile;
return;
} else {
imagesToLoad.push(image);
tilesToLoad.push(tile);
}
} else if (tileState == ol.TileState.ERROR) {
return;
@@ -459,29 +458,28 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
framebufferExtentSize.height - 1;
goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
mapRenderer.bindImageTexture(
tile.getImage(), goog.webgl.LINEAR, goog.webgl.LINEAR);
mapRenderer.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this);
}, this);
if (!goog.array.isEmpty(imagesToLoad)) {
if (!goog.array.isEmpty(tilesToLoad)) {
goog.events.listenOnce(
map,
ol.MapEventType.POSTRENDER,
goog.partial(function(mapRenderer, imagesToLoad) {
goog.partial(function(mapRenderer, tilesToLoad) {
if (goog.DEBUG) {
this.logger.info(
'uploading ' + imagesToLoad.length + ' textures');
'uploading ' + tilesToLoad.length + ' textures');
}
goog.array.forEach(imagesToLoad, function(image) {
mapRenderer.bindImageTexture(
image, goog.webgl.LINEAR, goog.webgl.LINEAR);
goog.array.forEach(tilesToLoad, function(tile) {
mapRenderer.bindTileTexture(
tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
});
if (goog.DEBUG) {
this.logger.info('uploaded textures');
}
}, mapRenderer, imagesToLoad));
}, mapRenderer, tilesToLoad));
}
if (allTilesLoaded) {

View File

@@ -6,7 +6,7 @@ goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.net.Jsonp');
goog.require('ol.TileCoverageArea');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -25,7 +25,7 @@ ol.BingMapsStyle = {
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.BingMapsOptions} bingMapsOptions Bing Maps options.
*/
ol.source.BingMaps = function(bingMapsOptions) {
@@ -57,7 +57,7 @@ ol.source.BingMaps = function(bingMapsOptions) {
}, goog.bind(this.handleImageryMetadataResponse, this));
};
goog.inherits(ol.source.BingMaps, ol.source.TileSource);
goog.inherits(ol.source.BingMaps, ol.source.ImageTileSource);
/**

View File

@@ -0,0 +1,95 @@
goog.provide('ol.source.ImageTileSource');
goog.provide('ol.source.ImageTileSourceOptions');
goog.require('ol.Attribution');
goog.require('ol.Extent');
goog.require('ol.ImageTile');
goog.require('ol.Projection');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType');
goog.require('ol.source.TileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined),
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
*/
ol.source.ImageTileSourceOptions;
/**
* @constructor
* @extends {ol.source.TileSource}
* @param {ol.source.ImageTileSourceOptions} options Options.
*/
ol.source.ImageTileSource = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
projection: options.projection,
tileGrid: options.tileGrid
});
/**
* @protected
* @type {ol.TileUrlFunctionType}
*/
this.tileUrlFunction = goog.isDef(options.tileUrlFunction) ?
options.tileUrlFunction :
ol.TileUrlFunction.nullTileUrlFunction;
/**
* @private
* @type {?string}
*/
this.crossOrigin_ =
goog.isDef(options.crossOrigin) ? options.crossOrigin : 'anonymous';
/**
* @private
* @type {Object.<string, ol.ImageTile>}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/
this.tileCache_ = {};
};
goog.inherits(ol.source.ImageTileSource, ol.source.TileSource);
/**
* @inheritDoc
*/
ol.source.ImageTileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) {
return this.tileCache_[key];
} else {
var tileUrl = this.getTileCoordUrl(tileCoord);
var tile;
if (goog.isDef(tileUrl)) {
tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_);
} else {
tile = null;
}
this.tileCache_[key] = tile;
return tile;
}
};
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {string|undefined} Tile URL.
*/
ol.source.ImageTileSource.prototype.getTileCoordUrl = function(tileCoord) {
return this.tileUrlFunction(tileCoord);
};

View File

@@ -10,14 +10,14 @@ goog.require('ol.Attribution');
goog.require('ol.Projection');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.TiledWMSOptions} tiledWMSOptions options.
*/
ol.source.TiledWMS = function(tiledWMSOptions) {
@@ -116,4 +116,4 @@ ol.source.TiledWMS = function(tiledWMSOptions) {
});
};
goog.inherits(ol.source.TiledWMS, ol.source.TileSource);
goog.inherits(ol.source.TiledWMS, ol.source.ImageTileSource);

View File

@@ -16,7 +16,7 @@ goog.require('goog.string');
goog.require('ol.Projection');
goog.require('ol.TileCoverageArea');
goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -45,7 +45,7 @@ goog.exportSymbol('grid', grid);
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.TileJSONOptions} tileJsonOptions TileJSON optios.
*/
ol.source.TileJSON = function(tileJsonOptions) {
@@ -69,7 +69,7 @@ ol.source.TileJSON = function(tileJsonOptions) {
this.deferred_.addCallback(this.handleTileJSONResponse, this);
};
goog.inherits(ol.source.TileJSON, ol.source.TileSource);
goog.inherits(ol.source.TileJSON, ol.source.ImageTileSource);
/**

View File

@@ -14,11 +14,9 @@ goog.require('ol.tilegrid.TileGrid');
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined),
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
* tileGrid: (ol.tilegrid.TileGrid|undefined)}}
*/
ol.source.TileSourceOptions;
@@ -44,29 +42,6 @@ ol.source.TileSource = function(tileSourceOptions) {
this.tileGrid = goog.isDef(tileSourceOptions.tileGrid) ?
tileSourceOptions.tileGrid : null;
/**
* @protected
* @type {ol.TileUrlFunctionType}
*/
this.tileUrlFunction = goog.isDef(tileSourceOptions.tileUrlFunction) ?
tileSourceOptions.tileUrlFunction :
ol.TileUrlFunction.nullTileUrlFunction;
/**
* @private
* @type {?string}
*/
this.crossOrigin_ = goog.isDef(tileSourceOptions.crossOrigin) ?
tileSourceOptions.crossOrigin : 'anonymous';
/**
* @private
* @type {Object.<string, ol.Tile>}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/
this.tileCache_ = {};
};
goog.inherits(ol.source.TileSource, ol.source.Source);
@@ -83,31 +58,7 @@ ol.source.TileSource.prototype.getResolutions = function() {
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {ol.Tile} Tile.
*/
ol.source.TileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) {
return this.tileCache_[key];
} else {
var tileUrl = this.getTileCoordUrl(tileCoord);
var tile;
if (goog.isDef(tileUrl)) {
tile = new ol.Tile(tileCoord, tileUrl, this.crossOrigin_);
} else {
tile = null;
}
this.tileCache_[key] = tile;
return tile;
}
};
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {string|undefined} Tile URL.
*/
ol.source.TileSource.prototype.getTileCoordUrl = function(tileCoord) {
return this.tileUrlFunction(tileCoord);
};
ol.source.TileSource.prototype.getTile = goog.abstractMethod;
/**

View File

@@ -11,7 +11,7 @@ goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -31,7 +31,7 @@ ol.source.XYZOptions;
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.XYZOptions} xyzOptions XYZ options.
*/
ol.source.XYZ = function(xyzOptions) {
@@ -110,4 +110,4 @@ ol.source.XYZ = function(xyzOptions) {
});
};
goog.inherits(ol.source.XYZ, ol.source.TileSource);
goog.inherits(ol.source.XYZ, ol.source.ImageTileSource);

View File

@@ -24,10 +24,8 @@ ol.TileState = {
* @constructor
* @extends {goog.events.EventTarget}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
*/
ol.Tile = function(tileCoord, src, crossOrigin) {
ol.Tile = function(tileCoord) {
goog.base(this);
@@ -37,39 +35,10 @@ ol.Tile = function(tileCoord, src, crossOrigin) {
this.tileCoord = tileCoord;
/**
* Image URI
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @protected
* @type {ol.TileState}
*/
this.state_ = ol.TileState.IDLE;
/**
* @private
* @type {Image}
*/
this.image_ = new Image();
if (!goog.isNull(crossOrigin)) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Object.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
this.imageListenerKeys_ = null;
this.state = ol.TileState.IDLE;
};
goog.inherits(ol.Tile, goog.events.EventTarget);
@@ -85,24 +54,16 @@ ol.Tile.prototype.dispatchChangeEvent = function() {
/**
* @param {Object=} opt_context Object.
* @return {Image} Image.
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
ol.Tile.prototype.getImage = function(opt_context) {
if (goog.isDef(opt_context)) {
var image;
var key = goog.getUid(opt_context);
if (key in this.imageByContext_) {
return this.imageByContext_[key];
} else if (goog.object.isEmpty(this.imageByContext_)) {
image = this.image_;
} else {
image = /** @type {Image} */ (this.image_.cloneNode(false));
}
this.imageByContext_[key] = image;
return image;
} else {
return this.image_;
}
ol.Tile.prototype.getImage = goog.abstractMethod;
/**
* @return {string} Key.
*/
ol.Tile.prototype.getKey = function() {
return goog.getUid(this).toString();
};
@@ -110,59 +71,10 @@ ol.Tile.prototype.getImage = function(opt_context) {
* @return {ol.TileState} State.
*/
ol.Tile.prototype.getState = function() {
return this.state_;
return this.state;
};
/**
* Tracks loading or read errors.
*
* @private
*/
ol.Tile.prototype.handleImageError_ = function() {
this.state_ = ol.TileState.ERROR;
this.unlistenImage_();
};
/**
* Tracks successful image load.
*
* @private
*/
ol.Tile.prototype.handleImageLoad_ = function() {
this.state_ = ol.TileState.LOADED;
this.unlistenImage_();
this.dispatchChangeEvent();
};
/**
* Load not yet loaded URI.
*/
ol.Tile.prototype.load = function() {
if (this.state_ == ol.TileState.IDLE) {
this.state_ = ol.TileState.LOADING;
goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
this.imageListenerKeys_ = [
goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
this.handleImageError_, false, this),
goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
this.handleImageLoad_, false, this)
];
this.image_.src = this.src_;
}
};
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ol.Tile.prototype.unlistenImage_ = function() {
goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
this.imageListenerKeys_ = null;
};
ol.Tile.prototype.load = goog.abstractMethod;