Beautifl - Flash Gallery

Thumbnail : Curved text using DisplacementMapFilter
Curved text using DisplacementMapFilter
makc3d 2010-06-10 see code comments

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

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;
		}
	}
}