Flash Filters on HTML Elements with AIR
The second leg of three for the on AIR Bus Tour is drawing to a close. The crew is in Philadelphia, PA now, with Boston to go at the end of the week. During this leg of the tour I’ve been showing off some new samples, the most popular of which seems to b applying Flash bitmap filters to HTML elements in AIR. In this walkthrough, I’ll cover some of the techniques I use to blur the line between Flash and HTML.
First of all, it’s important to remember that AIR stands for the “Adobe Integrated Runtime”. The word “integrated” refers to taking the three most common display types of HTML, Flash and PDF, and allowing them to be easily mixed. In that sense, what was once just JavaScript, is now accessible from ActionScript. More importantly for this example is that what was once just ActionScript, is now accessible from JavaScript.
To apply a Flash filter to an HTML element, we must first determine where on the screen that element is located. There are numerous implementations of this out there, but I chose to use the Prototype framework, which gives me Position.cumulativeOffset() and Element.getDimensions(). The resulting code (very much abbreviated) looks something like the follow snippet.
var dim = Element.getDimensions( this );
var pos = Position.cumulativeOffset( this );
pos = new air.Rectangle( pos[0], pos[1], dim.width, dim.height );
The HTML rendering engine inside of AIR is WebKit. The interesting thing about the display of content however, is that it is rendered through the Flash display list. This means that any part of the viewable space, to include the HTML content, can be accessed as an array of pixel values. This is called bitmap data in Flash, and is represented by the Bitmap and BitmapData classes.
Using BitmapData.draw() it is possible to pull the pixel values of the entire screen. By specifying a transform matrix, we can crop at the same time as the pixel data is being copied into a BitmapData object. Since we now know where on the screen the HTML element lives, we can copy just that pixel data. That operation looks something like the following (again, abbreviated).
var bmp = new runtime.flash.display.BitmapData( pos.width, pos.height );
var matrix = new runtime.flash.geom.Matrix()
matrix.translate( 0 - pos.x, 0 - pos.y );
bmp.draw( window.htmlControl, matrix );
To put this back into the rendering pipe, we’ll need to add it back to the runtime’s display list. As it just so happens, (almost) anything that’s going to be on the display list can have a series of filers applied to it. There are a number of filters (effects) provided with Flash, and thereby AIR. Filters are applied to display objects by through a “filters” property which is an array of filter objects. Yup, more than one filter can be applied at a a time.
var effect = new runtime.flash.display.Bitmap( bmp );
var filters = new runtime.Array();
filters.push( new runtime.flash.filters.BlurFilter() );
effect.filters = filters;
Now that we’ve applied a filter to the pixel data, we just need to add it back to the display list, and position it over top of the HTML element in the actual page content itself. Again, because we captured the position of the HTML element already, we know where it resides in the display, and where to put our fancy new copy. The last step I take just to be clean about it is to also hide the original HTML element itself via traditional CSS means.
var sprite = new runtime.flash.display.Sprite();
sprite.addChild( effect );
sprite.x = pos.x;
sprite.y = pos.y;
this.style.visibility = 'hidden';
window.htmlControl.stage.addChild( sprite );
This of course is just a copy of the actual HTML element, and won’t update if the underlying HTML element updates. It stands to reason then that the next logical step would be to continue to update the pixels in the filter as the underlying HTML element changes. I can think of a few ways to handle this problem, to include watching user-generated events, or updating with every redraw of the screen (generally somewhere around 30 frames per second).
I’ve chosen to stop here, but I encourage you to explore. Flash is well suited to these types of bitmap display manipulations. Remember that the WebKit output itself is actually going through the Flash rendering pipeline as a bitmap. In the meantime, you’ll find the complete source code for a filter explorer application attached to this post. I hope you’ve enjoyed blurring the lines as much as I have.

August 23rd, 2007 at 1:12 pm
kevin! i’m flying out tomorrow and will miss you guys in boston - so sad! AIR is amazing, i’m so excited to see how far it’s come and how incredibly cool it is. you guys rock. my best to all, i sure wish i could be there.
libby
August 27th, 2007 at 12:36 pm
I dowloaded the source and packaged as AIR but it does not look anything like the index page in a browser what am i doing wrong?/
August 28th, 2007 at 11:10 am
Libby!
Sorry to hear you couldn’t make it, but I’m glad to hear that you like AIR! (smile) I’ll look forward to catching up on IM in the near future. And hey, thanks for reading my blog!
Talk to you soon,
Kevin
August 28th, 2007 at 11:12 am
Chris,
Depending on what you’re doing, the easiest way to launch and test this is simply to unpack the source, and then use the ADL command-line utility to launch by pointing it at the included “application.xml” file. If you’ve packaged the AIR file, then you’ll need to run it to install the application. That application can then be launched in the typical manner as any other desktop application.
Hope that helps,
Kevin
November 12th, 2007 at 2:33 pm
Hi Kevin, would you care to elaborate on this “I can think of a few ways to handle this problem, to include watching user-generated events, or updating with every redraw of the screen (generally somewhere around 30 frames per second).”
I cannot seem to figure out a way to get the HTML to redraw. Please see http://tech.groups.yahoo.com/group/flexcoders/message/93331
Thanks!