Light Masking

by noonat
There are three classes in this file:
- LightDemo (the actual "application")
- LightPool
- Light
♥0 | Line 191 | Modified 2009-09-06 01:36:53 | MIT License
play

ActionScript3 source code

/**
 * Copyright noonat ( http://wonderfl.net/user/noonat )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/s06c
 */

// There are three classes in this file:
// - LightDemo (the actual "application")
// - LightPool
// - Light

package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.*;
    import flash.utils.getTimer;
    
    public class LightDemo extends Sprite { 
        private var _bitmap:Bitmap;
        private var _light:Light;
        private var _pixels:BitmapData;
        private var _pixelsBg:BitmapData;
        private var _pool:LightPool;
        private var _p:Point, _r:Rectangle;
        
        function LightDemo():void {
            // basic setup for rendering
            stage.scaleMode = StageScaleMode.NO_SCALE;
            _p = new Point(0, 0);
            _r = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            _pixels = new BitmapData(_r.width, _r.height, true, 0xffffffff);
            _pixelsBg = _pixels.clone();
            _pixelsBg.perlinNoise(_r.width, _r.height, 32, 32982, false, false, 5);
            _pixelsBg.colorTransform(_r, new ColorTransform(1, 1, 1, 1, 100, 100, 100));
            _bitmap = addChild(new Bitmap(_pixels)) as Bitmap;
            addEventListener(Event.ENTER_FRAME, _onEnterFrame);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP, _onMouseUp);
            
            // create the light pool and add some lights
            _pool = new LightPool(_r.width, _r.height, 1/2, 0.9);
            _pool.add(new Light(_r.width/2, _r.height/2-41, 64, 0.3));
            _pool.add(new Light(_r.width/2-32, _r.height/2+32, 64, 0.3));
            _pool.add(new Light(_r.width/2+32, _r.height/2+32, 64, 0.3));
        }
        
        internal function _onEnterFrame(event:Event):void {
            _updateFps();
            _updateLight();
            _render();
        }
        
        internal function _onMouseDown(event:MouseEvent):void {
            if (_light) return;
            _light = _pool.add(new Light(mouseX, mouseY, 16, 0.3));
        }
        
        internal function _onMouseUp(event:MouseEvent):void {
            if (!_light) return;
            _updateLight();
            _light = null;
        }
        
        internal function _render():void {
            _pixels.lock();
            _pixels.copyPixels(_pixelsBg, _r, _p); // draw the unlit background
            _pool.render(_pixels); // draw the lights on top of it
            _pixels.unlock();
        }
        
        private var _fps:int=0, _fpsText:TextField=null, _fpsTime:Number=1000;
        private var _frameTime:int=0, _lastFrameTime:int=0;
        internal function _updateFps():void {
            ++_fps;
            if (_fpsText === null) {
                _fpsText = addChild(new TextField()) as TextField;
                _fpsText.defaultTextFormat = new TextFormat('Arial', null, 0xffffff, true);
                _fpsText.selectable = false;
                _frameTime = _lastFrameTime = getTimer();
            }
            else {
                _lastFrameTime = _frameTime;
                _frameTime = getTimer();
                _fpsTime += _frameTime - _lastFrameTime;
            }
            if (_fpsTime > 1000) {
                _fpsText.text = _fps + ' fps\ndrag to add lights';
                _fps = _fpsTime = 0;
            }
        }
        
        internal function _updateLight():void {
            if (!_light) return;
            var dx:Number = mouseX - _light.x;
            var dy:Number = mouseY - _light.y;
            _light.scale = Math.max(16, Math.sqrt(dx*dx+dy*dy));
        }
    }
}

import flash.display.*;
import flash.filters.*;
import flash.geom.*;

class LightPool {
    public var alpha:Number;
    public var scale:Number;
    private var _filter:BlurFilter;
    private var _lights:Array;
    private var _matrix:Matrix, _inverseMatrix:Matrix;
    private var _p:Point, _r:Rectangle;
    private var _pixels:BitmapData, _alphaPixels:BitmapData;
    
