PixelBenderBooth

Even thou I think PixelBender is a really good idea I’ve never actually tried it. One day i thought to my self ”Hey, that PhotoBooth thing Apple ship with their computers cant be that hard to replicate”. And an hour or two later PixelBenderBooth was born. Press ”space” to remove effects and you need a webcam.

I ”borrowed” the PixelBender kernels from PixelBender Exchange over at Adobe.com.

[SWF]http://blog.mattiasnorell.com/wp-content/uploads/2010/09/FlashBooth.swf, 640,480[/SWF]

Sample code

If you want to try it out for yourself you can copy my sample code below and use it with your own kernels. I wrote this thing in FDT 4 so if you use the Flash IDE you need to change a few things, like [SWF(width=640,height=480,frameRate=30)].

PhotoBooth.as

package {
    import flash.events.MouseEvent;
    import flash.events.KeyboardEvent;
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.events.Event;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;

    [SWF(width=640,height=480,frameRate=30)]

    public class FlashBooth extends MovieClip {
        public var camfeed : Video = new Video(640, 480);
        private var camFrameRate : int = 15;
        private var camWidth : int = 640;
        private var camHeight : int = 480;
        private var cam : Camera;
        private var cameraBitmap : BitmapData;
        private var bmp : Bitmap;

       // Location of the kernels.
        private var siteUrl:String = "";
        private var filterHolder : Sprite = new Sprite();
        private var pbFilters : Array = [siteUrl+"zoomBlur.pbj", siteUrl+"SmartNormalMap.pbj", siteUrl+"RippleBlocks.pbj"];

        public function FlashBooth():void {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e : Event = null):void {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            removeEventListener(Event.ADDED_TO_STAGE, init);

            // We start by settings up the camera
            cam = Camera.getCamera();

            // If no camera is found...
            if (!cam) {
                // ...we tell the user that is the case...
                var camError : TextField = new TextField();
                camError.text = "I search'd the internetz and haz not found camera. I haz no luck. *sigh*";
                addChild(camError);
            } else {
                // ...or we move on to set the height, width and framerate.
                cam.setMode(camWidth, camHeight, camFrameRate);

                // Then we tell the video-object to use the camera as its source.
                camfeed.attachCamera(cam);

                // Set the bitmap to match the size of the video.
                cameraBitmap = new BitmapData(camfeed.width, camfeed.height);

                bmp = new Bitmap(cameraBitmap);
                bmp.x = 0;
                bmp.y = 0;
                addChild(bmp);

                // Load all the filters in the pbFilters-array thru a for-loop.
                // All settings are defined in the FilterClass.as as get/setters.
                for(var i : uint = 0;i < pbFilters.length;++i) {
                    var f : FilterClass = new FilterClass();
                    f.url = pbFilters[i];
                    f.source = camfeed;
                    f.framerate = camFrameRate;
                    f.start();
                    f.x = (80 * i) + 10;
                    f.y = 10;

                    // Since we want the "big" picture to use the filter loaded
                    // in the class we tell our swf to listen for when the user
                    // click on the previewimage generated within the filterclass.
                    f.addEventListener(MouseEvent.CLICK, addFilter)
                    filterHolder.addChild(f);
                }

                // Some positioning and graphics stuff.
                filterHolder.x = (stage.stageWidth - filterHolder.width) / 2;
                filterHolder.y = stage.stageHeight - (filterHolder.height + 30);

                filterHolder.graphics.beginFill(0x000000, .5);
                filterHolder.graphics.drawRoundRect(0, 0, filterHolder.width + 20, filterHolder.height + 20, 10);
                filterHolder.graphics.endFill();
                addChild(filterHolder);

                // Set up a listener since we want to remove the filter when the
                // user press the spacebar.
                stage.addEventListener(KeyboardEvent.KEY_DOWN, keyboardInput);

                 // Update the big picture everytime a frame is loaded.
                stage.addEventListener(Event.ENTER_FRAME, updateViewport);
            }
        }

        private function addFilter(e : MouseEvent):void {
            // Fetch the filter from within the filterclass.
            bmp.filters = [e.currentTarget.getFilter()];
        }

        private function updateViewport(e : Event):void {
            // Update the big picture.
            cameraBitmap.lock();
            cameraBitmap.draw(camfeed);
            cameraBitmap.unlock();
        }

        private function keyboardInput(e : KeyboardEvent):void {
            // Detect what key the user pressed and if it is the
            // spacebar (keycode 32) remove the filter.
            if(e.keyCode == 32) {
                bmp.filters = [];
            }
        }
    }
}

FilterClass.as

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.net.URLRequest;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.display.Shader;
    import flash.filters.ShaderFilter;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.media.Video;
    import flash.utils.Timer;

    public class FilterClass extends Sprite {
        private var shader : Shader;
        private var shaderFilter : ShaderFilter;
        private var filterBmp : Bitmap;
        private var filterBmpData : BitmapData;
        private var frameRateTimer : Timer;

        // Get and set-vars
        private var _camFeed : Video;
        private var _filterUrl : String;
        private var _frameRate : int;

        public function FilterClass() {
            // We set useHandCursor and buttonMode to true since we want the
            // class to act as a button.
            this.useHandCursor = true;
            this.buttonMode = true;
        }

        public function start():void {

            // We start to load the PixelBender kernel.
            var urlRequest : URLRequest = new URLRequest(_filterUrl);
            var urlLoader : URLLoader = new URLLoader();

            // We need to sett the LoaderDataFormat to binary since the kernel
            // is a compiled file.
            urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
            urlLoader.addEventListener(Event.COMPLETE, applyFilter);
            urlLoader.load(urlRequest);

            // Set up the bitmap that will act as the preview.
            filterBmpData = new BitmapData(640, 480);
            filterBmp = new Bitmap(filterBmpData);
            filterBmp.width = 64;
            filterBmp.height = 48;
            addChild(filterBmp);
        }

        private function applyFilter(e : Event):void {
            // Always remove listeners you have no use for.
            e.currentTarget.removeEventListener(Event.COMPLETE, applyFilter);

            // Tell the Flash Player that we want to use the loaded data
            // as a filter.
            shader = new Shader(URLLoader(e.target).data);
            shaderFilter = new ShaderFilter(shader);

            // Since we want the to act as a button with graphics updated in
            // realtime we need to set the filters-parameter to the shaderFilter.
            filterBmp.filters = [shaderFilter];

            // We could use Event.ENTER_FRAME, but since we want to control the
            // framerate we use a timer so that the preview bitmap will update
            // when we want and not everytime a new frame is loaded.
            frameRateTimer = new Timer(1000 / _frameRate);
            frameRateTimer.addEventListener(TimerEvent.TIMER, update);
            frameRateTimer.start();
        }

        private function update(e : TimerEvent = null):void {
            // We lock the bitmapdata before we draw and then unlock it.
            // Performance my friends, performance.
            filterBmpData.lock();
            filterBmpData.draw(_camFeed);
            filterBmpData.unlock();
        }

        public function getFilter():ShaderFilter{
            // With this function we can pass the filter to objects outside the class.
            return shaderFilter;
        }

        /* GETTERS AND SETTERS */
        public function set source(val : Video):void {
            _camFeed = val;
        }

        public function set url(val : String):void {
            _filterUrl = val;
        }

        public function set framerate(val : int):void {
            _frameRate = val;
        }
    }
}

Kommentarer