Image Distortion and Tapering in ActionScript
Wednesday, June 24th, 2009 | ActionScript | 1 Comment
Distort and taper images in BOTH ActionScript 2 and 3 without a big 3D engine. Is it possible? YES!
**Update: 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:
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
Logic != Math: Transparency Example
Wednesday, June 17th, 2009 | Uncategorized | 3 Comments
If you have a 100% alpha red circle how many 10% alpha blue circles does it take to completely cover the red circle? The answer is 10, right? No, wait maybe 20? How about 100? Maybe 1,000? Believe it or not, an infinite number of blue circles are required it's actually impossible. Give it a try in Flash or Photoshop and you will get the following no matter how many times you click:

Why?
To busy right now to look for the mathematical explanation to this (but I know there is one!). Adding alpha transparent objects will eventually hit a limit somewhere around 97% rather than reaching one hundred percent. Maybe I'll take a look later tonight for some additional resources. Anybody know off the top of their head?
Is there any way to get around this?
Add a new layer and stamp a bunch more blue circles. One layer will hit a limit around 97% opacity but two layers each at 97% will appear pretty darn close to 100%, which is expected.
Six Months on AIR
Friday, June 5th, 2009 | AIR | 3 Comments
Attention AIR developers: Are you interested in learning about caching images in binary format, SQLite database migration, re-using existing view components, memory fragmentation and general tips / tricks about developing your application using Adobe AIR? If you answered YES than you should attend Minh Vu and Chris Black's session at Flashbelt on June 8th at 1:30PM. We're going to have a killer presentation for everyone interested in developing Adobe AIR desktop applications.
Can't attend Flashbelt?
I would highly recommend attending Flashbelt. Networking opportunities, great presentations and lots of fun! OK, if you absolutely can go than check out our presentation below.
Who should attend?
Developers that are currently developing AIR applications or are interested in doing AIR development in the future. Project managers would also benefit from knowing the decisions that go into creating large scale applications. How about designers? This presentation is geared towards the development side of AIR but designers are welcome.
BulkLoader with Binary data format:
link
About the presenters:
Chris Black is a Senior Developer at Sierra Bravo who focuses on ActionScript development with Adobe Flex and AIR, and is interested in integrating social networking APIs into Rich Internet Applications. He covers these topics as well as sharing solutions to the problems he encounters when working with Flex and AIR on his blog, blackcj.com. Chris has a degree in Computer Science from the University of Wisconsin - Eau Claire, and when he's away from the computer he enjoys rock climbing, backpacking, and tennis.
Minh Vu is a Developer at Sierra Bravo who works on interactive development with ActionScript, JavaScript, and iPhone. He has a strong interest in applying Model Driven Development to Flash or AIR applications. Minh studied at the University of Minnesota Duluth where he got his degree is in Information System & Technology. In his spare time he enjoys disc golfing, snowboarding and ultimate frisbee.
Re-Learning ActionScript 2
Tuesday, May 26th, 2009 | ActionScript, Flash | 2 Comments
It's amazing how easy it is to forget the little niches in programming languages after going almost a year without using them. I recently had the pleasure of working on an ActionScript 2 project after almost a year of doing primarily ActionScript 3 development. This post will highlight a few things that can make ActionScript 2 development a bit more like 3. I'll also provide links to resources that helped get me back in the mindset of AS2.
Event dispatching.
One of the toughest parts of going back to AS2 is the event handling. AS3 does this much better and one of my co-workers (@rexeisen) sent me this great link: AS3 Style Events in AS2
Singleton design pattern.
I found a great PDF outlining how to implement the singleton design pattern using AS2: Singleton Design Patter with AS2
My singleton implementation:
-
import com.cb.Utils.messaging.*;
-
import com.cb.events.Event;
-
import mx.utils.Delegate;
-
-
class com.sierrabravo.Utils.messaging.MessageManager
-
{
-
private static var instance:MessageManager;
-
private var _container:MovieClip;
-
private var _currentContent:MovieClip;
-
private var _tint:MovieClip;
-
-
private function MessageManager()
-
{
-
// Do nothing
-
}
-
-
public static function getInstance():MessageManager
-
{
-
if(MessageManager.instance == null)
-
{
-
MessageManager.instance = new MessageManager();
-
}
-
return MessageManager.instance;
-
}
-
-
public function setParent(parentClip:MovieClip):Void
-
{
-
_container = parentClip;
-
}
-
-
public function updatePosition():Void
-
{
-
if(_currentContent){
-
_currentContent._x = (Stage.width - _currentContent._width) / 2;
-
_currentContent._y = (Stage.height - _currentContent._height) / 2;
-
}
-
if(_tint){
-
_tint._width = Stage.width;
-
_tint._height = Stage.height;
-
}
-
}
-
-
public function addMessageWindowContent(contentWindow:MessageContentBase):Void
-
{
-
tint();
-
_currentContent = _container.createEmptyMovieClip( "content", _container.getNextHighestDepth() );
-
contentWindow.init( _currentContent );
-
-
_currentContent._x = (Stage.width - _currentContent._width) / 2;
-
_currentContent._y = (Stage.height - _currentContent._height) / 2;
-
contentWindow.addEventListener(Event.CLOSE, Delegate.create( this, closeWindow ));
-
}
-
-
public function closeWindow():Void
-
{
-
trace('close window');
-
_currentContent.removeMovieClip();
-
clearTint();
-
}
-
-
public function clearTint():Void
-
{
-
_tint.removeMovieClip();
-
}
-
-
public function tint():Void
-
{
-
_tint = _container.createEmptyMovieClip("rectangles", _container.getNextHighestDepth());
-
-
_tint.beginFill(0x000000, 50);
-
_tint.lineStyle(1, 0x000033, 100);
-
_tint.moveTo(0, 0);
-
_tint.lineTo(Stage.width, 0);
-
_tint.lineTo(Stage.width, Stage.height);
-
_tint.lineTo(0, Stage.height);
-
_tint.endFill();
-
_tint.onRelease = function(){
-
//DO NOTHING
-
}
-
_tint.useHandCursor = false;
-
}
-
}
Preventing click through.
In the above example the screen is tinted to put focus on the pop up window. Unfortunately, even though there is a movie clip on top of everything else, it does not prevent interaction with the elements layered below it. In order to get around this I added an onRelease to the _tint movie clip along with a useHandCursor of false. This prevents interaction with elements below the tint. Here is the link to where I found this solution: Disable "Click-Through" on MovieClips in Flash
Debugging in ActionScript 2.
For the project we were working on it was not possible to use the built in debugger for Flash. Another great opportunity to use the Singleton design patter to include a trace window that can have text dumped out from any part of the application.
-
class com.cb.Utils.debug.Debugger
-
{
-
private static var instance:Debugger;
-
private var _textField:TextField;
-
private var _parentClip:MovieClip;
-
-
private function Debugger()
-
{
-
// Do nothing
-
}
-
-
public static function getInstance()
ebugger -
{
-
if(Debugger.instance == null)
-
{
-
Debugger.instance = new Debugger();
-
}
-
return Debugger.instance;
-
}
-
-
public function init(myText:TextField, parentClip:MovieClip):Void
-
{
-
_textField = myText;
-
_textField.wordWrap = true;
-
_textField.background = true;
-
_textField.border = true;
-
_textField.multiline = true;
-
_parentClip = parentClip;
-
}
-
-
public function output(myString:String):Void
-
{
-
if(_textField) {
-
_textField.swapDepths(_parentClip.getNextHighestDepth());
-
_textField.text += myString + "\n";
-
}
-
}
-
}
XML parsing.
Parsing XML is a key part of AS2 and AS3. I found a great tutorial on Loading XML data in Flash using ActionScript 2. This is my code for parsing XML.
-
public function init(clip:MovieClip):Void
-
{
-
example_xml = new XML();
-
example_xml.ignoreWhite = true;
-
example_xml.load("xml/BookDefinition.xml");
-
example_xml.onLoad = Delegate.create(this, onLoadEvent);
-
}
-
function onLoadEvent(success:Boolean):Void {
-
if (success) {
-
var myImage = example_xml.firstChild.firstChild.childNodes;
-
for (var i = 0; i<myImage.length; i++) {
-
var pageNumber:Number = parseInt(myImage[i].childNodes[0].firstChild);
-
var imageURL:String = myImage[i].childNodes[1].firstChild;
-
var keywords:String = myImage[i].childNodes[2].firstChild.toString().toLowerCase();
-
}
-
}
-
}
-
<book>
-
<pages>
-
<page>
-
<pageNumber>1</pageNumber>
-
<pageFlash>thumbnails/page01.jpg</pageFlash>
-
<pageText>This is page 1.</pageText>k
-
</page>
-
<page>
-
<pageNumber>2</pageNumber>
-
<pageImage>thumbnails/page02.jpg</pageImage>
-
<pageText>This is page 2.</pageText>
-
</page>
-
</pages>
-
</book>
Delegate functions.
Notice above that a delegate function was used to keep the scope of the function with the current class. More information on delegate functions can be found here: The Delegate Class
That's it for now. I know that I'll be using this post as a reference the next time I have to jump on another AS2 project. Being able to use design patterns, event dispatching, and debugging actually made coding in AS2 fun! Enjoy.
Additional resources.
http://www.flashmobileblog.com/tag/flash-lite-31/
http://www.adobe.com/products/flashlite/features/
Technologies
Tags
My Next Conferences
Where I do Nerdy Stuff
Categories
- ActionScript (13)
- AIR (9)
- Flash (5)
- Flex (7)
- Other (4)
- Uncategorized (4)
Blogroll
Social Networks
Archives
| Blog: |
| blackcj.com |
Topics: |
| Flex, ActionScript, Flash |




