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

forked from: forked from: forked from: wonderfl KeyVisual V.4.ja

Get Adobe Flash player
by pacpac 06 Oct 2009
    Embed
package be.nascom.flash.graphics
{
    import flash.display.BitmapData;
    import flash.display.BitmapDataChannel;
    import flash.display.BlendMode;
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.filters.ConvolutionFilter;
    import flash.filters.DisplacementMapFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    
    /** 
     * 
     * The Rippler class creates an effect of rippling water on a source DisplayObject.
     * 
     * @example The following code takes a DisplayObject on the stage and adds a ripple to it, assuming source is a DisplayObject already on the stage.
     * 
     *     <listing version="3.0">
     *         import be.nascom.flash.graphics.Rippler;
     *         
     *         // create a Rippler instance to impact source, with a strength of 60 and a scale of 6.
     *         // The source can be any DisplayObject on the stage, such as a Bitmap or MovieClip object.
     *         var rippler : Rippler = new Rippler(source, 60, 6);
     * 
     *         // create a ripple with size 20 and alpha 1 with origin on position (200, 50)
     *         rippler.drawRipple(100, 50, 20, 1);
     * </listing>
     * 
     * @author David Lenaerts
     * @mail david.lenaerts@nascom.be
     * 
      */
    public class Rippler
    {
        // The DisplayObject which the ripples will affect.
        private var _source : DisplayObject;
        
        // Two buffers on which the ripple displacement image will be created, and swapped.
        // Depending on the scale parameter, this will be smaller than the source
        private var _buffer1 : BitmapData;
        private var _buffer2 : BitmapData;
        
        // The final bitmapdata containing the upscaled ripple image, to match the source DisplayObject
        private var _defData : BitmapData;
        
        // Rectangle and Point objects created once and reused for performance
        private var _fullRect : Rectangle; 			// A buffer-sized Rectangle used to apply filters to the buffer
        private var _drawRect : Rectangle;			// A Rectangle used when drawing a ripple
        private var _origin : Point = new Point();	// A Point object to (0, 0) used for the DisplacementMapFilter as well as for filters on the buffer
        
        // The DisplacementMapFilter applied to the source DisplayObject
        private var _filter : DisplacementMapFilter;
        // A filter causing the ripples to grow
        private var _expandFilter : ConvolutionFilter;
        
        // Creates a colour offset to 0x7f7f7f so there is no image offset due to the DisplacementMapFilter
        private var _colourTransform : ColorTransform;
        
        // Used to scale up the buffer to the final source DisplayObject's scale
        private var _matrix : Matrix;
        
        // We only need 1/scale, so we keep it here
        private var _scaleInv : Number;
        
        /**
         * Creates a Rippler instance.
         * 
         * @param source The DisplayObject which the ripples will affect.
         * @param strength The strength of the ripple displacements.
         * @param scale The size of the ripples. In reality, the scale defines the size of the ripple displacement map (map.width = source.width/scale). Higher values are therefor also potentially faster.
         * 
         */
        public function Rippler(source : DisplayObject, strength : Number, scale : Number = 2)
        {
            var correctedScaleX : Number;
            var correctedScaleY : Number;
            
            _source = source;
            _scaleInv = 1/scale;
            
            // create the (downscaled) buffers and final (upscaled) image data, sizes depend on scale
            _buffer1 = new BitmapData(source.width*_scaleInv, source.height*_scaleInv, false, 0x000000);
            _buffer2 = new BitmapData(_buffer1.width, _buffer1.height, false, 0x000000);
            _defData = new BitmapData(source.width, source.height, false, 0x7f7f7f);
            
            // Recalculate scale between the buffers and the final upscaled image to prevent roundoff errors.
            correctedScaleX = _defData.width/_buffer1.width;
            correctedScaleY = _defData.height/_buffer1.height;
            
            // Create reusable objects
            _fullRect = new Rectangle(0, 0, _buffer1.width, _buffer1.height);
            _drawRect = new Rectangle();
            
            // Create the DisplacementMapFilter and assign it to the source
            _filter = new DisplacementMapFilter(_defData, _origin, BitmapDataChannel.BLUE, BitmapDataChannel.BLUE, strength, strength, "wrap");
            _source.filters = [_filter];
            
            // Create a frame-based loop to update the ripples
            _source.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
            
            // Create the filter that causes the ripples to grow.
            // Depending on the colour of its neighbours, the pixel will be turned white
            _expandFilter = new ConvolutionFilter(3, 3, [0.5, 1, 0.5, 1, 0, 1, 0.5, 1, 0.5], 3);
            
            // Create the colour transformation based on 
            _colourTransform = new ColorTransform(1, 1, 1, 1, 128, 128, 128);
            
            // Create the Matrix object
            _matrix = new Matrix(correctedScaleX, 0, 0, correctedScaleY);
            
        }
        
        /**
         * Initiates a ripple at a position of the source DisplayObject.
         * 
         * @param x The horizontal coordinate of the ripple origin.
         * @param y The vertical coordinate of the ripple origin.
         * @param size The size of the ripple diameter on first impact.
         * @param alpha The alpha value of the ripple on first impact.
         */
        public function drawRipple(x : int, y : int, size : int, alpha : Number) : void
        {
        	var half : int = size >> 1;		// We need half the size of the ripple
            var intensity : int = (alpha*0xff & 0xff)*alpha;	// The colour which will be drawn in the currently active buffer
            
            // calculate and draw the rectangle, having (x, y) in its centre
            _drawRect.x = (-half+x)*_scaleInv;	
            _drawRect.y = (-half+y)*_scaleInv;
            _drawRect.width = _drawRect.height = size*_scaleInv;
            _buffer1.fillRect(_drawRect, intensity);
        }
        
       	/**
       	 * Returns the actual ripple image.
       	 */
        public function getRippleImage() : BitmapData
        {
        	return _defData;
        }
        
        /**
         * Removes all memory occupied by this instance. This method must be called before discarding an instance.
         */
        public function destroy() : void
        {
            _source.removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
            _buffer1.dispose();
            _buffer2.dispose();
            _defData.dispose();
        }
        
        // the actual loop where the ripples are animated
        private function handleEnterFrame(event : Event) : void
        {
        	// a temporary clone of buffer 2
            var temp : BitmapData = _buffer2.clone();
            // buffer2 will contain an expanded version of buffer1
            _buffer2.applyFilter(_buffer1, _fullRect, _origin, _expandFilter);
            // by substracting buffer2's old image, buffer2 will now be a ring
            _buffer2.draw(temp, null, null, BlendMode.SUBTRACT, null, false);
            // scale up and draw to the final displacement map, and apply it to the filter
            _defData.draw(_buffer2, _matrix, _colourTransform, null, null, true);
            _filter.mapBitmap = _defData;
            _source.filters = [_filter];
            temp.dispose();
            // switch buffers 1 and 2
            switchBuffers();
        }
        
        // switch buffer 1 and 2, so that 
        private function switchBuffers() : void
        {
            var temp : BitmapData;
            temp = _buffer1;
            _buffer1 = _buffer2;
            _buffer2 = temp;
        }
    }
}