package { import com.bit101.components.PushButton; import com.bit101.components.TextArea; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Sprite; import flash.events.Event; import flash.filters.DisplacementMapFilter; import flash.geom.Matrix; import flash.geom.Point; /** * Curved text using DisplacementMapFilter * Port of AS2 app made for Alan Zhao years ago * 7px fonts are not really good for this, but duh * @see http://makc.coverthesky.com/FlashFX/ffx.php?id=7 * @license WTFPLv2 * @author makc */ [SWF(width=465,height=465)] public class CurvedText extends Sprite { public function CurvedText () { stage.scaleMode = "noScale"; ta = new TextArea (this, 0, 0, li); ta.setSize (465, 300); for (var i:int = -1; i < maps.length; i++) with (new PushButton (this, 25 * (i + 2), 430, (i + 1).toString (), onButtonClick)) width = height; } private var ta:TextArea; private var li:String = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n" + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n" + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n" + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n" + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n"; private function onButtonClick (e:Event):void { var map:BitmapData = BitmapData (maps [parseInt (PushButton (e.target).label) - 1]); applyEffect (ta.textField, ta.textField.width, ta.textField.height, map); } private function applyEffect (clip:DisplayObject, w:Number, h:Number, map:BitmapData):void { // if there ever will be need in zoom, pass it var zoom:Number = 1; // if there is a filter already, remove it clip.filters = []; // if empty map supplied, resize clip to original size and do nothing else clip.scaleX = clip.scaleY = 1; if (map == null) return; // scale basic map up to required dimensions // (for some reason flash puts textbox border outside of rectangle, so we make larger map) var k:Number = 0.05 / zoom; var w1:Number = (1 + k) * w, x1:Number = -0.5 * k * w; var h1:Number = (1 + k) * h, y1:Number = -0.5 * k * h; var dst:BitmapData = new BitmapData (w1, h1, true); var m1:Matrix = new Matrix; m1.scale (w1 / map.width, h1 / map.height); m1.translate (x1, y1); dst.draw (map, m1, null, null, null, true); // make the filter out of dst and attach it to clip clip.filters = [new DisplacementMapFilter (dst, new Point, 4, 2, 256 * m1.a, 256 * m1.d, "clamp")]; // resize clip to specified dimensions clip.scaleX = 1 / zoom * w / clip.width; clip.scaleY = 1 / zoom * h / clip.height; } private var maps:Array = [ getArcMap (true), getArcMap (false), getBridgeOrValleyMap (true), getBridgeOrValleyMap (false), getBulgeMap (), getPinchMap (), getCurveMap (true), getCurveMap (false), getRoofMap (true), getRoofMap (false), getWedgeOrWidenMap (false), getWedgeOrWidenMap (true) ]; // base 512x256 maps --8<----------------------------------- private function getArcMap (up:Boolean):BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // place curve center K (>1) times h below bitmap var K:Number = 1.65, xK:Number = w/2, yK:Number = K * h; // find upper intersection of bitmap rectanle with curve var B:Number = Math.sqrt (yK*yK - xK*xK); var yT:Number = yK - B; // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // dy from dx var dx:Number = xK - x, dy:Number = yK - Math.sqrt (yK*yK - dx*dx); // map to source rectangle var xS:int = x; var yS:int = Math.floor(h * (y - dy) / (h - yT)); if (yS < 0) yS = 0; if (yS > h-1) yS = h-1; // make color var c:uint = bm * (128 + xS - x) + gm * (128 + yS - y); // quick hack to support both directions b.setPixel (x, up ? y : h - 1 - y, up ? c : rm - 1 - c); } return b; } private function getBridgeOrValleyMap (valley:Boolean):BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // place curve center K (>1) times h below bitmap var K:Number = 1.65, xK:Number = w/2, yK:Number = K * h; // find upper intersection of bitmap rectanle with curve var B:Number = Math.sqrt (yK*yK - xK*xK); var yT:Number = yK - B; // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // dy from dx var dx:Number = xK - x, dy:Number = yK - Math.sqrt (yK*yK - dx*dx); // map to source rectangle var xS:int = x; var yS:int = Math.floor(h * (y - dy) / (h - dy)); if (yS < 0) yS = 0; if (yS > h-1) yS = h-1; // make color var c:uint = bm * (128 + xS - x) + gm * (128 + yS - y); // quick hack to support both directions b.setPixel (x, valley ? y : h - 1 - y, valley ? c : rm - 1 - c); } return b; } private function getBulgeMap ():BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // place curve center K (>1) times h below bitmap var K:Number = 1.65, xK:Number = w/2, yK:Number = K * h; // find upper intersection of bitmap rectanle with curve var M:Number = 0.8; var B:Number = Math.sqrt (yK*yK - xK*xK); var yT:Number = M * (yK - B); // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // dy from dx var dx:Number = xK - x, dy:Number = M * (yK - Math.sqrt (yK*yK - dx*dx)); // map to source rectangle var xS:int = x; var yS:int = Math.floor(h * (y - dy) / (h - 2*dy)); if (yS < 0) yS = 0; if (yS > h-1) yS = h-1; // make color b.setPixel (x, y, bm * (128 + xS - x) + gm * (128 + yS - y)); } return b; } private function getPinchMap ():BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // place curve center K (>1) times h below bitmap var K:Number = 1.65, xK:Number = w/2, yK:Number = K * h; // find upper intersection of bitmap rectanle with curve var M:Number = 0.8; var B:Number = Math.sqrt (yK*yK - xK*xK); var yT:Number = M * (yK - B); // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // dy from dx var dx:Number = xK - x, dy:Number = M * ( yT/M - (yK - Math.sqrt (yK*yK - dx*dx)) ); // map to source rectangle var xS:int = x; var yS:int = Math.floor(h * (y - dy) / (h - 2*dy)); if (yS < 0) yS = 0; if (yS > h-1) yS = h-1; // make color b.setPixel (x, y, bm * (128 + xS - x) + gm * (128 + yS - y)); } return b; } private function getCurveMap (up:Boolean):BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // place curve center K (>1) times h below bitmap var K:Number = 1.65, xK:Number = w/2, yK:Number = K * h; // find upper intersection of bitmap rectanle with curve var A:Number = Math.sqrt (yK*yK - xK*xK), aK:Number = Math.asin (xK / yK); // find distance to bottom intersection var rK:Number = yK * (yK - h) / A; // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // angle and distance for (x, y) var d:Number = Math.sqrt ((x - xK)*(x - xK) + (y - yK)*(y - yK)); var a:Number = Math.asin ((x - xK) / d); // ratios to curve params var rd:Number = (d - rK) / (yK - rK), ra:Number = a / aK; rd = (rd > 1) ? 1 : ((rd < 0) ? 0 : rd); ra = (ra > 1) ? 1 : ((ra < -1) ? -1 : ra); // map to source rectangle var xS:int = Math.round (xK * (1 + ra)); var yS:int = Math.round (h * (1 - rd)); // make color var c:uint = bm * (128 + xS - x) + gm * (128 + yS - y); // quick hack to support both directions b.setPixel (up ? x : w - 1 - x, up ? y : h - 1 - y, up ? c : rm - 1 - c); } return b; } private function getRoofMap (up:Boolean):BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // roof height var H:Number; if (x < 256) H = 128 + 128 * x / (0.5*w); else H = 128 + 128 * (512 - x) / (0.5*w); // ratio var r:Number = (h - y) / H; // map to source rectangle var xS:int = x; var yS:int = 255 - Math.floor (r * h); if (yS < 0) yS = 0; // make color var c:uint = bm * (128 + xS - x) + gm * (128 + yS - y); // quick hack to support both directions b.setPixel (x, up ? y : h - 1 - y, up ? c : rm - 1 - c); } return b; } private function getWedgeOrWidenMap (wedge:Boolean):BitmapData { var rm:int = 65536, gm:int = 256, bm:int = 1; var b:BitmapData = new BitmapData (512, 256, true), w:int = b.width, h:int = b.height; // for (var x:int = 0; x < w; x++) for (var y:int = 0; y < h; y++) { // wedge half-height var H:Number = 128 - 256 * x / w / 3; // ratio var r:Number = (y - h/2) / H; // map to source rectangle var xS:int = x; var yS:int = 128 + Math.floor (r * h/2); if (yS < 0) yS = 0; if (yS > h-1) yS = h-1; // make color var c:uint = bm * (128 + xS - x) + gm * (128 + yS - y); // quick hack to support both directions b.setPixel (wedge ? x : w - 1 - x, y, c); } return b; } } } Curved text using DisplacementMapFilter