DistordImage, the way to distord bitmaps by code

November 1st, 2005 by Kiroukou :: Resources, en :: RSS 2.0 :: trackback

Who never hoped to be able to transform some bitmaps directly by code. Yes We can now skew and scale a MovieClip in Flash8, but when you need to map a bitmap on a more complex support, you just can't.

But thanks to Andre Michelle we were able to do that for two years, but this technics wasn't really optimized and precise.

Today Flash 8 brings some new possibilities making this very accurate.

Here is the example :
placeholder for flash movie

Play with the anchors clips to distort the picture :)

Here is the class :

JavaScript:
  1. /**********************************************
  2. * Copyright (c) 2005 Thomas PFEIFFER. All rights
  3. * reserved.
  4. *
  5. * Licensed under the CREATIVE COMMONS Attribution-NonCommercial-ShareAlike 2.0
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *                      http://creativecommons.org/licenses/by-nc-sa/2.0/fr/deed.en_GB
  9. *
  10. * DistortImage class
  11. * Availability : Flash Player 8.
  12. *
  13. * Description
  14. * _________
  15. * Tesselate a movieclip into several triangles
  16. * to allow free transform distorsion.
  17. ****************************
  18. * From an idea and a first implementation from (C) Andre Michelle
  19. * http://www.andre-michelle.com
  20. ****************************
  21. * @author Thomas Pfeiffer - kiroukou
  22. * @contact kiroukou@@gmail..com
  23. **********************************************/
  24.  
  25. import flash.geom.Matrix;
  26. import flash.display.BitmapData;
  27.  
  28. class com.kiroukou.graphics.DistordImage
  29. {
  30.     private var _mc: MovieClip;
  31.     private var _w: Number;
  32.     private var _h: Number;
  33.     // -- skew and translation matrix
  34.     private var _sMat:Matrix ;
  35.     private var _tMat:Matrix ;
  36.  
  37.     private var _xMin, _xMax, _yMin, _yMax: Number;
  38.  
  39.     private var _hseg: Number;
  40.     private var _vseg: Number;
  41.  
  42.     private var _hsLen: Number;
  43.     private var _vsLen: Number;
  44.  
  45.     private var _p: Array;
  46.     private var _tri: Array;
  47.    
  48.     private var _texture:BitmapData;
  49.  
  50.     /* Constructor
  51.      *
  52.      * @param mc MovieClip :  the movieClip containing the distorded picture
  53.      * @param symbolId String : th link name of the picture in the library
  54.      * @param vseg Number : the vertical precision
  55.      * @param hseg Number : the horizontal precision
  56.      */
  57.     public function DistordImage( mc: MovieClip, symbolId: String, vseg: Number, hseg: Number )
  58.     {
  59.         _mc = mc;
  60.         _texture = BitmapData.loadBitmap( symbolId );
  61.         _vseg = vseg;
  62.         _hseg = hseg;
  63.        
  64.         _w = _texture.width ;
  65.         _h = _texture.height;
  66.         __init();
  67.        
  68.     }
  69.  
  70.  
  71.     private function __init( Void ): Void
  72.     {
  73.         _p = new Array();
  74.         _tri = new Array();
  75.        
  76.         var ix: Number;
  77.         var iy: Number;
  78.  
  79.         var w2: Number = _w / 2;
  80.         var h2: Number = _h / 2;
  81.  
  82.         _xMin = _yMin = 0;
  83.         _xMax = _w; _yMax = _h;
  84.        
  85.         _hsLen = _w / ( _hseg + 1 );
  86.         _vsLen = _h / ( _vseg + 1 );
  87.  
  88.         var x: Number, y: Number;
  89.         // -- we create the points
  90.         for ( ix = 0 ; ix <_vseg + 2 ; ix++ )
  91.         {
  92.             for ( iy = 0 ; iy <_hseg + 2 ; iy++ )
  93.             {
  94.                 x = ix * _hsLen;
  95.                 y = iy * _vsLen;
  96.                 _p.push( { x: x, y: y, sx: x, sy: y } );
  97.             }
  98.         }
  99.         // -- we create the triangles
  100.         for ( ix = 0 ; ix <_vseg + 1 ; ix++ )
  101.         {
  102.             for ( iy = 0 ; iy <_hseg + 1 ; iy++ )
  103.             {
  104.                 _tri.push([ _p[ iy + ix * ( _hseg + 2 ) ] , _p[ iy + ix * ( _hseg + 2 ) + 1 ] , _p[ iy + ( ix + 1 ) * ( _hseg + 2 ) ] ] );
  105.                 _tri.push([ _p[ iy + ( ix + 1 ) * ( _hseg + 2 ) + 1 ] , _p[ iy + ( ix + 1 ) * ( _hseg + 2 ) ] , _p[ iy + ix * ( _hseg + 2 ) + 1 ] ] );
  106.             }
  107.         }
  108.  
  109.         __render();
  110.     }
  111.  
  112.     /* setTransform
  113.      *
  114.      * @param x0 Number the horizontal coordinate of the first point
  115.      * @param y0 Number the vertical coordinate of the first point 
  116.      * @param x1 Number the horizontal coordinate of the second point
  117.      * @param y1 Number the vertical coordinate of the second point 
  118.      * @param x2 Number the horizontal coordinate of the third point
  119.      * @param y2 Number the vertical coordinate of the third point 
  120.      * @param x3 Number the horizontal coordinate of the fourth point
  121.      * @param y3 Number the vertical coordinate of the fourth point 
  122.      *
  123.      * @description : Distord the bitmap to ajust it to those points.
  124.      */
  125.     function setTransform( x0:Number , y0:Number , x1:Number , y1:Number , x2:Number , y2:Number , x3:Number , y3:Number ): Void
  126.     {
  127.         var w:Number = _w;
  128.         var h:Number = _h;
  129.         var dx30:Number = x3 - x0;
  130.         var dy30:Number = y3 - y0;
  131.         var dx21:Number = x2 - x1;
  132.         var dy21:Number = y2 - y1;
  133.         var l:Number = _p.length;
  134.         while( --l> -1 )
  135.         {
  136.             var point:Object = _p[ l ];
  137.             var gx = ( point.x - _xMin ) / w;
  138.             var gy = ( point.y - _yMin ) / h;
  139.             var bx = x0 + gy * ( dx30 );
  140.             var by = y0 + gy * ( dy30 );
  141.  
  142.             point.sx = bx + gx * ( ( x1 + gy * ( dx21 ) ) - bx );
  143.             point.sy = by + gx * ( ( y1 + gy * ( dy21 ) ) - by );
  144.         }
  145.  
  146.         __render();
  147.     }
  148.  
  149.     private function __render( Void ): Void
  150.     {
  151.  
  152.         var t: Number;
  153.         var vertices: Array;
  154.         var p0, p1, p2:Object;
  155.         var c:MovieClip = _mc;
  156.         var a:Array;
  157.         c.clear();
  158.         _sMat = new Matrix();
  159.         _tMat = new Matrix();
  160.        
  161.         var l:Number = _tri.length;
  162.         while( --l> -1 )
  163.         {
  164.             a = _tri[ l ];
  165.             p0 = a[0];
  166.             p1 = a[1];
  167.             p2 = a[2];
  168.             var x0: Number = p0.sx;
  169.             var y0: Number = p0.sy;
  170.             var x1: Number = p1.sx;
  171.             var y1: Number = p1.sy;
  172.             var x2: Number = p2.sx;
  173.             var y2: Number = p2.sy;
  174.                
  175.             var u0: Number = p0.x;
  176.             var v0: Number = p0.y;
  177.             var u1: Number = p1.x;
  178.             var v1: Number = p1.y;
  179.             var u2: Number = p2.x;
  180.             var v2: Number = p2.y;
  181.  
  182.             _tMat.tx = u0;
  183.             _tMat.ty = v0;
  184.        
  185.             _tMat.a = ( u1 - u0 ) / _w;
  186.             _tMat.b = ( v1 - v0 ) / _w;
  187.             _tMat.c = ( u2 - u0 ) / _h;
  188.             _tMat.d = ( v2 - v0 ) / _h;
  189.        
  190.             _sMat.a = ( x1 - x0 ) / _w;
  191.             _sMat.b = ( y1 - y0 ) / _w;
  192.             _sMat.c = ( x2 - x0 ) / _h;
  193.             _sMat.d = ( y2 - y0 ) / _h;
  194.        
  195.             _sMat.tx = x0;
  196.             _sMat.ty = y0;
  197.        
  198.             _tMat.invert();
  199.             _tMat.concat( _sMat );
  200.            
  201.             c.beginBitmapFill( _texture, _tMat, false, false );
  202.             c.moveTo( x0, y0 );
  203.             c.lineTo( x1, y1 );
  204.             c.lineTo( x2, y2 );
  205.             c.endFill();
  206.         }
  207.     }
  208. }

