Tutorial - Creating the reflection of an image dynamically in AS3

You've seen it everywhere, even in the new iTunes. Basically the idea is to create a reflection of an image, as if the surface it is standing on was mirror-like.

sample reflection

I've managed to hack away some code using the BitmapData, Bitmap and Sprite classes. This is probably not an efficient way to do things, especially the changeAlpha function which adapts the negative color values (why do I get that?) for each pixel.

_art is a Sprite containing the image we want to create the reflection of; in my example _art has been scaled by fixing it's width and height dynamically, therefore we need to take that into consideration when drawing it's content into a BitmapData object

_artReflection is a Sprite which will contain the reflection

Here we go:

Actionscript:
  1. import flash.display.Bitmap;
  2. import flash.display.BitmapData;
  3. import flash.geom.Matrix;
  4.  
  5. protected function createReflection()
  6. {
  7.    // you'll need to rewrite this, my _art variable is a covert art from a CD loaded dynamically, and I scale it previously
  8.    var xscale:Number = _art.scaleX;
  9.    var yscale:Number = _art.scaleY;
  10.  
  11.    // create my scaling matrix to draw the bitmap into a bitmap data, since even it it's containing Sprite is scaled, it will draw the thing in full proportions otherwise
  12.    var scaleMatrix:flash.geom.Matrix;
  13.    scaleMatrix = new Matrix();
  14.    scaleMatrix.scale(xscale, yscale);         
  15.  
  16.    // create a bitmap data for the source bitmap, of the full dimensions of the image we are using to create the reflection from
  17.    var sourceBmp:BitmapData;
  18.    sourceBmp = new BitmapData(_art.width, _art.height, true, 0x00ffffff);
  19.  
  20.    // draw the source (covert art in my case, into the bitmap data.
  21.    sourceBmp.draw(_art, scaleMatrix);
  22.  
  23.    // the height of my reflection is 1/3 of the original image
  24.    var reflectionHeight:Number = Math.round(_art.height/3);
  25.  
  26.    // variable to track the row we are writing in the target bitmapdata
  27.    var targetRow:Number;
  28.  
  29.    // this is our starting alpha value (0 to 255). The reflection starts at 1/4th of alpha, going gradually to 0
  30.    var curAlpha:Number = 64;
  31.  
  32.    // this is the step by which the alpha will lessen on each row, calculated based on the height of our reflection
  33.    var steps:Number = 64 / reflectionHeight;
  34.  
  35.    // create our target bitmap data, same width as the original image, and using the height we calculated earlier
  36.    var targetBmp:BitmapData = new BitmapData(_art.width, reflectionHeight, true, 0x00FFFFFF);
  37.  
  38.    // loop through the rows of pixels, starting at the bottom of the image to be reflected
  39.    for(var row:Number = _art.height-1; row> _art.height - reflectionHeight; row--)
  40.    {
  41.       // our target row is opposite the sourcerow, since we are reflecting it
  42.       targetRow = _art.height - row - 1;
  43.  
  44.       // for each row, decrement the alpha
  45.       curAlpha -= steps;
  46.  
  47.       // we go through columns left to right in both source and target image
  48.       for(var col:Number = 0; col <_art.width; col++)
  49.       {
  50.          // we set the pixel in the target image, using col, targetrow, and changing the alpha value of the pixel we get from the source image on the way
  51.          targetBmp.setPixel32(col, targetRow, changeAlpha(sourceBmp.getPixel32(col, row), Math.round(curAlpha)));
  52.       }
  53.    }
  54.  
  55.    // create a bitmap from the bitmapdata
  56.    var image:Bitmap = new Bitmap(targetBmp);
  57.  
  58.    // add it to the sprite we use to contain the reflection
  59.    _artReflection.addChild(image);
  60.  
  61.    // and eventually position _art and _artReflection so that _art is right OVER the _artReflection
  62. }
  63.  
  64. // this function alters the alpha value of a 0xAARRGGBB color value and returns the new color value
  65. protected function changeAlpha(color:uint, alpha:int):uint
  66. {
  67.    // decompose the 0xAARRGGBB value, each component is a byte, therefore from 0 to 255 in value.
  68.    var blueOffset:Number = color % 256;
  69.    var greenOffset:Number = ( color>> 8 ) % 256;
  70.    var redOffset:Number = ( color>> 16 ) % 256;
  71.    var alphaOffset:Number = ( color>> 24) % 256;
  72.  
  73.    // i'm not sure why sometimes the color values are negative, but I remember 7 years ago in C++ under windows I did this hack and it worked.
  74.    if(greenOffset <0)
  75.       greenOffset += 256;
  76.    if(redOffset <0)
  77.       redOffset += 256;
  78.    if(blueOffset <0)
  79.       blueOffset += 256;
  80.  
  81.    //trace("blue:"+blueOffset+", green:"+greenOffset+", red:"+redOffset+", alpha:"+alpha);
  82.  
  83.    // recompose the 0xAARRGGBB value
  84.    return (alpha<<24|redOffset<<16|greenOffset<<8|blueOffset);
  85. }

Voilà. Post your comments or questions! Or even better, improve the efficiency of my code!

5 Responses to “Tutorial - Creating the reflection of an image dynamically in AS3”

  1. mk says:

    Hi,
    and thanks for the useful script!
    How do you create a Sprite from an image loaded dynamically? Could you post some fast code about that?

    Thanks

  2. mit says:

    useful !

  3. montana says:

    link to source if you’d like help with your code, mate.

    regards

  4. montana says:

    if you cast as uint you won’t get negative values…

    var alphaOffset:uint = color>>> 24 & 0xFF;
    var redOffset:uint = color>>> 16 & 0xFF;
    var greenOffset:uint = color >>> 8 & 0xFF;
    var blueOffset:uint = color & 0xFF;
    //i = (x ^ (x >> 31)) - (x >> 31); this is the same as math.abs …also ternary below
    greenOffset=(greenOffset <0)?(-greenOffset):(greenOffset);
    redOffset=(redOffset <0)?(-redOffset):(redOffset);
    blueOffset=(blueOffset <0)?(-blueOffset):(blueOffset);

    thanks again, this is fun to play with…

  5. Porter says:

    That’s pretty neat stuff, definitely fun to play with. As you said, probably not the most efficient, but it can be fixed up.

Leave a Reply