Distort and taper images in BOTH ActionScript 2 and 3 without a big 3D engine. Is it possible? YES!

**Update
(12/15/09): Alex Uhlmann made this class even better! Distortion Effects (AS3 only)

(06/24/09): After doing some more research I found that Ruben Swieringa re-wrote this same class into AS3 exactly two years ago. I wish I would have found his post earlier today :)
****

Problem:
ActionScript does not have native support image distorting and tapering (Flash Player 9 and below). After a lot of digging I stumbled upon an AS2 class that distorts images and it’s less than 200 lines of code.

Solution:
You can use the AS2 class ‘as is’ from FlashSandy. Or, if you’d prefer to code in a AS3, I’ve ported it over and provided an example:

Alternate Page.

Get Adobe Flash player

Move the red squares by clicking, dragging, and releasing them on the stage. Source View

What’s the total filesize?
Well, lets see. The demo above is 53KB for the swf- 41.5 for the embedded image – 3KB for TweenLite = 8.5KB :)

How does it work?
Embed an image:

[Embed(source="/assets/glacier.jpg")]
private var Background: Class;
private var _background:BitmapAsset = new Background();		

Set up your DistortImage and DistortVO:

_image = new Sprite();
_image.addChild(_background);
this.addChild(_image);

_d = new DistortImage( _image, 2, 2 );
_image.removeChild(_background);

_dVO = new DistortVO();
_dVO.data = _image;

Use your favorite tweening engine to do the rest:

private function clickHandler(event:MouseEvent = null):void
{
	TweenLite.to(_dVO, 0.5, {x0:_tl.x, y0:_tl.y, x1:_tr.x, y1:_tr.y, x2:_bl.x, y2:_bl.y, x3:_br.x, y3:_br.y, onUpdate:applyMatrix, onUpdateParams:[_dVO], ease:Cubic.easeOut});			
}

private function applyMatrix($dVO:DistortVO):void 
{
   _d.setTransform($dVO.x0, $dVO.y0, $dVO.x1, $dVO.y1, $dVO.x2, $dVO.y2 ,$dVO.x3, $dVO.y3);	
}

The following is ActionScript 2 related
I am re-posting the AS2 code for redundancy purposes. The original code (DistordImage) is sitting on a broken link and I feel lucky to have found the sample code on FlashSandy. The above example can be done in AS2 which is really cool. Thank you to all of the authors of the DistortImage AS2 class!