You can find the fla : here

Have fun :)

37 Responses to “DistordImage, the way to distord bitmaps by code”

  1. November 3rd, 2005 at 11:24 pm :: William Sheller

    Je connais pas la distortion en as. Tu écris :

    * Tesselate a movieclip into several triangles
    * to allow free transform distorsion.

    Pourquoi faut-il creer des triangles ? Avec Flash8, ses matrices et ses transform, le skewing n'est plus (+ou-) automatique ?

  2. November 4th, 2005 at 2:07 pm :: kiroukou

    Salut William
    Le but de decouper cette image en triangles, vient surtout du fait que c'est plus simple. Quand on veut deformer une image il y a beacoup de choses à faire et à prendre en compte. L'idée de "mailler" une image est très utile, et le choix du triangle vient du fait que l'on retrouve ainsi une base (au sens mathématique) d'un repère à 2 dimensions (meme dimension spatial que l'image). Ainsi on arrive à repérer un point par rapport à un repère local (la surface triangulaire auquel ce point appartient), et du coup on peut retrouver sa position apres modification du centre de son repere local (homothétie et rotation).

    Apres là intervient la manipulation mathématique d'Andre Michelle qui a reussi a recuperer la transformation de chaque repere et calculer la matrice associée. Ainsi en passant a la methode de dessin (draw) la matrice equivalent, il retrouve le morceau d'image deformé correctement.

    On aurait pu tout refaire "à la main" en manipulant les pixels, mais il faut refaire un échantillonage des points, et pour avoir des resultats corrects, un echantilonnage bilinéaire est nécéssaire, ce qui rendrai les calculs tres tres lourds.

    Ensuite pour ta deuxieme question, je pense que tu te rend compte que le rendu que tu as sur l'exemple, est loin d'un simple skew ;)

    ++

  3. November 4th, 2005 at 4:23 pm :: William Sheller

    Ok, merci pour ces explications :)

    - Pour le skew, en fait, ce qui manque a flash c'est une fonction quad comme celle de director. J'ai pas regardé dans l'as3 mais je pense que ce ne doit pas y etre. J'imagine que ca doit etre dans votre wishlist :)

    - Pour la partie améliorée d'Andre Michelle, vous savez si c'est lui qui trouve ces fonctions mathématiques miraculeuses où si il les trouve deci-delà ?

  4. November 4th, 2005 at 5:35 pm :: kiroukou

    En effet quad est exactementce qui nous faudrai :)

    Sinon pour la formule d'andre michelle, c pas tres dur mathématiquement, c juste un changement de repère, mais il reste qu'il à eu l'idée et la talent pour le faire :)

  5. January 11th, 2006 at 11:07 am :: Erik

    Damn, this is really great! Excelent work :)

    And thnx alot :)

    Greetz Erik

  6. January 12th, 2006 at 10:42 am :: Si

    That's a nice little effect with some tidy code. Thanks for the effort and for sharing it. I'm sure an occassion will arise eventually when I'll be needing such an engine.

  7. January 24th, 2006 at 1:09 pm :: Mike

    Salut Kiroukou,

    Superbe classe :)

    Je me demandais... comment appliquer un tel effet sur des TextField dynamiquement instanciés.

    Aurais-tu une idée ?

    Mike.

  8. January 24th, 2006 at 1:12 pm :: kiroukou

    Slt
    Il faudrai que je modifie un peu cette classe pour qu'elle recoive en parametre du constructeur soit le bitmapData soit son indentifiant de liaison. Ainsi si tu passe une instance de bitmapData (contenant ton texte dynamique), toute se passera facilement :)
    ++

  9. January 31st, 2006 at 11:28 am :: Quentin

    The SWF won't load !
    Here's the actual URL : http://lesitekilestbien.free.fr/distordImage/meshtextured.swf

  10. January 31st, 2006 at 12:18 pm :: kiroukou

    Hi Quentin
    I know the problem... Just wonder why it doesn't load. I'm going to check if the url is correct (I think so).

  11. February 3rd, 2006 at 6:31 am :: nodename » The Sandy AS2 3D API and a Flash Cubic Panorama Tutorial

    [...] A note about texture mapping in Flash. I didn’t go into detail about the transformation-matrix method of texture mapping in my article Prospects for Immersive Panoramas in Flash. The idea is that since a single Matrix cannot perform a perspective transformation, we implement it by slicing the texture into smaller pieces (generally triangles) and doing a matrix transformation on each of them. So there’s a tradeoff: the more pieces we cut the texture into, the better the result approximates the true perspective transformation, and the slower our movie runs. This is how Sandy does it, and you can see the technique abstracted into a standalone class by Thomas Pfeiffer here: DistortImage. This class gives you Photoshop-style free transform in Flash. | Permalink [...]

  12. February 3rd, 2006 at 11:32 am :: kiroukou

    The link to the swf is repaired !
    HAve fun :)

  13. February 9th, 2006 at 3:31 pm :: Seb-AS

    [...] Aqui el link a Web del autor de la clase [...]

  14. March 20th, 2006 at 7:56 pm :: Weblogue de Kiroukou » Blog Archive » DistortImage V2, vous pouvez m’aider!

    [...] Pour ceux qui ne le sauraient pas, je suis à l’origine du projet Sandy, projet ayant pour but de créer une librairie permettant de concevoir des scènes 3D pour Flash. Lors de la réalisation de ce moteur, j’ai développé une classe permettant de déformer un bitmap par le code (chose impossible nativement). Vous pouvez voir le résutat ici : http://sandy.media-box.net/blog/distordimage-the-way-to-distord-bitmaps-by-code.html [...]

  15. March 20th, 2006 at 10:04 pm :: iteratif

    Concernant le skew (le calcul) que Andre michelle aurait trouvé ... :) , il lui a suffit de lire le texturage sous GBA (GameBoy Advance) :

    d'ailleurs en voici le lien :
    http://user.chem.tue.nl/jakvijn/tonc/affine.htm

    si vous allez a la page d'accueil du site
    vous y trouverez aussi des infos sur le mode 7 aussi

    bonne lecture... ;)

  16. March 24th, 2006 at 1:37 pm :: Sandy’s Blog » DistortImage 2.0, the fastest way to freely distort image with Flash in actionscript

    [...] But in the same time we found some pretty potimizations that we can add to the original DistordImage class. And after some complete benchmark, we can see that the two solutions are equivalent in performance now. The new class runs between 2.5 3 times faster ! [...]

  17. July 14th, 2006 at 3:07 pm :: KugleX

    its distorT not distorD

  18. October 2nd, 2006 at 4:07 am :: bajjzhdx

    bajjzhdx...

    bajjzhdx...

  19. December 18th, 2006 at 2:32 pm :: Mino

    Hi, this effect is awesome!

    I'd like to use it for my flash learning.
    Could you please teach me how to use the class?
    Where should I put the class above?

    Thanks before.

  20. January 24th, 2007 at 10:49 pm :: Timothy Gray

    we're using this class for a project that adds perspective to a flv file. It works fine if the flv is referenced with a relative url but breaks if we change that to an absolute url. the audio plays, but the video content is not visible. we added an animation to the mc and that animation stops as well. it's as if flash just stops trying if it gets an absolute path. any ideas?

  21. January 31st, 2007 at 12:17 pm :: phil

    Hi,
    Does anyone know how to change this code, so I can have an animated image that is being distorted? I thougt maybe you need to use the BitmapData.draw method to draw a new image into the bitmapdata but it doesn't work, any ideas??

    thanks.
    phil

  22. March 1st, 2007 at 2:54 pm :: Helo

    Salut,

    Je travaille actuellement sur un site en flash. J'aurais aimé partir de ta classe pour développer une fonctionnalité de ce site. Comme le site en question sera mis en ligne par une entreprise (mais pas pour vendre des choses), je ne sais pas si la licence l'autorise. Peux tu me dire si tu es d'accord ou pas.

    Merci d'avance,

    Hélo

  23. March 19th, 2007 at 10:38 pm :: Brad

    DistordImage is a very useful class. Congratulations! I would like to use your class in a commercial project. I would change the code a bit to allow bitmaps to import externally. I would be very grateful if you would grant me permission. Thanks

    Brad

  24. April 25th, 2007 at 11:47 pm :: Polaco

    This idea is the greatest thing I have ever seen in AS.
    By the way.. Why are those useless comments there?

    Great code!!!

  25. September 21st, 2007 at 3:50 pm :: Gred

    Nice!

    Merci beaucoup pour le code, vous avez sauvé mon emploi ;)
    (jexagère)

    Sérieux, le code est facile à travailler et pas trop lourd.

    Merci!!

  26. October 18th, 2007 at 7:30 pm :: vendramini

    foda, usei muito já... valeu mesmo :)

  27. January 23rd, 2008 at 11:31 am :: matt b

    How can this be altered to work on loaded images?

  28. January 24th, 2008 at 4:43 pm :: matt b

    …yes it can. I've sorted it and it works a treat. Great bit of scripting. Thanks.

  29. January 31st, 2008 at 11:16 pm :: Jeremy

    How do I get the latest copy of the DistortImage class for AS3.

  30. March 5th, 2008 at 3:26 pm :: Nick

    This is fanstastic - I've got no experience of using classes in AS2, please could you outline how to set up that class file. Please Please

  31. March 6th, 2008 at 5:53 am :: Sean

    Hi, this script is fantastic, but I can't get it working when I dynamically load a jpg using loadMovie(); a black square appears in the DistortImage area.

    Have you been able to do this with this class?

  32. March 6th, 2008 at 6:22 am :: Sean

    oh, I figured it out. The image hadn't loaded yet. Contact me through my website if anyone else is having trouble with this, as it was boggling me for longer than it should

  33. March 10th, 2008 at 5:14 am :: Richard

    Hi kiroukou,

    just fantastic stuff. was looking for how to do this, and saw numerous people saying how impossible it was.

    Anyway, just writing to say good on ya

  34. September 3rd, 2008 at 6:01 am :: Vijay Dadhich

    It’s really nice example. But it’s not work on text.

    I have tried with text But it become distorted the text then skew.

    Is any way to improve text quality when distort/skew?

    I really need for T-Shirt design project.

  35. November 4th, 2008 at 8:27 am :: florian

    kiroukou Slt
    Il faudrai que je modifie un peu cette classe pour qu'elle recoive en parametre du constructeur soit le bitmapData soit son indentifiant de liaison. Ainsi si tu passe une instance de bitmapData (contenant ton texte dynamique), toute se passera facilement
    ++

    bonjour,
    effectivement ça serait une riche idée ou alors eut-tu nous dire comment faire ? J'aimerais tout simplement que ma _texture soit un movieClip.
    Ce que je fais actuellement c'est de cacher mon movieClip as Bitmap : movieClip.cacheAsBitmap=true;
    ensuite dans la classe DistordImage j'ai modifié :
    _texture = BitmapData.loadBitmap( symbolId );
    en :
    _texture = symbolId;
    mais cela ne marche pas, Flash me dit :
    Type mismatch in assignment statement: found String where flash.display.BitmapData is required.

    Bref je suis un peu perdu, je vais continuer à chercher néanmoins.

  36. November 4th, 2008 at 9:25 am :: kiroukou

    Il te suffit de faire toi meme la converssion MovieClip -> Bitmapdata et de le donner à distortImage.

    monBitmap.draw( monClip );
    ++

  37. November 6th, 2008 at 9:45 am :: florian

    je te remercie, je connais pas très bien ActionScript et je sais pas s'i j'aurais trouvé tout seul.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>