Saving User Generated Imagery from an AIR Application Using the JPG and PNG Encoder Classes and Flash CS4

by Ben Pritchard 4

While anyone with an intimate knowledge of encoding bits of information can write their own JPG or PNG encoder, isn’t it great to know that Adobe already provides classes to take care of it?

Prerequisite Knowledge

  • An intermediate understanding of ActionScript 3
  • An basic understanding of creating AIR applications

Source Materials

Getting the Library of Classes Required

While anyone with an intimate knowledge of encoding bits of information can write their own JPG or PNG encoder, isn’t it great to know that Adobe already provides classes to take care of it?  If you head on over to Adobe’s as3corelib Google Code page, you can download numerous classes that will help you when developing web or AIR-based Flash applications.  In this article I use three particular classes which enable me to encode BitmapData into JPG and PNG formats.  These three classes are the JPGEncoder, BitString (used in conjunction with the JPGEncoder Class) and the PNGEncoder classes found in the com.adobe.images package.

I have created an AIR Application for this purposes of this article.  This AIR Application, AirEncoder (pictured below) will allow users to design their own T-Shirt and then save out an image of their creation in either JPG or PNG format.  The entire source of this application is available with this article so we will only cover the portions of the code pertinent to using the two encoder classes.

Fig.01
Fig.01

Setting Up Our AIR App’s Class

Once you have the as3corelib downloaded, you can then setup your development environment so that it has access to the com.adobe.images package.  For my application I created a Document Class named AirEncoder.  Within AirEncoder I imported the PNGEncoder and JPGEncoder classes using the following code:

     import  com.adobe.images.PNGEncoder;
     import  com.adobe.images.JPGEncoder;

Getting the BitmapData to Save

Once you have your application created and visually generating what you are looking to save you can then gather the bits of information that will be used to create your final image format.  The AirEncoder application uses a function I created called getBitmapData.  The getBitmapData function creates a new BitmapData object that is the width and height of the Display Object that I want to take a snap shot of, in this case the T-Shirt.  The next portion of the function invokes the draw method of the BitmapData class.  The draw method will gather all of the visible bits of a given Display Object (in this case image_mc) and store them within the BitmapData object.  My getBitmapData function then returns the resulting BitmapData object.

private function getBitmapData():BitmapData{
    var bd:BitmapData = new  BitmapData(image_mc.width,image_mc.height);
         bd.draw(image_mc);
         return bd;
     }

Saving Using the PNGEncoder and JPGEncoder Classes

The AirEncoder application will allow a user to save in either PNG or JPG formats.  Both of the encoder classes work in a fairly similar fashion.  Below you will see the saveAsPNG and saveAsJPG functions that I created for the AirEncoder application.  As you can see we are setting the value of a variable named imageByteArray (this is defined elsewhere in my Document Class as you will see in the source) which is typed as a ByteArray.

Both the PNGEncoder and the JPGEncoder classes have a method named encode.  The encode method in both of these classes will return the encoded data as a ByteArray.  One of the only differences between the two functions is the quality argument passed to the JPGEncoder constructor.  In the example below I am passing a 100% quality level

private  function saveAsPNG():void{
       imageByteArray = PNGEncoder.encode(getBitmapData());
       browseForSave("PNG");
     }

     private function saveAsJPG():void{
       var jpgEncoder:JPGEncoder = new JPGEncoder(100);
       imageByteArray = jpgEncoder.encode(getBitmapData());
       browseForSave("JPG");
     }

Saving the File to the User’s Hard Drive

Fig.02
Fig.02

Once we have the data stored within the imageByteArray variable we can then allow the user to choose a location in which to save the resulting image.  The AirEncoder application uses a function named browseForSave.  As you can see the browseForSave function is passed a String, either “PNG” or “JPG”.  The variable saveFileType stores the type of file we are saving and the variable saveFileRef (typed as File and defined elsewhere in the Document Class) invokes the File class’s browseForSave command (not to be confused with the AirEncoder’s browseForSave function) presenting the user with a instance of their operating system’s “Save As” window allowing the user to choose where to save their image file.

     private function  browseForSave(fileType:String):void{
     saveFileType =  fileType;
     saveFileRef.browseForSave("Save  As " + fileType);
     saveFileRef.addEventListener(Event.SELECT,  onSaveAsSelected);
 }

Once the user has selected a file name the onSaveAsSelected event is fired.  This function looks at the name of the file being saved and adds the proper extension to the file name should it be absent.  The addition of this check is to compensate for the fact that AIR does not provide the ability to filter the type of files saved when using the browseForSave function as of the time of this article being written.  Once the file name has been vetted, we then create a new FileStream object and save the imageByteArray data to the FileStream object.  Once the stream is closed the user will then have a new image of the content they generated within the AirEncoder application.

     private function onSaveAsSelected(e:Event):void{
     saveFileRef =  e.target as File;
     var ext:String;
     switch(saveFileType){
         case "JPG":
             ext = ".jpg";
             break;
         case "PNG":
             ext = ".png";
             break;
     }
     if(saveFileRef.extension  == null){
         saveFileRef.nativePath += ext;
     }
     var stream:FileStream  = new FileStream();
     stream.open(saveFileRef,  FileMode.WRITE);
     stream.writeBytes(imageByteArray,  0, imageByteArray.length);
     stream.close();
 }

Putting it All Together

Once you have the core functionality of saving visual data from Flash complete it is simple to wire the rest of the application together.  Keep in mind that you can save the visual state of any Display Object to a JPG or PNG file using the above described methods (within Flash’s security sandbox – e.g. no streamed RTMP media content).  For the complete Document Class and associated files be sure to download this article’s accompanying files.  The will give you a better perspective on how everything works together.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>