/*
****************************
* From a first idea and first implementation of Andre Michelle www.andre-michelle.com
* @version 2.0
* @author Thomas Pfeiffer - kiroukou - http://www.thomas-pfeiffer.info
* @author Richard Lester - RichL
* @author Didier Brun - foxy - http://www.foxaweb.com
* @website: http://sandy.media-box.net
* @description: Tesselate a movieclip into several triangles to allow free transform distorsion.
* *************************
* Licensed under the CREATIVE COMMONS Attribution-NonCommercial-ShareAlike 2.0
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://creativecommons.org/licenses/by-nc-sa/2.0/fr/deed.en_GB
***************************
* DistortImage class
* Availability : Flash Player 8.
**********************************************
*/
import flash.geom.Matrix;
import flash.display.BitmapData;
class DistortImage
{
	// -- texture to distord
	public var texture:BitmapData;
	/*
	* Constructor
	* @param mc MovieClip : the movieClip containing the distorded picture
	* @param symbolId String : th link name of the picture in the library
	* @param vseg Number : the vertical precision
	* @param hseg Number : the horizontal precision
	* @throws: An error in the second constructor parameter isn't a BitmapData or a MovieClip
	*/
	public function DistortImage( mc: MovieClip, ptexture, vseg: Number, hseg: Number )
	{
		_mc = mc;
		if( ptexture instanceof BitmapData )
		{
			texture = ptexture;
		}
		else if( ptexture instanceof MovieClip )
		{
			texture = new BitmapData( ptexture._width, ptexture._height );
			texture.draw( ptexture );
		}
		else
		{
			throw new Error('Second argument in DistortImage class must be a BitmapData object or a Movieclip');
		}
		_vseg = vseg || 0;
		_hseg = hseg || 0;
		// --
		_w = texture.width ;
		_h = texture.height;
		// --
		_aMcs   = new Array();
		_p   = new Array();
		_tri	= new Array();
		// --
		__init();
	}
	/**
	* setTransform
	*
	* @param x0 Number the horizontal coordinate of the first point
	* @param y0 Number the vertical coordinate of the first point
	* @param x1 Number the horizontal coordinate of the second point
	* @param y1 Number the vertical coordinate of the second point
	* @param x2 Number the horizontal coordinate of the third point
	* @param y2 Number the vertical coordinate of the third point
	* @param x3 Number the horizontal coordinate of the fourth point
	* @param y3 Number the vertical coordinate of the fourth point
	*
	* @description : Distord the bitmap to ajust it to those points.
	*/
	function setTransform( x0:Number , y0:Number , x1:Number , y1:Number , x2:Number , y2:Number , x3:Number , y3:Number ): Void
	{
		var w:Number = _w;
		var h:Number = _h;
		var dx30:Number = x3 - x0;
		var dy30:Number = y3 - y0;
		var dx21:Number = x2 - x1;
		var dy21:Number = y2 - y1;
		var l:Number = _p.length;
		while( --l> -1 )
		{
			var point:Object = _p[ l ];
			var gx = ( point.x - _xMin ) / w;
			var gy = ( point.y - _yMin ) / h;
			var bx = x0 + gy * ( dx30 );
			var by = y0 + gy * ( dy30 );
			point.sx = bx + gx * ( ( x1 + gy * ( dx21 ) ) - bx );
			point.sy = by + gx * ( ( y1 + gy * ( dy21 ) ) - by );
		}
		__render();
	}
	/////////////////////////
	///  PRIVATE METHODS  ///
	/////////////////////////
	private function __init( Void ): Void
	{
		_p = new Array();
		_tri = new Array();
		var ix:Number, iy:Number;
		var w2: Number = _w / 2;
		var h2: Number = _h / 2;
		_xMin = _yMin = 0;
		_xMax = _w; _yMax = _h;
		_hsLen = _w / ( _hseg + 1 );
		_vsLen = _h / ( _vseg + 1 );
		var x:Number, y:Number;
		var p0:Object, p1:Object, p2:Object;
		// -- we create the points
		for ( ix = 0 ; ix <_hseg + 2 ; ix++ )
		{
			for ( iy = 0 ; iy <_vseg + 2 ; iy++ )
			{
				x = ix * _hsLen;
				y = iy * _vsLen;
				_p.push( { x: x, y: y, sx: x, sy: y } );
			}
		}
		// -- we create the triangles
		for ( ix = 0 ; ix <_vseg + 1 ; ix++ )
		{
			for ( iy = 0 ; iy <_hseg + 1 ; iy++ )
			{
				p0 = _p[ iy + ix * ( _hseg + 2 ) ];
				p1 = _p[ iy + ix * ( _hseg + 2 ) + 1 ];
				p2 = _p[ iy + ( ix + 1 ) * ( _hseg + 2 ) ];
				__addTriangle( p0, p1, p2 );
				// --
				p0 = _p[ iy + ( ix + 1 ) * ( _vseg + 2 ) + 1 ];
				p1 = _p[ iy + ( ix + 1 ) * ( _vseg + 2 ) ];
				p2 = _p[ iy + ix * ( _vseg + 2 ) + 1 ];
				__addTriangle( p0, p1, p2 );
			}
		}
		__render();
	}
	private function __addTriangle( p0:Object, p1:Object, p2:Object ):Void
	{
		var u0:Number, v0:Number, u1:Number, v1:Number, u2:Number, v2:Number;
		var tMat:Object = {};
		// --
		u0 = p0.x; v0 = p0.y;
		u1 = p1.x; v1 = p1.y;
		u2 = p2.x; v2 = p2.y;
		tMat.tx = -v0*(_w / (v1 - v0));
		tMat.ty = -u0*(_h / (u2 - u0));
		tMat.a = tMat.d = 0;
		tMat.b = _h / (u2 - u0);
		tMat.c = _w / (v1 - v0);
		// --
		_tri.push( [p0, p1, p2, tMat] );
	}
	private function __render( Void ): Void
	{
		var vertices: Array;
		var p0, p1, p2:Object;
		var x0:Number, y0:Number;
		var ih:Number = 1/_h, iw:Number = 1/_w;
		var c:MovieClip = _mc; c.clear();
		var a:Array;
		var sM = {};
		var tM = {};
		//--
		var l:Number = _tri.length;
		while( --l> -1 )
		{
			a   = _tri[ l ];
			p0  = a[0];
			p1  = a[1];
			p2  = a[2];
			tM = a[3];
			// --
			sM.a = ( p1.sx - ( x0 = p0.sx ) ) * iw;
			sM.b = ( p1.sy - ( y0 = p0.sy ) ) * iw;
			sM.c = ( p2.sx - x0 ) * ih;
			sM.d = ( p2.sy - y0 ) * ih;
			sM.tx = x0;
			sM.ty = y0;
			// --
			sM = __concat( sM, tM );
			c.beginBitmapFill( texture, sM, false, false );
			c.moveTo( x0, y0 );
			c.lineTo( p1.sx, p1.sy );
			c.lineTo( p2.sx, p2.sy );
			c.endFill();
		}
	}
	private function __concat( m1, m2 ):Object
	{   //Relies on the original triangles being right angled with p0 being the right angle.
		//Therefore a = d = zero (before and after invert)
		var mat = {};
		mat.a  = m1.c * m2.b;
		mat.b  = m1.d * m2.b;
		mat.c  = m1.a * m2.c;
		mat.d  = m1.b * m2.c;
		mat.tx = m1.a * m2.tx + m1.c * m2.ty + m1.tx;
		mat.ty = m1.b * m2.tx + m1.d * m2.ty + m1.ty;
		return mat;
	}
	/////////////////////////
	/// PRIVATE PROPERTIES //
	/////////////////////////
	private var _mc:MovieClip;
	private var _w:Number;
	private var _h:Number;
	private var _xMin:Number, _xMax:Number, _yMin:Number, _yMax:Number;
	// -- picture segmentation properties
	private var _hseg:Number;
	private var _vseg:Number;
	private var _hsLen:Number;
	private var _vsLen:Number;
	// -- arrays of differents datas types
	private var _p:Array;
	private var _tri:Array;
	private var _aMcs:Array;
}

Resources:
Understanding the Transformation Matrix in Flash 8
FlashSandy
Ruben Swieringa – DistortImage

One thought on “Image Distortion and Tapering in ActionScript

Comments are closed.