diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js new file mode 100644 index 0000000000..c259b02b99 --- /dev/null +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -0,0 +1,220 @@ +goog.provide('ol.renderer.webgl.ImageLayer'); + +goog.require('goog.vec.Mat4'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); +goog.require('ol.ViewHint'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.renderer.webgl.Layer'); + + + +/** + * @constructor + * @extends {ol.renderer.webgl.Layer} + * @param {ol.renderer.Map} mapRenderer Map renderer. + * @param {ol.layer.ImageLayer} imageLayer Tile layer. + */ +ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) { + + goog.base(this, mapRenderer, imageLayer); + + /** + * The last rendered image. + * @private + * @type {?ol.Image} + */ + this.image_ = null; + + /** + * The last rendered texture. + * @private + * @type {WebGLTexture} + */ + this.texture_ = null; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.texCoordMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.vertexCoordMatrix_ = goog.vec.Mat4.createNumber(); + +}; +goog.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer); + + +/** + * @private + * @param {ol.Image} image Image. + * @return {WebGLTexture} Texture. + */ +ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) { + + // We meet the conditions to work with non-power of two textures. + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support + // http://learningwebgl.com/blog/?p=2101 + + var imageElement = image.getImageElement(this); + var gl = this.getMapRenderer().getGL(); + + 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, imageElement); + + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, + goog.webgl.CLAMP_TO_EDGE); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, + goog.webgl.CLAMP_TO_EDGE); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR); + + return texture; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.disposeInternal = function() { + var mapRenderer = this.getMapRenderer(); + var gl = mapRenderer.getGL(); + if (!gl.isContextLost()) { + gl.deleteTexture(this.texture_); + } + goog.base(this, 'disposeInternal'); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getTexCoordMatrix = function() { + return this.texCoordMatrix_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getTexture = function() { + return this.texture_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getVertexCoordMatrix = function() { + return this.vertexCoordMatrix_; +}; + + +/** + * @return {ol.layer.ImageLayer} Tile layer. + */ +ol.renderer.webgl.ImageLayer.prototype.getImageLayer = function() { + return /** @type {ol.layer.ImageLayer} */ (this.getLayer()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.handleWebGLContextLost = function() { + this.texture_ = null; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.renderFrame = + function(frameState, layerState) { + + var gl = this.getMapRenderer().getGL(); + + var view2DState = frameState.view2DState; + var viewCenter = view2DState.center; + var viewResolution = view2DState.resolution; + var viewRotation = view2DState.rotation; + + var image = this.image_; + var texture = this.texture_; + var imageLayer = this.getImageLayer(); + var imageSource = imageLayer.getImageSource(); + + var hints = frameState.viewHints; + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { + var image_ = imageSource.getImage(frameState.extent, viewResolution); + var imageState = image_.getState(); + var animate = false; + if (imageState == ol.ImageState.ERROR) { + // pass + } else if (imageState == ol.ImageState.IDLE) { + animate = true; + image_.load(); + } else if (imageState == ol.ImageState.LOADING) { + animate = true; + } else if (imageState == ol.ImageState.LOADED) { + image = image_; + texture = this.createTexture_(image_); + if (!goog.isNull(this.texture_)) { + frameState.postRenderFunctions.push( + goog.partial(function(gl, texture) { + if (!gl.isContextLost()) { + gl.deleteTexture(texture); + } + }, gl, this.texture_)); + } + } + if (animate) { + frameState.animate = true; + } + } + + if (!goog.isNull(image)) { + goog.asserts.assert(!goog.isNull(texture)); + + var canvas = this.getMapRenderer().getCanvas(); + + var canvasExtentWidth = canvas.width * viewResolution; + var canvasExtentHeight = canvas.height * viewResolution; + + var imageExtent = image.getExtent(); + + var vertexCoordMatrix = this.vertexCoordMatrix_; + goog.vec.Mat4.makeIdentity(vertexCoordMatrix); + goog.vec.Mat4.scale(vertexCoordMatrix, + 2 / canvasExtentWidth, 2 / canvasExtentHeight, 1); + goog.vec.Mat4.rotateZ(vertexCoordMatrix, -viewRotation); + goog.vec.Mat4.translate(vertexCoordMatrix, + imageExtent.minX - viewCenter.x, + imageExtent.minY - viewCenter.y, + 0); + goog.vec.Mat4.scale(vertexCoordMatrix, + imageExtent.getWidth() / 2, imageExtent.getHeight() / 2, 1); + goog.vec.Mat4.translate(vertexCoordMatrix, 1, 1, 0); + + // Translate and scale to flip the Y coord. + var texCoordMatrix = this.texCoordMatrix_; + goog.vec.Mat4.makeIdentity(texCoordMatrix); + goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1); + goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0); + + this.image_ = image; + this.texture_ = texture; + } +}; diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 238462410f..067a4e054c 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -97,6 +97,12 @@ ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = goog.abstractMethod; ol.renderer.webgl.Layer.prototype.getTexture = goog.abstractMethod; +/** + * @return {!goog.vec.Mat4.Number} Matrix. + */ +ol.renderer.webgl.Layer.prototype.getVertexCoordMatrix = goog.abstractMethod; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index f7a553f3f2..62b03fc489 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -14,9 +14,11 @@ goog.require('goog.webgl'); goog.require('ol.FrameState'); goog.require('ol.Size'); goog.require('ol.Tile'); +goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.webgl.FragmentShader'); +goog.require('ol.renderer.webgl.ImageLayer'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VertexShader'); goog.require('ol.structs.LRUCache'); @@ -79,11 +81,12 @@ ol.renderer.webgl.map.shader.Vertex = function() { 'attribute vec2 aTexCoord;', '', 'uniform mat4 uTexCoordMatrix;', + 'uniform mat4 uVertexCoordMatrix;', '', 'varying vec2 vTexCoord;', '', 'void main(void) {', - ' gl_Position = vec4(aPosition, 0., 1.);', + ' gl_Position = uVertexCoordMatrix * vec4(aPosition, 0., 1.);', ' vTexCoord = (uTexCoordMatrix * vec4(aTexCoord, 0., 1.)).st;', '}' ].join('\n')); @@ -159,7 +162,8 @@ ol.renderer.webgl.Map = function(container, map) { * uColorMatrix: WebGLUniformLocation, * uOpacity: WebGLUniformLocation, * uTexture: WebGLUniformLocation, - * uTexCoordMatrix: WebGLUniformLocation}|null} + * uTexCoordMatrix: WebGLUniformLocation, + * uVertexCoordMatrix: WebGLUniformLocation}|null} */ this.locations_ = null; @@ -270,12 +274,15 @@ ol.renderer.webgl.Map.prototype.bindTileTexture = * @inheritDoc */ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) { + var layerRenderer = null; if (layer instanceof ol.layer.TileLayer) { - return new ol.renderer.webgl.TileLayer(this, layer); + layerRenderer = new ol.renderer.webgl.TileLayer(this, layer); + } else if (layer instanceof ol.layer.ImageLayer) { + layerRenderer = new ol.renderer.webgl.ImageLayer(this, layer); } else { goog.asserts.assert(false); - return null; } + return layerRenderer; }; @@ -519,6 +526,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { aTexCoord: gl.getAttribLocation(program, 'aTexCoord'), uColorMatrix: gl.getUniformLocation(program, 'uColorMatrix'), uTexCoordMatrix: gl.getUniformLocation(program, 'uTexCoordMatrix'), + uVertexCoordMatrix: gl.getUniformLocation(program, 'uVertexCoordMatrix'), uOpacity: gl.getUniformLocation(program, 'uOpacity'), uTexture: gl.getUniformLocation(program, 'uTexture') }; @@ -555,6 +563,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { gl.uniformMatrix4fv( this.locations_.uTexCoordMatrix, false, layerRenderer.getTexCoordMatrix()); + gl.uniformMatrix4fv( + this.locations_.uVertexCoordMatrix, false, + layerRenderer.getVertexCoordMatrix()); gl.uniformMatrix4fv( this.locations_.uColorMatrix, false, layerRenderer.getColorMatrix()); gl.uniform1f(this.locations_.uOpacity, layer.getOpacity()); diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 8f1dab302a..360f8f8d82 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -141,6 +141,12 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) { */ this.texCoordMatrix_ = goog.vec.Mat4.createNumber(); + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.vertexCoordMatrix_ = goog.vec.Mat4.createNumberIdentity(); + /** * @private * @type {ol.TileRange} @@ -237,6 +243,14 @@ ol.renderer.webgl.TileLayer.prototype.getTexture = function() { }; +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.getVertexCoordMatrix = function() { + return this.vertexCoordMatrix_; +}; + + /** * @return {ol.layer.TileLayer} Tile layer. */