package { import aether.utils.ImageUtil; import aether.utils.MathUtil; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.LoaderInfo; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.net.FileFilter; import flash.net.FileReference; [SWF(width=500, height=500, backgroundColor=0xCCCCCC)] /** * Demonstrates how a kaleidoscope effect can be achieved by copying and rotating portions of an image. * This class also shows how to allow user to load local image, as well as how to transform the effect * based on mouse movement, where they can click and drag image to alter angle of kaleidoscope. */ public class graphic_flex_image_effects_12_Flex_Kaleidoscope extends Sprite { // number of segments in kaleidoscope private static const NUM_SEGMENTS:uint = 8; private var _file:FileReference; private var _originalImage:BitmapData; private var _kaleidoscope:Bitmap; private var _startAngle:Number; private var _angle:Number; private var _center:Point; /** * Constructor. This sets up a listener for when the stage is clicked. */ public function graphic_flex_image_effects_12_Flex_Kaleidoscope() { stage.addEventListener(MouseEvent.CLICK, onStageClick); } /** * Creates a new Bitmap for the bitmap data passed in. This bitmap is rotated and flipped based * on the index, which is its index in the total number of segments. This allows for segments to * be placed around a central point, with every other segment mirrored, to create the effect. * * @param index The index of the segment in the total number of segments. * @param segment The bitmap data to display. * @param center The center of the effect around which the segments are placed. * @param angle The angle that each segment will be placed at, based on total number of segments. * * @return The bitmap created. */ private function drawSegment(index:uint, segment:BitmapData, center:Point, angle:Number):Bitmap { var bitmap:Bitmap = new Bitmap(segment); // odd segments are flipped vertically if (index % 2 > 0) { bitmap.scaleY = -1; // add one to the index because the segment was flipped bitmap.rotation = (index+1)*angle; } else { bitmap.rotation = index*angle; } bitmap.x = center.x; bitmap.y = center.y; return bitmap; } /** * Creates the different segments that make up the total kaleidoscope effect. */ private function drawgraphic_flex_image_effects_12_Flex_Kaleidoscope():void { var width:Number = _originalImage.width; var height:Number = _originalImage.height; // radius of effect will be half of image size var radius:Number = Math.min(width, height)/2; // find difference in angle of each segment based on total number of segments var segmentAngle:Number = 360/NUM_SEGMENTS; // these values will be used to draw triangle shape out of original image var theta:Number = MathUtil.degreesToRadians(segmentAngle); var x:Number = Math.cos(theta)*radius; var y:Number = Math.sin(theta)*radius; // translate image so it is centered on registration point var matrix:Matrix = new Matrix(); matrix.translate(-width/2, -height/2); // rotate so that the visible pixel in the positive quadrant of image are from the current angle matrix.rotate(_angle); // draw pixels from positive image quadrant into triangular shape var shape:Shape = new Shape(); shape.graphics.beginBitmapFill(_originalImage, matrix, false); shape.graphics.lineTo(radius, 0); shape.graphics.lineTo(x, y); shape.graphics.lineTo(0, 0); shape.graphics.endFill(); // draw shape data into bitmap data var segment:BitmapData = ImageUtil.getBitmapData(shape); var sprite:Sprite = new Sprite(); var center:Point = new Point(width/2, height/2); // run through total number of segments and place drawn segment into rotated bitmaps for (var i:uint; i < NUM_SEGMENTS; i++) { sprite.addChild(drawSegment(i, segment, center, segmentAngle)); } _kaleidoscope.bitmapData.dispose(); // draw new kaleidoscope contents from drawn segments _kaleidoscope.bitmapData = ImageUtil.getBitmapData(sprite); segment.dispose(); } /** * Handler for when stage is clicked. This opens file browse dialog. * * @param event Event dispatched by stage. */ private function onStageClick(event:MouseEvent):void { _file = new FileReference(); _file.addEventListener(Event.SELECT, onFileSelect); _file.browse([new FileFilter("Images", "*.jpg;*.jpeg;*.gif;*.png")]); } /** * Handler for when a file is selected. Loads file. * * @param event Event dispatched by FileReference. */ private function onFileSelect(event:Event):void { stage.removeEventListener(MouseEvent.CLICK, onStageClick); _file.addEventListener(Event.COMPLETE, onImageLoadComplete); _file.load(); } /** * Handler for when local image completes loading. This loads bytes into Loader for access. * * @param event Event dispatched by FileReference. */ private function onImageLoadComplete(event:Event):void { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLocalFileRead); loader.loadBytes(_file.data); } /** * Handler for when bytes from local file are read into Loader. This kicks off the effect with * the loaded image and sets up an ENTER_FRAME listener so that the effect may be animated. * * @param event Event dispatched by LoaderInfo. */ private function onLocalFileRead(event:Event):void { var loaderInfo:LoaderInfo = event.target as LoaderInfo; // add initial image to stage _kaleidoscope = loaderInfo.content as Bitmap; addChild(_kaleidoscope); var width:Number = stage.stageWidth; var height:Number = stage.stageHeight; // will be used to save reference to original image, scaled to stage _originalImage = new BitmapData(width, height); var bitmapData:BitmapData = _kaleidoscope.bitmapData; var matrix:Matrix = new Matrix(); // scale loaded image to stage size matrix.scale(width/bitmapData.width, height/bitmapData.height); _originalImage.draw(bitmapData, matrix); _kaleidoscope.bitmapData = _originalImage.clone(); _angle = 0; // will be used to determine angle of drag _center = new Point(stage.stageWidth/2, stage.stageHeight/2); // apply initial effect drawgraphic_flex_image_effects_12_Flex_Kaleidoscope(); stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown); } /** * Handler for when user clicks stage. Thissets up listeners for movement and release, * and records start angle of click in relation to center of effect. * * @param event Event dispatched by stage. */ private function onStageMouseDown(event:MouseEvent):void { var position:Point = new Point(stage.mouseX-_center.x, stage.mouseY-_center.y); _startAngle = _angle - Math.atan2(position.y, position.x); stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp); stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove); } /** * Handler for then mouse is released after clicking stage. This removes listeners. * * @param event Event dispatched by stage. */ private function onStageMouseUp(event:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp); stage.removeEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove); } /** * Handler for then mouse is moved after clicking stage. This redraws kaleidoscope * based on mouse's current position. * * @param event Event dispatched by stage. */ private function onStageMouseMove(event:MouseEvent):void { var position:Point = new Point(stage.mouseX-_center.x, stage.mouseY-_center.y); // determine new angle based on angle at start of click and new mouse position _angle = _startAngle + Math.atan2(position.y, position.x); drawgraphic_flex_image_effects_12_Flex_Kaleidoscope(); } } }