package { 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.filters.BlurFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; [SWF(width=600, height=300, backgroundColor=0x333333)] /** * Demonstrates how letters in a textfield can be separated into separate bitmap data instances * so that different effects may be applied to each. Here, each letter has a growing amount of * distress applied. */ public class graphic_flex_image_effects_09_Flex_GrowingDistress extends Sprite { /** * Constructor. Creates textfield, draws each letter into separate bitmap data, * then distresses each letter individually. */ public function graphic_flex_image_effects_09_Flex_GrowingDistress() { var field:TextField = createField(); var letters:Vector. = createLetterBitmaps(field); distressLetters(letters); } /** * Creates a textfield. * * @return The textfield created. */ private function createField():TextField { var field:TextField = new TextField(); // make sure you have Impact installed field.defaultTextFormat = new TextFormat("Impact", 100, 0xFFFFFF); field.autoSize = TextFieldAutoSize.LEFT; field.text = "DISTRESS"; field.x = (stage.stageWidth - field.width)/2; field.y = (stage.stageHeight - field.height)/2; return field; } /** * Draws each letter in textfield into separate bitmap data and returns * these references in a vector. * * @param field The textfield with text to draw into bitmap data instance. * * @return A vector of BitmapData instances where each instance holds the pixel * data from a single letter. */ private function createLetterBitmaps(field:TextField):Vector. { // draw entire field into bitmap data var bitmapData:BitmapData = ImageUtil.getBitmapData(field); var letters:Vector. = new Vector.(); var text:String = field.text; var numLetters:uint = field.text.length; // will be used to center vertically var halfHeight:Number = field.height/2; // the placement of the first letter var startX:Number = field.x; // the vertical middle of the field var y:Number = field.y + halfHeight; var x:Number; var letterData:BitmapData; var bitmap:Bitmap; var sprite:Sprite; var bitmapWidth:Number; for (var i:uint = 0; i < numLetters; i++) { // create a sprite to hold each bitmap sprite = new Sprite(); // place the single letter being evaluated into the field field.text = text.charAt(i); // draw the single letter into bitmap data letterData = ImageUtil.getBitmapData(field); bitmap = new Bitmap(letterData); // find width of letter bitmapWidth = bitmap.width; // center the letter horizontally on the bitmaps registration point bitmap.x = -bitmapWidth/2; // center the letter vertically on the bitmaps registration point bitmap.y = -halfHeight; // place all the text up to and including this letter into field field.text = text.substr(0, i+1); // based on field's current length and width of bitmap, place letter x = startX + field.width - bitmapWidth; sprite.x = x + bitmapWidth/2; sprite.y = y; // add bitmap to sprite and sprite to stage; // the nesting is done so that the sprite may be transformed with the bitmap centered within it sprite.addChild(bitmap); addChild(sprite); // push data into vector letters.push(letterData); } return letters; } /** * Distresses each bitmap data instance in the vector separately, with each * letter getting more distressed than the previous. * * @param letters A vector of bitmap data to distress. */ private function distressLetters(letters:Vector.):void { for (var i:uint = 1; i < letters.length; i++) { distressImage(letters[i], i*3); // rotate the image to apply same distortion in opposite directions rotateImage(letters[i]); distressImage(letters[i], i*3); // rotate image back right side up rotateImage(letters[i]); } } /** * Draws a copy of the image, rotated 180 degrees, into the original image. * * @param bitmapData The image to rotate. */ private function rotateImage(bitmapData:BitmapData):void { var matrix:Matrix = new Matrix(); // PI radians == 180 degress matrix.rotate(Math.PI); matrix.translate(bitmapData.width, bitmapData.height); var rotatedBitmap:BitmapData = new BitmapData(bitmapData.width, bitmapData.height, true, 0x00000000); rotatedBitmap.draw(bitmapData, matrix); // copy rotated data into original bitmap data bitmapData.copyPixels(rotatedBitmap, bitmapData.rect, new Point()); } /** * Applies distress effects to the specified image at the specified amount. * * @param bitmapData The image to distress * @param amount The amount to distress the image. This should be somethig between 0 and 20, ideally. */ private function distressImage(bitmapData:BitmapData, amount:Number):void { // Perlin noise that will be used for displacement var perlin:BitmapData = new BitmapData(bitmapData.width, bitmapData.height); perlin.perlinNoise( 10, 10, 5, Math.random(), true, true, BitmapDataChannel.RED ); // create more light than dark in the noise Adjustments.setLevels(perlin, 0, 50, 100); var displaceX:Number = amount; // displacing more vertically works better for maintaining text legibility var displaceY:Number = amount*3; ImageUtil.applyFilter( bitmapData, new DisplacementMapFilter( perlin, new Point(), BitmapDataChannel.RED, BitmapDataChannel.RED, displaceX, displaceY, DisplacementMapFilterMode.WRAP ) ); // this noise will be used to alter opacity of distressed image with transparent speckles var noise:BitmapData = new BitmapData(bitmapData.width, bitmapData.height); noise.noise(Math.random(), 0, 255, BitmapDataChannel.RED, true); // apply blur to noise ImageUtil.applyFilter(noise, new BlurFilter(displaceX, displaceY)); // really reduce contrast of noise Adjustments.setLevels(noise, 105, 107, 109); // draw a grayscale image of the distressed image's alpha channel var alpha:BitmapData = ImageUtil.getChannelData( bitmapData, BitmapDataChannel.ALPHA ); // apply the noise to the grayscale alpha channel image using multiply alpha.draw( noise, null, new ColorTransform( 1, 1, 1, Math.min(1, amount*.2) ), BlendMode.MULTIPLY ); // copy the altered grayscale image into the alpha channel of the distressed image bitmapData.copyChannel( alpha, alpha.rect, new Point(), BitmapDataChannel.RED, BitmapDataChannel.ALPHA ); } } }