package com.bit101 { import flash.display.DisplayObject; import flash.display.BitmapData; import flash.display.Bitmap; import flash.geom.Matrix; import flash.geom.Point; /** * PolarDistort class. Distorts any DisplayObject around a circle. Draws it to a BitmapData. */ public class PolarDistort { private var _source:DisplayObject; /** * Constructor. * @param sourceObject The DisplayObject to distort. */ public function PolarDistort(sourceObject:DisplayObject) { _source = sourceObject; } /** * Renders the distorted object to the specified output BitmapData. * @param output The BitmapData to render to. * @param innerRadius The inner radius of the resulting circle. Defaults to 0. * @param outerRadius The outer radius of the resulting circle. A value of zero will result in the circle being drawn to the full size of the BitmapData. Defaults to 0. * @param inverted If false, top of source image will be on the outer edge of circle. If true, top of image will be in the center of the circle. Defaults to false. */ public function render(output:BitmapData, innerRadius:Number = 0, outerRadius:Number = 0, inverted:Boolean = false, seam:Number = 90):void { // make all local vars up front var w:Number = output.width; var h:Number = output.height; var cx:Number = w * .5; // center of image. also serves as max radius var cy:Number = h * .5; // center of image var dx:Number; var dy:Number; var dist:Number; var angle:Number; var x1:int; var y1:int; while(seam > 180) seam -= 360; while(seam < -180) seam += 360; seam = seam * Math.PI / 180; // convert to radians if(outerRadius == 0) { // if outer is zero, set it to full radius of output image outerRadius = w / 2; } var circle:Number = Math.PI * 2; // find scale to get source width to radius * 2PI for minimal pixellation during distortion var optWidth:Number = Math.round(cx * circle); var scale:Number = optWidth / _source.width; var optHeight = Math.round(_source.height * scale); if(optWidth > 2880 || optHeight > 2880) { scale = Math.min(2880 / optWidth, 2880 / optHeight); optWidth = Math.round(optWidth * scale); optHeight = Math.round(optHeight * scale); scale = optWidth / _source.width; } // draw source to temp bitmapdata, using scale value just calculated var temp:BitmapData = new BitmapData(optWidth, optHeight, true, 0x00000000); temp.draw(_source, new Matrix(scale, 0, 0, scale), null, null, null, true); // loop through each pixel of output bitmap var ypos:int = h - 1; var xpos:int; while(ypos-- >= 0) { xpos = w - 1; while(xpos-- >= 0) { // find distance of current pixel to center dx = xpos - cx; dy = ypos - cy; dist = Math.sqrt(dx * dx + dy * dy); if(dist > innerRadius && dist < outerRadius) { // if pixel is within inner and outer radius, find angle to center angle = Math.atan2(dy, dx) - seam; if(angle < 0) { // correct angle to range of 0 - 2PI angle += circle; } // map distance and angle to x/y location of source image x1 = angle / circle * optWidth; y1 = (dist - innerRadius) / (outerRadius - innerRadius) * optHeight; // set pixel in output to mapped pixel in source if(!inverted) { output.setPixel32(xpos, ypos, temp.getPixel32(x1, optHeight - y1 - 1)); } else { output.setPixel32(xpos, ypos, temp.getPixel32(optWidth - x1 - 1, y1)); } } } } temp.dispose(); } } }