package { import aether.effects.transformations.TranslateEffect; import aether.utils.Adjustments; import aether.utils.ImageUtil; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.Sprite; import flash.events.Event; import flash.filters.BlurFilter; [SWF(width=550, height=400, backgroundColor=0x000000)] /** * Demonstrates how rainfall can be simulated using multiple planes of bitmap data * overlaid and animated at different rates. */ public class graphic_flex_image_effects_08_Flex_Rainstorm extends Sprite { private var _rainData0:BitmapData; private var _rainData1:BitmapData; private var _rainBitmap0:Bitmap; private var _rainBitmap1:Bitmap; private var _blackRect:BitmapData; /** * Constructor. Creates assets and sets up ENTER_FRAME handler for animation. */ public function graphic_flex_image_effects_08_Flex_Rainstorm() { makeSky(); makeRainPlanes(); // this will be used to darken previous frame renders in order to create trailing droplets _blackRect = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x44000000); addEventListener(Event.ENTER_FRAME, onSpriteEnterFrame); } /** * Creates the bitmap data with the sky image. */ private function makeSky():void { var sky:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight); sky.perlinNoise(250, 200, 3, Math.random(), false, true, BitmapDataChannel.RED, true); Adjustments.adjustContrast(sky, -0.8); Adjustments.adjustBrightness(sky, -100); addChild(new Bitmap(sky)); } /** * Creates the two planes that will be used for the rain droplets. */ private function makeRainPlanes():void { _rainData0 = getRainTexture(0.4); // we clone the data since we will be transforming it each frame // and previous renders need to remain in place _rainBitmap0 = new Bitmap(_rainData0.clone()); // white droplets will overlay over dark background _rainBitmap0.blendMode = BlendMode.SCREEN; addChild(_rainBitmap0); _rainData1 = getRainTexture(0.5); _rainBitmap1 = new Bitmap(_rainData1.clone()); _rainBitmap1.blendMode = BlendMode.SCREEN; addChild(_rainBitmap1); } /** * Generates a rain texture using noise, adjustments and blurs. * * @param density Controls the amount of droplets that should appear. * * @return The bitmap data created. */ private function getRainTexture(density:Number):BitmapData { var noise:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight); var perlin:BitmapData = noise.clone(); /// high contrast, grayscale noise noise.noise(int(new Date()), 0, 255, BitmapDataChannel.RED, true); // blur to create more levels of gray ImageUtil.applyFilter(noise, new BlurFilter(2, 2)); // density setting controls how levels are applied var black:uint = 240 - 127*density; var mid:uint = (240 - black)/2 + black; Adjustments.setLevels(noise, black, mid, 240); // create additional Perlin noise to add more levels of light and dark perlin.perlinNoise(50, 50, 2, Math.random(), false, true, BitmapDataChannel.RED, true); noise.draw(perlin, null, null, BlendMode.MULTIPLY); perlin.dispose(); // blur slightly more vertically for falling drops ImageUtil.applyFilter(noise, new BlurFilter(1, 2)); return noise; } /** * Handler for ENTER_FRAME to control animation of multiple planes of bitmap data. * * @param event Event dispatched by this sprite. */ private function onSpriteEnterFrame(event:Event):void { // move rain plane data slightly, more vertically than horizontally new TranslateEffect(1, 6, true).apply(_rainData0); // draw the black rectangle of low opacity over rain image to create a fade _rainBitmap0.bitmapData.draw(_blackRect); // draw new, translated rain data over old data _rainBitmap0.bitmapData.draw(_rainData0, null, null, BlendMode.SCREEN); new TranslateEffect(2, 8, true).apply(_rainData1); _rainBitmap1.bitmapData.draw(_blackRect); _rainBitmap1.bitmapData.draw(_rainData1, null, null, BlendMode.SCREEN); } } }