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:

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

Set up your DistortImage and DistortVO:

Actionscript:
  1. _image = new Sprite();
  2. _image.addChild(_background);
  3. this.addChild(_image);
  4.  
  5. _d = new DistortImage( _image, 2, 2 );
  6. _image.removeChild(_background);
  7.  
  8. _dVO = new DistortVO();
  9. _dVO.data = _image;

Use your favorite tweening engine to do the rest:

Actionscript:
  1. private function clickHandler(event:MouseEvent = null):void
  2. {
  3.     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});   
  4. }
  5.  
  6. private function applyMatrix($dVO:DistortVO):void
  7. {
  8.    _d.setTransform($dVO.x0, $dVO.y0, $dVO.x1, $dVO.y1, $dVO.x2, $dVO.y2 ,$dVO.x3, $dVO.y3)
  9. }

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!

Actionscript:
  1. /*
  2. ****************************
  3. * From a first idea and first implementation of Andre Michelle www.andre-michelle.com
  4. * @version 2.0
  5. * @author Thomas Pfeiffer - kiroukou - http://www.thomas-pfeiffer.info
  6. * @author Richard Lester - RichL
  7. * @author Didier Brun - foxy - http://www.foxaweb.com
  8. * @website: http://sandy.media-box.net
  9. * @description: Tesselate a movieclip into several triangles to allow free transform distorsion.
  10. * *************************
  11. * Licensed under the CREATIVE COMMONS Attribution-NonCommercial-ShareAlike 2.0
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. * http://creativecommons.org/licenses/by-nc-sa/2.0/fr/deed.en_GB
  15. ***************************
  16. * DistortImage class
  17. * Availability : Flash Player 8.
  18. **********************************************
  19. */
  20. import flash.geom.Matrix;
  21. import flash.display.BitmapData;
  22. class DistortImage
  23. {
  24.     // -- texture to distord
  25.     public var texture:BitmapData;
  26.     /*
  27.     * Constructor
  28.     * @param mc MovieClip : the movieClip containing the distorded picture
  29.     * @param symbolId String : th link name of the picture in the library
  30.     * @param vseg Number : the vertical precision
  31.     * @param hseg Number : the horizontal precision
  32.     * @throws: An error in the second constructor parameter isn't a BitmapData or a MovieClip
  33.     */
  34.     public function DistortImage( mc: MovieClip, ptexture, vseg: Number, hseg: Number )
  35.     {
  36.         _mc = mc;
  37.         if( ptexture instanceof BitmapData )
  38.         {
  39.             texture = ptexture;
  40.         }
  41.         else if( ptexture instanceof MovieClip )
  42.         {
  43.             texture = new BitmapData( ptexture._width, ptexture._height );
  44.             texture.draw( ptexture );
  45.         }
  46.         else
  47.         {
  48.             throw new Error('Second argument in DistortImage class must be a BitmapData object or a Movieclip');
  49.         }
  50.         _vseg = vseg || 0;
  51.         _hseg = hseg || 0;
  52.         // --
  53.         _w = texture.width ;
  54.         _h = texture.height;
  55.         // --
  56.         _aMcs   = new Array();
  57.         _p   = new Array();
  58.         _tri    = new Array();
  59.         // --
  60.         __init();
  61.     }
  62.     /**
  63.     * setTransform
  64.     *
  65.     * @param x0 Number the horizontal coordinate of the first point
  66.     * @param y0 Number the vertical coordinate of the first point
  67.     * @param x1 Number the horizontal coordinate of the second point
  68.     * @param y1 Number the vertical coordinate of the second point
  69.     * @param x2 Number the horizontal coordinate of the third point
  70.     * @param y2 Number the vertical coordinate of the third point
  71.     * @param x3 Number the horizontal coordinate of the fourth point
  72.     * @param y3 Number the vertical coordinate of the fourth point
  73.     *
  74.     * @description : Distord the bitmap to ajust it to those points.
  75.     */
  76.     function setTransform( x0:Number , y0:Number , x1:Number , y1:Number , x2:Number , y2:Number , x3:Number , y3:Number ): Void
  77.     {
  78.         var w:Number = _w;
  79.         var h:Number = _h;
  80.         var dx30:Number = x3 - x0;
  81.         var dy30:Number = y3 - y0;
  82.         var dx21:Number = x2 - x1;
  83.         var dy21:Number = y2 - y1;
  84.         var l:Number = _p.length;
  85.         while( --l> -1 )
  86.         {
  87.             var point:Object = _p[ l ];
  88.             var gx = ( point.x - _xMin ) / w;
  89.             var gy = ( point.y - _yMin ) / h;
  90.             var bx = x0 + gy * ( dx30 );
  91.             var by = y0 + gy * ( dy30 );
  92.             point.sx = bx + gx * ( ( x1 + gy * ( dx21 ) ) - bx );
  93.             point.sy = by + gx * ( ( y1 + gy * ( dy21 ) ) - by );
  94.         }
  95.         __render();
  96.     }
  97.     /////////////////////////
  98.     ///  PRIVATE METHODS  ///
  99.     /////////////////////////
  100.     private function __init( Void ): Void
  101.     {
  102.         _p = new Array();
  103.         _tri = new Array();
  104.         var ix:Number, iy:Number;
  105.         var w2: Number = _w / 2;
  106.         var h2: Number = _h / 2;
  107.         _xMin = _yMin = 0;
  108.         _xMax = _w; _yMax = _h;
  109.         _hsLen = _w / ( _hseg + 1 );
  110.         _vsLen = _h / ( _vseg + 1 );
  111.         var x:Number, y:Number;
  112.         var p0:Object, p1:Object, p2:Object;
  113.         // -- we create the points
  114.         for ( ix = 0 ; ix <_hseg + 2 ; ix++ )
  115.         {
  116.             for ( iy = 0 ; iy <_vseg + 2 ; iy++ )
  117.             {
  118.                 x = ix * _hsLen;
  119.                 y = iy * _vsLen;
  120.                 _p.push( { x: x, y: y, sx: x, sy: y } );
  121.             }
  122.         }
  123.         // -- we create the triangles
  124.         for ( ix = 0 ; ix <_vseg + 1 ; ix++ )
  125.         {
  126.             for ( iy = 0 ; iy <_hseg + 1 ; iy++ )
  127.             {
  128.                 p0 = _p[ iy + ix * ( _hseg + 2 ) ];
  129.                 p1 = _p[ iy + ix * ( _hseg + 2 ) + 1 ];
  130.                 p2 = _p[ iy + ( ix + 1 ) * ( _hseg + 2 ) ];
  131.                 __addTriangle( p0, p1, p2 );
  132.                 // --
  133.                 p0 = _p[ iy + ( ix + 1 ) * ( _vseg + 2 ) + 1 ];
  134.                 p1 = _p[ iy + ( ix + 1 ) * ( _vseg + 2 ) ];
  135.                 p2 = _p[ iy + ix * ( _vseg + 2 ) + 1 ];
  136.                 __addTriangle( p0, p1, p2 );
  137.             }
  138.         }
  139.         __render();
  140.     }
  141.     private function __addTriangle( p0:Object, p1:Object, p2:Object ):Void
  142.     {
  143.         var u0:Number, v0:Number, u1:Number, v1:Number, u2:Number, v2:Number;
  144.         var tMat:Object = {};
  145.         // --
  146.         u0 = p0.x; v0 = p0.y;
  147.         u1 = p1.x; v1 = p1.y;
  148.         u2 = p2.x; v2 = p2.y;
  149.         tMat.tx = -v0*(_w / (v1 - v0));
  150.         tMat.ty = -u0*(_h / (u2 - u0));
  151.         tMat.a = tMat.d = 0;
  152.         tMat.b = _h / (u2 - u0);
  153.         tMat.c = _w / (v1 - v0);
  154.         // --
  155.         _tri.push( [p0, p1, p2, tMat] );
  156.     }
  157.     private function __render( Void ): Void
  158.     {
  159.         var vertices: Array;
  160.         var p0, p1, p2:Object;
  161.         var x0:Number, y0:Number;
  162.         var ih:Number = 1/_h, iw:Number = 1/_w;
  163.         var c:MovieClip = _mc; c.clear();
  164.         var a:Array;
  165.         var sM = {};
  166.         var tM = {};
  167.         //--
  168.         var l:Number = _tri.length;
  169.         while( --l> -1 )
  170.         {
  171.             a   = _tri[ l ];
  172.             p0  = a[0];
  173.             p1  = a[1];
  174.             p2  = a[2];
  175.             tM = a[3];
  176.             // --
  177.             sM.a = ( p1.sx - ( x0 = p0.sx ) ) * iw;
  178.             sM.b = ( p1.sy - ( y0 = p0.sy ) ) * iw;
  179.             sM.c = ( p2.sx - x0 ) * ih;
  180.             sM.d = ( p2.sy - y0 ) * ih;
  181.             sM.tx = x0;
  182.             sM.ty = y0;
  183.             // --
  184.             sM = __concat( sM, tM );
  185.             c.beginBitmapFill( texture, sM, false, false );
  186.             c.moveTo( x0, y0 );
  187.             c.lineTo( p1.sx, p1.sy );
  188.             c.lineTo( p2.sx, p2.sy );
  189.             c.endFill();
  190.         }
  191.     }
  192.     private function __concat( m1, m2 ):Object
  193.     {   //Relies on the original triangles being right angled with p0 being the right angle.
  194.         //Therefore a = d = zero (before and after invert)
  195.         var mat = {};
  196.         mat.a  = m1.c * m2.b;
  197.         mat.b  = m1.d * m2.b;
  198.         mat.c  = m1.a * m2.c;
  199.         mat.d  = m1.b * m2.c;
  200.         mat.tx = m1.a * m2.tx + m1.c * m2.ty + m1.tx;
  201.         mat.ty = m1.b * m2.tx + m1.d * m2.ty + m1.ty;
  202.         return mat;
  203.     }
  204.     /////////////////////////
  205.     /// PRIVATE PROPERTIES //
  206.     /////////////////////////
  207.     private var _mc:MovieClip;
  208.     private var _w:Number;
  209.     private var _h:Number;
  210.     private var _xMin:Number, _xMax:Number, _yMin:Number, _yMax:Number;
  211.     // -- picture segmentation properties
  212.     private var _hseg:Number;
  213.     private var _vseg:Number;
  214.     private var _hsLen:Number;
  215.     private var _vsLen:Number;
  216.     // -- arrays of differents datas types
  217.     private var _p:Array;
  218.     private var _tri:Array;
  219.     private var _aMcs:Array;
  220. }

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

Tags: , ,