    function LightPool(width:uint, height:uint, scale:Number=1, alpha:Number=1.0,
                       blurX:Number=8, blurY:Number=8):void {
        this.alpha = alpha;
        this.scale = scale;
        _filter = new BlurFilter(blurX, blurY);
        _lights = [];
        _matrix = new Matrix();
        _matrix.scale(Math.floor(1/scale), Math.floor(1/scale));
        _inverseMatrix = new Matrix();
        _inverseMatrix.scale(scale, scale);
        _p = new Point(0, 0);
        _r = new Rectangle(0, 0, Math.floor(width*scale), Math.floor(height*scale));
        _pixels = new BitmapData(_r.width, _r.height, true);
        _alphaPixels = new BitmapData(_r.width, _r.height);
    }
    
    public function add(light:Light):Light {
        for (var i:uint=0; i < _lights.length; ++i) {
            if (_lights[i] === null) {
                _lights[i] = light;
                return light;
            }
        }
        _lights.push(light);
        return light;
    }
    
    public function remove(light:Light):void {
        var index:int = _lights.indexOf(light);
        if (index !== -1) _lights[index] = null;
    }

    public function render(dest:BitmapData):void {
        _alphaPixels.fillRect(_r, Math.floor(0xff * alpha) | 0xff000000);
        for (var i:uint=0; i < _lights.length; ++i) {
            if (_lights[i]) _lights[i].render(_alphaPixels, _inverseMatrix);
        }
        _alphaPixels.applyFilter(_alphaPixels, _r, _p, _filter);
        _pixels.fillRect(_r, 0x00000000);
        _pixels.copyChannel(_alphaPixels, _r, _p, BitmapDataChannel.BLUE,
                            BitmapDataChannel.ALPHA);
        dest.draw(_pixels, _matrix);
    }    
}

class Light {
    private static var _circleShape:Shape;
    private static var _colorTransform:ColorTransform;
    public var active:Boolean;
    public var alpha:Number;
    public var shape:Shape;
    private var _angle:Number;
    private var _matrix:Matrix;
    private var _radians:Number;
    private var _scale:Number;
    private var _transform:Boolean;
    private var _x:Number, _y:Number;
    
    public function Light(x:Number=0, y:Number=0, scale:Number=1, alpha:Number=1.0,
                          angle:Number=0.0, shape:Shape=null) {
        if (_circleShape === null) {
            _circleShape = new Shape();
            _circleShape.graphics.beginFill(0x000000, 1.0);
            _circleShape.graphics.drawCircle(0, 0, 1);
            _circleShape.graphics.endFill();
            _colorTransform = new ColorTransform();
        }
        _matrix = new Matrix();
        this.active = true;
        this.angle = angle;
        this.shape = shape || _circleShape;
        this.x = x;
        this.y = y;
        this.alpha = alpha;
        this.scale = scale;
    }
    
    public function render(dest:BitmapData, matrix:Matrix=null):void {
        _matrix.createBox(_scale, _scale, _radians, _x, _y);
        if (matrix) {
            _matrix.concat(matrix);
        }
        if (alpha != 1.0) {
            _colorTransform.alphaMultiplier = alpha;
            dest.draw(shape, _matrix, _colorTransform)
        }
        else {
            dest.draw(shape, _matrix);
        }
    }
    
    public function get angle():Number { return _angle; }
    public function set angle(value:Number):void {
        _angle = value;
        _radians = _angle * (Math.PI / 180);
        _transform = true;
    }
    public function get radians():Number { return _radians; }
    public function set radians(value:Number):void {
        _radians = value;
        _angle = _radians * (180 / Math.PI);
    }
    public function get scale():Number { return _scale; }
    public function set scale(value:Number):void { _scale = value; _transform = true; }
    public function get x():Number { return _x; }
    public function set x(value:Number):void { _x = value; _transform = true; }
    public function get y():Number { return _y; }
    public function set y(value:Number):void { _y = value; _transform = true; }
}