/* * AberrationFilter * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module EaselJS */ (function() { "use strict"; // constructor: /** * Separates and pushes each of the colour channels apart. I.E. shift the red channel slightly left. * Allows specifying the direction and the ammount it affects each channel. Great for computer glitches and VCR like * effects. * * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. * @class AberrationFilter * @extends Filter * @constructor * @param {Number} [xDir=0] Movement in x at a multiplier of 1, specified in pixels. * @param {Number} [yDir=0] Movement in y at a multiplier of 1, specified in pixels. * @param {Number} [redMultiplier=0] Multiplier for the movement of the Red channel. Negative values allowed. * @param {Number} [greenMultiplier=0] Multiplier for the movement of the Green channel. Negative values allowed. * @param {Number} [blueMultiplier=0] Multiplier for the movement of the Blue channel. Negative values allowed. * @param {Number} [originalMix=0] Amount of original image to keep, 0-1. * @param {Boolean} [alphaMax=false] Calculate combined alpha using maximum alpha available. Creates a stronger image. **/ function AberrationFilter(xDir, yDir, redMultiplier, greenMultiplier, blueMultiplier, originalMix, alphaMax) { this.Filter_constructor(); // public properties: this.xDir = Number(xDir) || 0; this.yDir = Number(yDir) || 0; this.redMultiplier = Number(redMultiplier) || 0; this.greenMultiplier = Number(greenMultiplier) || 0; this.blueMultiplier = Number(blueMultiplier) || 0; this.originalMix = Math.min(Math.max(originalMix, 0), 1) || 0; this._alphaMax = Boolean(alphaMax); this.FRAG_SHADER_BODY = ( "uniform vec2 uColorDirection;" + "uniform vec3 uColorMultiplier;" + "uniform vec2 uExtraProps;" + "void main(void) {" + "vec4 sample = texture2D(" + "uSampler, " + "vTextureCoord" + ");" + "vec4 rSample = texture2D(" + "uSampler, " + "vTextureCoord + (uColorDirection * uColorMultiplier.r)" + ");" + "vec4 gSample = texture2D(" + "uSampler, " + "vTextureCoord + (uColorDirection * uColorMultiplier.g)" + ");" + "vec4 bSample = texture2D(" + "uSampler, " + "vTextureCoord + (uColorDirection * uColorMultiplier.b)" + ");" + "float newAlpha = " + (alphaMax ? "max(rSample.a, max(gSample.a, max(bSample.a, sample.a)))" : "(rSample.a + gSample.a + bSample.a) / 3.0" ) + ";" + "vec4 result = vec4(" + "min(1.0, rSample.r/(rSample.a+0.00001)) * newAlpha, " + "min(1.0, gSample.g/(gSample.a+0.00001)) * newAlpha, " + "min(1.0, bSample.b/(bSample.a+0.00001)) * newAlpha, " + "newAlpha" + ");" + "gl_FragColor = mix(result, sample, uExtraProps[0]*sample.a);" + "}" ); } var p = createjs.extend(AberrationFilter, createjs.Filter); // public methods: p.shaderParamSetup = function(gl, stage, shaderProgram) { gl.uniform2f( gl.getUniformLocation(shaderProgram, "uColorDirection"), this.xDir*(1/stage._viewportWidth), this.yDir*(1/-stage._viewportHeight) ); gl.uniform3f( gl.getUniformLocation(shaderProgram, "uColorMultiplier"), -this.redMultiplier, -this.greenMultiplier, -this.blueMultiplier ); gl.uniform2f( gl.getUniformLocation(shaderProgram, "uExtraProps"), this.originalMix, 0 ); }; // private methods: p._applyFilter = function(imageData) { var refPixels = imageData.data.slice(); var outPixels = imageData.data; var width = imageData.width; var height = imageData.height; var offset, pixel; for (var i=0; i<height; i++) { offset = i*width; for (var j=0; j<width; j++) { pixel = (offset+j)*4; var redX = j+( (this.xDir*-this.redMultiplier) |0), redY = i+( (this.yDir*-this.redMultiplier) |0); var grnX = j+( (this.xDir*-this.greenMultiplier) |0), grnY = i+( (this.yDir*-this.greenMultiplier) |0); var bluX = j+( (this.xDir*-this.blueMultiplier) |0), bluY = i+( (this.yDir*-this.blueMultiplier) |0); if (redX < 0) { redX = 0; } if (redX >= width) { redX = width-1; } if (redY < 0) { redY = 0; } if (redY >= height) { redY = height-1; } if (grnX < 0) { grnX = 0; } if (grnX >= width) { grnX = width-1; } if (grnY < 0) { grnY = 0; } if (grnY >= height) { grnY = height-1; } if (bluX < 0) { bluX = 0; } if (bluX >= width) { bluX = width-1; } if (bluY < 0) { bluY = 0; } if (bluY >= height) { bluY = height-1; } var redPixel = ((redY*width)+redX)*4; var grnPixel = ((grnY*width)+grnX)*4; var bluPixel = ((bluY*width)+bluX)*4; outPixels[pixel] = refPixels[redPixel]; outPixels[pixel+1] = refPixels[grnPixel+1]; outPixels[pixel+2] = refPixels[bluPixel+2]; outPixels[pixel+3] = this._alphaMax ? Math.max(refPixels[redPixel+3], refPixels[grnPixel+3], refPixels[bluPixel+3]) : (refPixels[redPixel+3] + refPixels[grnPixel+3] + refPixels[bluPixel+3]) / 3; } } return true; }; createjs.AberrationFilter = createjs.promote(AberrationFilter, "Filter"); }());