Friday, September 22, 2006

Flex 2 BitmapData Tricks and Techniques

Most seasoned flash developers know that the Bitmap API has been around in the Flash player since the relase of FP8. Those that know all about the Bitmap API appreciate it for its many benefits... Those that don't know about it, trust me, you appreciate it too... you just don't know it. In Flex 2, you can take advantage of the Bitmap API to implement some very powerful and useful techniques that simply are not possible in non-Flash-based web applications.

Some of you may be asking: What is the Bitmap API?

The Bitmap API refers to the flash player's functionality that renders every visual object within a swf as a bitmap (which gets cached), rather than redrawing the entire object every frame interval. Only objects that are marked as "dirty" will be redrawn. The amount of resources used to render each frame is significantly reduced, thus improving the runtime performance of swf files on the client machine. You can find some more information on the Bitmap API at:

Now you may now be asking: What does this have to do with my Flex application? All Flex components that can be visually represented within your Flex application apply to this. They are drawn initially, and then cached as a Bitmap within the Flash player. This is where things get really interesting. Not only does this increase application performance, but it gives us access to the rendered bitmap of the instantiated object. Below you will find a very small, yet powerful, function that will allow you to capture the BitmapData of ANY visual flex component. I just used the UIComponent class as a parameter b/c it is the base class for virtually all flex components.
private function getUIComponentBitmapData( target : UIComponent ) : BitmapData

    var bd : BitmapData = new BitmapData( target.width, target.height );
    var m : Matrix = new Matrix();
    bd.draw( target, m );
    return bd;  
}

So now what? You can capture the BitmapData from any visual component. Why would you ever want to do that? I've got a few reasons (and this is just the tip of the iceberg).
  1. Bitmap-Based Imaging Effects
  2. UI Component Mirroring
  3. Application Sharing
  4. Screen Exports
Did you know that you can set the source of a Image object to a Bitmap object? Did you also know that you can create a Bitmap object from a BitmapData object. This means that you can easily take the BitmapData from an existing visual component, and push it into an Image object.

My first example shows how to put this code to use, although its rather boring compared to my other examples. It takes the BitmapData of the specified UI component and pushes it into an Image object. This code example can also be found on JAM.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
 xmlns:mx="http://www.adobe.com/2006/mxml" 
 layout="absolute">
 
 <mx:Script>
  <![CDATA[
  
   import mx.core.UIComponent;
   
   private function captureFullScreen() : void
   {
    var bd : BitmapData = getBitmapData( UIComponent( mx.core.Application.application ) );
    targetImage.source = new Bitmap( bd );
   }
   
   private function captureHiddenDatagrid() : void
   {
    var bd : BitmapData = getBitmapData( UIComponent( hiddenDg ) );
    targetImage.source = new Bitmap( bd );
   }

   private function getBitmapData( target : UIComponent ) : BitmapData
   {
    var bd : BitmapData = new BitmapData( target.width, target.height );
    var m : Matrix = new Matrix();
    bd.draw( target, m );
    return bd;
   }
   
   
  ]]>
 </mx:Script>
 
 <mx:Button 
  id="captureButton" 
  label="Capture Full Screen" 
  click="captureFullScreen()" />
  
 <mx:Button 
  id="captureButton2" 
  label="Capture Hidden Datagrid" 
  click="captureHiddenDatagrid()"  
  x="153"/>
 
 <mx:Image 
  id="targetImage" 
  x="10" 
  y="30"/>
 
 <mx:DataGrid 
  x="99" 
  y="64" 
  id="hiddenDg" 
  visible="false">
  <mx:columns>
   <mx:DataGridColumn headerText="Column 1" dataField="col1"/>
   <mx:DataGridColumn headerText="Column 2" dataField="col2"/>
   <mx:DataGridColumn headerText="Column 3" dataField="col3"/>
  </mx:columns>
 </mx:DataGrid>
 
</mx:Application>

Launch Application
View Source
Download Source

Bitmap-Based Imaging Effects

Wouldn't it be cool if you moved one thing around on the screen, and it left motion trails? What if you wanted to capture part of a component, distort the image (colors, blur, size, crop, etc...) and then show it in a different part of your application? Well, the getUIComponentBitmapData function makes this difficult-sounding task much easier.

