In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

3x3 Convolution Matrices in AGAL / Stage3D

It is what it is...
Get Adobe Flash player
by devon_o 12 Sep 2012
/**
 * Copyright devon_o ( http://wonderfl.net/user/devon_o )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/uBTI
 */

package
{
    /**
     * A look at performing 3x3 matrix convolutions with AGAL / Stage3D
     * 
     * 
     * @author Devon O.
     */
    
    import com.adobe.utils.*;
    import com.bit101.components.ComboBox;
    import com.bit101.components.HUISlider;
    import flash.display.*;
    import flash.display3D.*;
    import flash.display3D.textures.Texture;
    import flash.events.*;
    import flash.geom.Matrix3D;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    
    
    [SWF(width="512", height="512", frameRate="60", backgroundColor="#000000")]
    public class Convolution extends Sprite
    {
        private var mContext3d:Context3D;
        private var mVertBuffer:VertexBuffer3D;
        private var mIndexBuffer:IndexBuffer3D; 
        private var mProgram:Program3D;
        private var mTexture:Texture;
        private var mTextureData:BitmapData;
        
        private var mMatrix:Matrix3D = new Matrix3D();
        
        private var mIdentity:Array = 
            [
                new <Number>[ 0, 0, 0,     1],
                new <Number>[ 0, 1, 0,     1],
                new <Number>[ 0, 0, 0,     1]
            ];
            
        private var mGaussianBlur1:Array = 
            [
                new <Number>[ 0.045, 0.122, 0.045,     1],
                new <Number>[ 0.122, 0.332, 0.122,     1],
                new <Number>[ 0.045, 0.122, 0.045,     1]
            ];
        
        private var mGaussianBlur2:Array = 
            [
                new <Number>[ 0, 1, 0,     1],
                new <Number>[ 1, 1, 1,     1],
                new <Number>[ 0, 1, 0,     1]
            ];
        
        private var mSharpen:Array = 
            [
                new <Number>[ -1, -1, -1,     1],
                new <Number>[ -1, 16, -1,     1],
                new <Number>[ -1, -1, -1,     1]
            ];
        
        private var mSharpen2:Array = 
            [
                new <Number>[ 1, 1, 1,     1],
                new <Number>[ 1, -7, 1,     1],
                new <Number>[ 1, 1, 1,     1]
            ];
                
        private var mEdgeDetect1:Array = 
            [
                new <Number>[ -0.125, -0.125, -0.125,     1],
                new <Number>[ -0.125,  1,     -0.125,     1],
                new <Number>[ -0.125, -0.125, -0.125,     1]
            ];
            
        private var mEdgeDetect2:Array = 
            [
                new <Number>[  0,  1,  0,     1],
                new <Number>[  1, -4,  1,     1],
                new <Number>[  0,  1,  0,     1]
            ];
            
        private var mSobelVert:Array = 
            [
                new <Number>[  -1,  0,  1,     1],
                new <Number>[  -2,  0,  2,     1],
                new <Number>[  -1,  0,  1,     1]
            ];
            
        private var mSobelHorz:Array = 
            [
                new <Number>[  -1, -2,  -1,     1],
                new <Number>[  0,   0,   0,     1],
                new <Number>[  1,   2,   1,     1]
            ];
            
        private var mBoxBlur:Array = 
            [
                new <Number>[ 0.111, 0.111, 0.111,     1],
                new <Number>[ 0.111, 0.111, 0.111,     1],
                new <Number>[ 0.111, 0.111, 0.111,     1]
            ];
            
        private var mTriangleBlur:Array = 
            [
                new <Number>[ 0.0625, 0.125, 0.0625,     1],
                new <Number>[ 0.125,  0.25,  0.125,     1],
                new <Number>[ 0.0625, 0.125, 0.0625,     1]
            ];
            
        private var mEmboss:Array = 
            [
                new <Number>[ -2, -1,  0,     1],
                new <Number>[ -1,  1,  1,     1],
                new <Number>[  0,  1,  2,     1]
            ];
            
        private var mEmboss2:Array = 
            [
                new <Number>[ -1,  -1,  0,     1],
                new <Number>[ -1, .25,  1,     1],
                new <Number>[  0,   1,  1,     1]
            ];
            
            
        public function Convolution()
        {    
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);    
        }
        
        private function init(event:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            initStage();
            initCombo();
            loadImage();
            
            addEventListener(Event.ENTER_FRAME, onTick);
        }
        
        private function loadImage():void {
            var l:Loader = new Loader();
            l.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
            l.load(new URLRequest("http://assets.wonderfl.net/images/related_images/6/68/68ef/68efcb70438fb102c11265efe3fd1a737160999c"), new LoaderContext(true));
        }
        
        private function onImageLoad(event:Event = null):void
        {
            event.currentTarget.removeEventListener(Event.COMPLETE, onImageLoad);
            var l:Loader = (event.currentTarget as LoaderInfo).loader;
            mTextureData = (l.content as Bitmap).bitmapData;    
            stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initStage3d );
            stage.stage3Ds[0].requestContext3D();
        }
        
        private function initStage():void
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
        }
        
        private function initCombo():void
        {
            var back:Shape = new Shape();
            back.graphics.beginFill(0x000000, .90);
            back.graphics.drawRect(0, 0, 125, 40);
            back.graphics.endFill();
            addChild(back);
            
            var data:Array = 
            [
                { label:'Identity', data:mIdentity },
                { label:'Gaussian Blur 1', data:mGaussianBlur1 },
                { label:'Gaussian Blur 2', data:mGaussianBlur2 },
                { label:'Sharpen', data:mSharpen },
                { label:'Sharpen (Edges)', data:mSharpen2 },
                { label:'Edge Detect 1', data:mEdgeDetect1 },
                { label:'Edge Detect 2', data:mEdgeDetect2 },
                { label:'Sobel (Vert)', data:mSobelVert },
                { label:'Sobel (Horz)', data:mSobelHorz },
                { label:'Box Blur', data:mBoxBlur },
                { label:'Triangle Blur', data:mTriangleBlur },
                { label:'Emboss 1', data:mEmboss },
                { label:'Emboss 2', data:mEmboss2 }
            ]
                
            var combo:ComboBox = new ComboBox(this, 10, 10, "Identity", data);
            combo.numVisibleItems = 13;
            combo.addEventListener(Event.SELECT, onMatrixSelect);
        }
        
        private function onMatrixSelect(event:Event):void
        {
            var combo:ComboBox = event.currentTarget as ComboBox;
            var mat:Array = combo.selectedItem.data as Array;
            
            m1 = mat[0];
            m2 = mat[1];
            m3 = mat[2];
        }
        
        
        private function initStage3d(event:Event):void
        {
            mContext3d = stage.stage3Ds[0].context3D;            
            
            mContext3d.configureBackBuffer(stage.stageWidth, stage.stageHeight, 1, true);
            
            var vertices:Vector.<Number> = Vector.<Number>([
            //    x        y        z            u     v
                -1.0,     -1.0,     0,          0, 0, 
                -1.0,      1.0,     0,             0, 1,
                 1.0,      1.0,     0,             1, 1,
                 1.0,     -1.0,     0,            1, 0  ]);
            
            mVertBuffer = mContext3d.createVertexBuffer(4, 5);
            mVertBuffer.uploadFromVector(vertices, 0, 4);
            
            mIndexBuffer = mContext3d.createIndexBuffer(6);            
            mIndexBuffer.uploadFromVector (Vector.<uint>([0, 1, 2, 2, 3, 0]), 0, 6);
            
            mTexture = mContext3d.createTexture(mTextureData.width, mTextureData.height, Context3DTextureFormat.BGRA, true);
            mTexture.uploadFromBitmapData(mTextureData);
            
            // va0 holds xyz
            mContext3d.setVertexBufferAt(0, mVertBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
            
            // va1 holds uv
            mContext3d.setVertexBufferAt(1, mVertBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
            
            generateMicroProg();
            
            mContext3d.setTextureAt(0, mTexture);
            mContext3d.setProgram(mProgram);
        }
        
        private function generateMicroProg():void
        {
            var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
            vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
                "m44 op, va0, vc0                                \n" +
                "mov v0, va1                                      "
            );
            var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
            fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
                "mov ft2.xy, fc0.yy  \n" +
                "mul ft3.xy, fc0.x, ft2.xy  \n" +
                "add ft3.xy, ft3.xy, v0.xy  \n" +
                "tex ft3, ft3.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft3, ft3, fc1.x  \n" +
                "mov ft2.xy, fc0.zw  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc1.y  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.wy  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc1.z  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.yz  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc2.x  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.zz  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc2.y  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.wz  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc2.z  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.yw  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc3.x  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.zw  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc3.y  \n" +
                "add ft3, ft3, ft4  \n" +
                "mov ft2.xy, fc0.ww  \n" +
                "mul ft4.xy, fc0.x, ft2.xy  \n" +
                "add ft4.xy, ft4.xy, v0.xy  \n" +
                "tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip>  \n" +
                "mul ft4, ft4, fc3.z  \n" +
                "add ft3, ft3, ft4  \n" +
                "div ft3.xyz, ft3.xyz, fc4.x  \n" +
                "mov ft3.w, fc0.w  \n" +
                "mov oc, ft3"
            );
            
            mProgram = mContext3d.createProgram();
            mProgram.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
        }

        private var m1:Vector.<Number> = mIdentity[0];
        private var m2:Vector.<Number> = mIdentity[1];
        private var m3:Vector.<Number> = mIdentity[2];
        private const PIXEL:Number = 1 / 512;
        private function onTick(event:Event):void
        {
            if ( !mContext3d ) 
                return;
            
            mContext3d.clear ( 0, 0, 0, 1 );
            
            // set vertex data from blank Matrix3D
            mContext3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mMatrix, true);
            
            // FC0 = CONSTANTS = [onePixel, -1, 0, 1]
            mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>( [ PIXEL, -1, 0, 1 ]) );
            
            // FC1 - FC3 = 3x3 MATRIX
            mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, m1 );
            mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, m2 );
            mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, m3 );
            
            // add it up
            var sum:Number = 0.0;
            for (var i:int = 0; i < 3; i++)
            {
                sum += m1[i];
                sum += m2[i];
                sum += m3[i];
            }
            if (sum <= 0.0) sum = 1.0;
            
            // FC4 = SUM
            mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, Vector.<Number>( [ sum, 1, 1, 1 ]) );
            
            mContext3d.drawTriangles(mIndexBuffer);
            mContext3d.present();
        }
    }
}