From f280faff67499ebf9822718d708502442c154b06 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 22 Jul 2012 02:06:31 +0200 Subject: [PATCH] Add hue and saturation controls, thanks @evanw --- src/ol/base/layer.js | 36 +++++++++++++++++++++++++++ src/ol/layerrenderer/layerrenderer.js | 20 +++++++++++++++ src/ol/webgl/layerrenderer.js | 16 ++++++++++++ src/ol/webgl/map.js | 36 ++++++++++++++++++++++++--- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/ol/base/layer.js b/src/ol/base/layer.js index d684502eea..ef9de59107 100644 --- a/src/ol/base/layer.js +++ b/src/ol/base/layer.js @@ -9,7 +9,9 @@ goog.require('ol.Store'); * @enum {string} */ ol.LayerProperty = { + HUE: 'hue', OPACITY: 'opacity', + SATURATION: 'saturation', VISIBLE: 'visible' }; @@ -32,7 +34,9 @@ ol.Layer = function(store, opt_values) { this.store_ = store; this.setVisible(true); + this.setHue(0); this.setOpacity(1); + this.setSaturation(0); if (goog.isDef(opt_values)) { this.setValues(opt_values); @@ -42,6 +46,14 @@ ol.Layer = function(store, opt_values) { goog.inherits(ol.Layer, ol.Object); +/** + * @return {number} Hue. + */ +ol.Layer.prototype.getHue = function() { + return /** @type {number} */ this.get(ol.LayerProperty.HUE); +}; + + /** * @return {number} Opacity. */ @@ -51,6 +63,14 @@ ol.Layer.prototype.getOpacity = function() { }; +/** + * @return {number} Saturation. + */ +ol.Layer.prototype.getSaturation = function() { + return /** @type {number} */ this.get(ol.LayerProperty.SATURATION); +}; + + /** * @return {ol.Store} Store. */ @@ -68,6 +88,14 @@ ol.Layer.prototype.getVisible = function() { }; +/** + * @param {number} hue Hue. + */ +ol.Layer.prototype.setHue = function(hue) { + this.set(ol.LayerProperty.HUE, hue); +}; + + /** * @param {number} opacity Opacity. */ @@ -76,6 +104,14 @@ ol.Layer.prototype.setOpacity = function(opacity) { }; +/** + * @param {number} saturation Saturation. + */ +ol.Layer.prototype.setSaturation = function(saturation) { + this.set(ol.LayerProperty.SATURATION, saturation); +}; + + /** * @param {boolean} visible Visible. */ diff --git a/src/ol/layerrenderer/layerrenderer.js b/src/ol/layerrenderer/layerrenderer.js index a7cc644099..74b6d85929 100644 --- a/src/ol/layerrenderer/layerrenderer.js +++ b/src/ol/layerrenderer/layerrenderer.js @@ -29,10 +29,18 @@ ol.LayerRenderer = function(map, layer) { */ this.layer_ = layer; + goog.events.listen(this.layer_, + ol.Object.getChangedEventType(ol.LayerProperty.HUE), + this.handleLayerHueChange, false, this); + goog.events.listen(this.layer_, ol.Object.getChangedEventType(ol.LayerProperty.OPACITY), this.handleLayerOpacityChange, false, this); + goog.events.listen(this.layer_, + ol.Object.getChangedEventType(ol.LayerProperty.SATURATION), + this.handleLayerSaturationChange, false, this); + goog.events.listen(this.layer_, ol.Object.getChangedEventType(ol.LayerProperty.VISIBLE), this.handleLayerVisibleChange, false, this); @@ -57,12 +65,24 @@ ol.LayerRenderer.prototype.getMap = function() { }; +/** + * @protected + */ +ol.LayerRenderer.prototype.handleLayerHueChange = goog.nullFunction; + + /** * @protected */ ol.LayerRenderer.prototype.handleLayerOpacityChange = goog.nullFunction; +/** + * @protected + */ +ol.LayerRenderer.prototype.handleLayerSaturationChange = goog.nullFunction; + + /** * @protected */ diff --git a/src/ol/webgl/layerrenderer.js b/src/ol/webgl/layerrenderer.js index 6e0a3bdbb1..91eb3e45b0 100644 --- a/src/ol/webgl/layerrenderer.js +++ b/src/ol/webgl/layerrenderer.js @@ -56,6 +56,14 @@ ol.webgl.LayerRenderer.prototype.getMap = function() { ol.webgl.LayerRenderer.prototype.getMatrix = goog.abstractMethod; +/** + * @inheritDoc + */ +ol.webgl.LayerRenderer.prototype.handleLayerHueChange = function() { + this.dispatchChangeEvent(); +}; + + /** * @inheritDoc */ @@ -64,6 +72,14 @@ ol.webgl.LayerRenderer.prototype.handleLayerOpacityChange = function() { }; +/** + * @inheritDoc + */ +ol.webgl.LayerRenderer.prototype.handleLayerSaturationChange = function() { + this.dispatchChangeEvent(); +}; + + /** * @inheritDoc */ diff --git a/src/ol/webgl/map.js b/src/ol/webgl/map.js index 0d2ac8292e..77ac1dee4c 100644 --- a/src/ol/webgl/map.js +++ b/src/ol/webgl/map.js @@ -34,22 +34,46 @@ ol.DEBUG_WEBGL = false; /** * @constructor * @extends {ol.webgl.shader.Fragment} + * @see https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/huesaturation.js */ ol.webgl.map.shader.Fragment = function() { goog.base(this, [ 'precision mediump float;', '', 'uniform mat4 uMatrix;', + 'uniform float uHue;', 'uniform float uOpacity;', + 'uniform float uSaturation;', 'uniform sampler2D uTexture;', '', 'varying vec2 vTexCoord;', '', 'void main(void) {', ' vec4 texCoord = uMatrix * vec4(vTexCoord, 0., 1.);', - ' vec4 srcColor = texture2D(uTexture, texCoord.st);', - ' gl_FragColor.rgb = srcColor.rgb;', - ' gl_FragColor.a = srcColor.a * uOpacity;', + ' vec4 color = texture2D(uTexture, texCoord.st);', + '', + ' float angle = uHue * 3.14159265;', + ' float s = sin(angle), c = cos(angle);', + ' vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c)', + ' + 1.0) / 3.0;', + ' float len = length(color.rgb);', + ' color.rgb = vec3(', + ' dot(color.rgb, weights.xyz),', + ' dot(color.rgb, weights.zxy),', + ' dot(color.rgb, weights.yzx)', + ' );', + ' ', + ' float average = (color.r + color.g + color.b) / 3.0;', + ' if (uSaturation > 0.0) {', + ' color.rgb += (average - color.rgb)', + ' * (1.0 - 1.0 / (1.001 - uSaturation));', + ' } else if (uSaturation < 0.0) {', + ' color.rgb += (average - color.rgb) * (-uSaturation);', + ' }', + '', + ' color.a = color.a * uOpacity;', + '', + ' gl_FragColor = color;', '}' ].join('\n')); }; @@ -132,8 +156,10 @@ ol.webgl.Map = function(target, opt_values) { * @private * @type {{aPosition: number, * aTexCoord: number, + * uHue: WebGLUniformLocation, * uMatrix: WebGLUniformLocation, * uOpacity: WebGLUniformLocation, + * uSaturation: WebGLUniformLocation, * uTexture: WebGLUniformLocation}|null} */ this.locations_ = null; @@ -458,8 +484,10 @@ ol.webgl.Map.prototype.renderInternal = function() { this.locations_ = { aPosition: gl.getAttribLocation(program, 'aPosition'), aTexCoord: gl.getAttribLocation(program, 'aTexCoord'), + uHue: gl.getUniformLocation(program, 'uHue'), uMatrix: gl.getUniformLocation(program, 'uMatrix'), uOpacity: gl.getUniformLocation(program, 'uOpacity'), + uSaturation: gl.getUniformLocation(program, 'uSaturation'), uTexture: gl.getUniformLocation(program, 'uTexture') }; } @@ -489,7 +517,9 @@ ol.webgl.Map.prototype.renderInternal = function() { this.forEachVisibleLayer(function(layer, layerRenderer) { gl.uniformMatrix4fv( this.locations_.uMatrix, false, layerRenderer.getMatrix()); + gl.uniform1f(this.locations_.uHue, layer.getHue()); gl.uniform1f(this.locations_.uOpacity, layer.getOpacity()); + gl.uniform1f(this.locations_.uSaturation, layer.getSaturation()); gl.bindTexture(goog.webgl.TEXTURE_2D, layerRenderer.getTexture()); gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); }, this);