package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.geom.ColorTransform; import flash.geom.Point; import flash.geom.Rectangle; [SWF(width=900, height=300, backgroundColor=0x000000)] /** * Demonstrates how an image can be posterized through calls to the paletteMap() method of BitmapData. */ public class graphic_flex_image_effects_04_Flex_PosterizeTest extends graphic_flex_image_effects_04_Flex_LevelsTest { /** * Run after the image loads in super class. This copies the loaded image twice and applies posterization to * both copies before adding them to the stage. */ override protected function runPostImageLoad():void { var bitmapData:BitmapData = _loadedBitmap.bitmapData; addChild(_loadedBitmap); var clone:BitmapData = bitmapData.clone(); // posterizes first clone to 4 levels posterize(clone, 4); var bitmap:Bitmap = new Bitmap(clone); bitmap.x = bitmapData.width; addChild(bitmap); clone = bitmapData.clone(); // posterizes first clone to 2 levels posterize(clone, 2); bitmap = new Bitmap(clone); bitmap.x = bitmapData.width*2; addChild(bitmap); } /** * Posterizes the specified image, reducing its number of colors, using the number of levels specified. * * @param bitmapData The image to which to apply the posterization. * @param levels The number of colors in each channel to reduce the image to. */ private function posterize(bitmapData:BitmapData, levels:uint):void { // creates a new image with the image data from a single channel used in all three channels var red:BitmapData = makeImageFromChannel(bitmapData, BitmapDataChannel.RED); var green:BitmapData = makeImageFromChannel(bitmapData, BitmapDataChannel.GREEN); var blue:BitmapData = makeImageFromChannel(bitmapData, BitmapDataChannel.BLUE); // stores the channel image data into a vector for easy access var sourceChannels:Vector. = new Vector.(); sourceChannels.push(red); sourceChannels.push(green); sourceChannels.push(blue); // creates three new BitmapData instances that can be used to manipulate each color channel red = new BitmapData(bitmapData.width, bitmapData.height); green = red.clone(); blue = red.clone(); // stores what will be the adjusted channel image data into a vector for easy access var adjustedChannels:Vector. = new Vector.(); adjustedChannels.push(red); adjustedChannels.push(green); adjustedChannels.push(blue); var channelData:BitmapData; var threshold:uint; var colorTransform:ColorTransform; var brightness:uint; var j:uint; // can reduce levels by 1 since the number of loops should be one less than the number of levels; // for instance, a single iteration of the loop will produce an image of two colors, so a levels // setting of 2 only needs to result in 1 loop levels--; for (var i:uint = 0; i < levels; i++) { // threshold will be higher on lower iterations of the loop, resulting in images with more black // for the lower iterations and images with more white for the higher iterations threshold = 255*((levels-i)/(levels+1)); // the lower the iteration, the more the resulting channel image will be brightened before it // is layered in the composite adjusted channel brightness = 255*((levels-i-1)/levels); colorTransform = new ColorTransform(1, 1, 1, 1, brightness, brightness, brightness); // run through all three color channels for (j = 0; j < 3; j++) { // grab the original data channelData = sourceChannels[j].clone(); // set the levels on the data to reduce the colors to 2 setLevels(channelData, threshold, threshold, threshold); // draw the thresholded image into the adjusted channel after brightening it, // using MULTIPLY to overlay the grays adjustedChannels[j].draw(channelData, null, colorTransform, BlendMode.MULTIPLY); } } // copy the adjusted channels into the original image copyChannel(red, bitmapData, BitmapDataChannel.RED); copyChannel(green, bitmapData, BitmapDataChannel.GREEN); copyChannel(blue, bitmapData, BitmapDataChannel.BLUE); } /** * Creates a new grayscale image using the data from a single channel, copying that channel's * data into each channel of the new image. * * @param bitmapData The image from which to copy the channel data. * @param channel The channel to create the grayscale image from. */ private function makeImageFromChannel( bitmapData:BitmapData, channel:uint ):BitmapData { var clone:BitmapData = bitmapData.clone(); var rect:Rectangle = clone.rect; var pt:Point = new Point(); // copy the same channel into all three channels of the new image clone.copyChannel(bitmapData, rect, pt, channel, BitmapDataChannel.RED); clone.copyChannel(bitmapData, rect, pt, channel, BitmapDataChannel.GREEN); clone.copyChannel(bitmapData, rect, pt, channel, BitmapDataChannel.BLUE); return clone; } /** * Copies the channel from the source into the destination bitmap data. * * @param source The image from which to copy the channel data. * @param destination The image to copy the channel data into. * @param channel The channel to copy. */ private function copyChannel( source:BitmapData, destination:BitmapData, channel:uint ):void { destination.copyChannel(source, source.rect, new Point(), channel, channel); } } }