#

API Docs for: 2.0.0
Show: Inherited Protected Private Deprecated

# File: src/pixi/renderers/webgl/utils/WebGLFilterManager.js

    /**
     * @author Mat Groves http://matgroves.com/ @Doormat23
     */

    /**
    * @class WebGLFilterManager
    * @constructor
    */
    PIXI.WebGLFilterManager = function()
    {
        /**
         * @property filterStack
         * @type Array
         */
        this.filterStack = [];

        /**
         * @property offsetX
         * @type Number
         */
        this.offsetX = 0;

        /**
         * @property offsetY
         * @type Number
         */
        this.offsetY = 0;
    };

    PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager;

    /**
    * Initialises the context and the properties.
    *
    * @method setContext
    * @param gl {WebGLContext} the current WebGL drawing context
    */
    PIXI.WebGLFilterManager.prototype.setContext = function(gl)
    {
        this.gl = gl;
        this.texturePool = [];

        this.initShaderBuffers();
    };

    /**
    * @method begin
    * @param renderSession {RenderSession}
    * @param buffer {ArrayBuffer}
    */
    PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
    {
        this.renderSession = renderSession;
        this.defaultShader = renderSession.shaderManager.defaultShader;

        var projection = this.renderSession.projection;
        this.width = projection.x * 2;
        this.height = -projection.y * 2;
        this.buffer = buffer;
    };

    /**
    * Applies the filter and adds it to the current filter stack.
    *
    * @method pushFilter
    * @param filterBlock {Object} the filter that will be pushed to the current filter stack
    */
    PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
    {
        var gl = this.gl;

        var projection = this.renderSession.projection;
        var offset = this.renderSession.offset;

        filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds();

        // filter program
        // OPTIMISATION - the first filter is free if its a simple color change?
        this.filterStack.push(filterBlock);

        var filter = filterBlock.filterPasses[0];

        this.offsetX += filterBlock._filterArea.x;
        this.offsetY += filterBlock._filterArea.y;

        var texture = this.texturePool.pop();
        if(!texture)
        {
            texture = new PIXI.FilterTexture(this.gl, this.width, this.height);
        }
        else
        {
            texture.resize(this.width, this.height);
        }

        gl.bindTexture(gl.TEXTURE_2D,  texture.texture);

        var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea;

        var padding = filter.padding;
        filterArea.x -= padding;
        filterArea.y -= padding;
        filterArea.width += padding * 2;
        filterArea.height += padding * 2;

        // cap filter to screen size..
        if(filterArea.x < 0)filterArea.x = 0;
        if(filterArea.width > this.width)filterArea.width = this.width;
        if(filterArea.y < 0)filterArea.y = 0;
        if(filterArea.height > this.height)filterArea.height = this.height;

        //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,  filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);

        // set view port
        gl.viewport(0, 0, filterArea.width, filterArea.height);

        projection.x = filterArea.width/2;
        projection.y = -filterArea.height/2;

        offset.x = -filterArea.x;
        offset.y = -filterArea.y;

        // update projection
        // now restore the regular shader..
        // this.renderSession.shaderManager.setShader(this.defaultShader);
        //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
        //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y);

        gl.colorMask(true, true, true, true);
        gl.clearColor(0,0,0, 0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        filterBlock._glFilterTexture = texture;

    };

    /**
    * Removes the last filter from the filter stack and doesn't return it.
    *
    * @method popFilter
    */
    PIXI.WebGLFilterManager.prototype.popFilter = function()
    {
        var gl = this.gl;
        var filterBlock = this.filterStack.pop();
        var filterArea = filterBlock._filterArea;
        var texture = filterBlock._glFilterTexture;
        var projection = this.renderSession.projection;
        var offset = this.renderSession.offset;

        if(filterBlock.filterPasses.length > 1)
        {
            gl.viewport(0, 0, filterArea.width, filterArea.height);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

            this.vertexArray[0] = 0;
            this.vertexArray[1] = filterArea.height;

            this.vertexArray[2] = filterArea.width;
            this.vertexArray[3] = filterArea.height;

            this.vertexArray[4] = 0;
            this.vertexArray[5] = 0;

            this.vertexArray[6] = filterArea.width;
            this.vertexArray[7] = 0;

            gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
            // now set the uvs..
            this.uvArray[2] = filterArea.width/this.width;
            this.uvArray[5] = filterArea.height/this.height;
            this.uvArray[6] = filterArea.width/this.width;
            this.uvArray[7] = filterArea.height/this.height;

            gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

            var inputTexture = texture;
            var outputTexture = this.texturePool.pop();
            if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height);
            outputTexture.resize(this.width, this.height);

            // need to clear this FBO as it may have some left over elements from a previous filter.
            gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.disable(gl.BLEND);

            for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
            {
                var filterPass = filterBlock.filterPasses[i];

                gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );

                // set texture
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);

                // draw texture..
                //filterPass.applyFilterPass(filterArea.width, filterArea.height);
                this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);

                // swap the textures..
                var temp = inputTexture;
                inputTexture = outputTexture;
                outputTexture = temp;
            }

            gl.enable(gl.BLEND);

            texture = inputTexture;
            this.texturePool.push(outputTexture);
        }

        var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];

        this.offsetX -= filterArea.x;
        this.offsetY -= filterArea.y;

        var sizeX = this.width;
        var sizeY = this.height;

        var offsetX = 0;
        var offsetY = 0;

        var buffer = this.buffer;

        // time to render the filters texture to the previous scene
        if(this.filterStack.length === 0)
        {
            gl.colorMask(true, true, true, true);//this.transparent);
        }
        else
        {
            var currentFilter = this.filterStack[this.filterStack.length-1];
            filterArea = currentFilter._filterArea;

            sizeX = filterArea.width;
            sizeY = filterArea.height;

            offsetX = filterArea.x;
            offsetY = filterArea.y;

            buffer =  currentFilter._glFilterTexture.frameBuffer;
        }

        // TODO need to remove these global elements..
        projection.x = sizeX/2;
        projection.y = -sizeY/2;

        offset.x = offsetX;
        offset.y = offsetY;

        filterArea = filterBlock._filterArea;

        var x = filterArea.x-offsetX;
        var y = filterArea.y-offsetY;

        // update the buffers..
        // make sure to flip the y!
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

        this.vertexArray[0] = x;
        this.vertexArray[1] = y + filterArea.height;

        this.vertexArray[2] = x + filterArea.width;
        this.vertexArray[3] = y + filterArea.height;

        this.vertexArray[4] = x;
        this.vertexArray[5] = y;

        this.vertexArray[6] = x + filterArea.width;
        this.vertexArray[7] = y;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);

        this.uvArray[2] = filterArea.width/this.width;
        this.uvArray[5] = filterArea.height/this.height;
        this.uvArray[6] = filterArea.width/this.width;
        this.uvArray[7] = filterArea.height/this.height;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

        gl.viewport(0, 0, sizeX, sizeY);

        // bind the buffer
        gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );

        // set the blend mode!
        //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)

        // set texture
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture.texture);

        // apply!
        this.applyFilterPass(filter, filterArea, sizeX, sizeY);

        // now restore the regular shader.. should happen automatically now..
        // this.renderSession.shaderManager.setShader(this.defaultShader);
        // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
        // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);

        // return the texture to the pool
        this.texturePool.push(texture);
        filterBlock._glFilterTexture = null;
    };

    /**
    * Applies the filter to the specified area.
    *
    * @method applyFilterPass
    * @param filter {AbstractFilter} the filter that needs to be applied
    * @param filterArea {texture} TODO - might need an update
    * @param width {Number} the horizontal range of the filter
    * @param height {Number} the vertical range of the filter
    */
    PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
    {
        // use program
        var gl = this.gl;
        var shader = filter.shaders[gl.id];

        if(!shader)
        {
            shader = new PIXI.PixiShader(gl);

            shader.fragmentSrc = filter.fragmentSrc;
            shader.uniforms = filter.uniforms;
            shader.init();

            filter.shaders[gl.id] = shader;
        }

        // set the shader
        this.renderSession.shaderManager.setShader(shader);

    //    gl.useProgram(shader.program);

        gl.uniform2f(shader.projectionVector, width/2, -height/2);
        gl.uniform2f(shader.offsetVector, 0,0);

        if(filter.uniforms.dimensions)
        {
            filter.uniforms.dimensions.value[0] = this.width;//width;
            filter.uniforms.dimensions.value[1] = this.height;//height;
            filter.uniforms.dimensions.value[2] = this.vertexArray[0];
            filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
        }

        shader.syncUniforms();

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
        gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

        // draw the filter...
        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

        this.renderSession.drawCount++;
    };

    /**
    * Initialises the shader buffers.
    *
    * @method initShaderBuffers
    */
    PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
    {
        var gl = this.gl;

        // create some buffers
        this.vertexBuffer = gl.createBuffer();
        this.uvBuffer = gl.createBuffer();
        this.colorBuffer = gl.createBuffer();
        this.indexBuffer = gl.createBuffer();

        // bind and upload the vertexs..
        // keep a reference to the vertexFloatData..
        this.vertexArray = new Float32Array([0.0, 0.0,
                                             1.0, 0.0,
                                             0.0, 1.0,
                                             1.0, 1.0]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);

        // bind and upload the uv buffer
        this.uvArray = new Float32Array([0.0, 0.0,
                                         1.0, 0.0,
                                         0.0, 1.0,
                                         1.0, 1.0]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW);

        this.colorArray = new Float32Array([1.0, 0xFFFFFF,
                                            1.0, 0xFFFFFF,
                                            1.0, 0xFFFFFF,
                                            1.0, 0xFFFFFF]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW);

        // bind and upload the index
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW);

    };

    /**
    * Destroys the filter and removes it from the filter stack.
    *
    * @method destroy
    */
    PIXI.WebGLFilterManager.prototype.destroy = function()
    {
        var gl = this.gl;

        this.filterStack = null;

        this.offsetX = 0;
        this.offsetY = 0;

        // destroy textures
        for (var i = 0; i < this.texturePool.length; i++) {
            this.texturePool[i].destroy();
        }

        this.texturePool = null;

        //destroy buffers..
        gl.deleteBuffer(this.vertexBuffer);
        gl.deleteBuffer(this.uvBuffer);
        gl.deleteBuffer(this.colorBuffer);
        gl.deleteBuffer(this.indexBuffer);
    };