In this example, I have an image that is at 0,0 (top left corner) and is the exact same size as my application. The alpha on that image is 90%. On every "ENTER_FRAME" event, it takes a snapshot of the entire application and pushes it into the image. This causes the "trail" effect that you will see. Since the alpha is only 90%, it will eventually fade out, creating the fading image effect. The code is amazingly short, powerful and efficient.



Note: This process will become very processor intensive at high resolutions.

Launch Application
View Source
Download Source

User Interface Component Mirroring
Have you ever had the need to mirror the user interface? I know, this is not a common need but take it a step further (which I will do later in this post), and you have some really useful tools in your toolbox. This next example takes the BitmapData from a tree inside of a panel and pushes it into an Image inside of another panel. This occurs every 100 milliseconds. You'll see that there is a slight delay, but overall it mirrors the interaction very effectively.



Launch Application
View Source
Download Source

The next example acutally mirrors a flv file by pushing the BitmapData of the video playback's current frame into an Image object on every "ENTER_FRAME" event. You'll notice that a few frames are dropped per second, but you really have to look very closely to see the difference. One thing to keep in mind, more frames will be dropped on slower client machines.



Disclaimer: This video clip was downloaded from Google video. This is a scene from the surfing movie Billabong Odyssey, which is a great movie (that I do personally own). I highly reccommend it.

Launch Application
View Source
Download Source

Application Sharing

In my opinion, this next example is really amazing. It is also incredibly simple, yet powerful. I've extended my "User Interface Mirroring" example to share data between two different browsers using local shard objects. Keep in mind, this code only works on the same machine, but who is to say that you can't change that. You could easily chage the local shared object to a remote shared object, incorporating Flash Media Server, and this will work across different client machines. Also, what is stopping you from doing a RemoteObject call to push the image data to a server? This would enable you to pull down the image from the server to any client machine, without the use of Flash Media Server.



View Captivate Demo
Launch Broadcaster Application
Launch Receiver Application

View Source
Download Source

Side Note: If you looked at the source, you might be wondering why I didn't use getPixels and setPixels instead of my own functions to loop through the pixel arrays. I found a bug in the Flash player and get an "End Of File" error when using get pixels and set pixels on items rendered within the player memory. I'm putting together a test case to send to Adobe for this. I didn't use a BitmapData object on the local shared object because the player can't deserialize it back into a BitmapData object when reading the object back from the local shared object. Instead, I saved it in the LSO as a ByteArray.

Screen Exports
I think this next example is really cool too. Have you ever wanted to export your flex application's current view to the client machine? Using this approach, you can capture the UI as a jpg that can be downloaded by the user. This technique could have a number of uses:
  • Capture the user's screen when an error occurs
  • Save the current image within the application: possibly for a flex-based image editing solution... yes, I am working on one of those as a side project :-)
  • Distributed application sharing... this could be a great asset for remote application assistance
In this example, I capture the UI BitmapData, convert it to a JPG ByteArray using a modified version of the JPGEncoder class that is a part of the source code to the ActionScript 3 corelib project available at Adobe Labs. I push it to the server using a RemoteObject call and save the JPG ByteArray in the active session. On the ResultEvent, I make a request to the server and pull down the jpg into a new window on the client machine's browser (completely outside of Flex). Its amazing how easy that was. This is another one of the incredibly rich interactions that is just not possible with an AJAX (DHTML)-based RIA. I used ColdFusion as a backend on this one. Sorry, Im not sharing the all of the source to this example, but you will be able to see a portion of it.



Note: this last example is hosted at tricedesigns.com, that is my personal ColdFusion server.
View Captivate Demo
Launch Application
View Partial Source
Download Partial Source

Questions or comments, you can contact me at andrew.trice( at )cynergysystems.com.

1 Comments:

Blogger Cyrus the virus said...

hii andrew i read ur posts they where really amazing and i appreciate your efforts done towards bitmap......but can u help me in splitting an image
.........actually i want to split a image in 4 parts ....can u post a eaxmple for the said..........because i have done using copy pixels and done by using an rectangle and trying to fit the image in rectangle of specific dimension but nothing is working may u please..........guide me.......hoping for a reply

Mon Jun 16, 10:11:00 AM EDT  

Post a Comment

<< Home