I’ve been creating a lot of AS3 generated graphics over at Art From Code and have faced the problem of how to get the generated bitmaps (or vectors) out to files. For a lot of the files, they are quick experiments and I do a quick screenshot with Jing. For others, I’ve created AIR apps that let you save out files to the file system. But it dawned on me the other day, that in Flash 10, the FileReference class has a save method.
By following the instructions here, I got Flex builder set up to compile Flash 10 movies. By including the framework swc in my project I get access to the JPEGEncoder and PNGEncoder classes. These take a BitmapData and return a ByteArray. You can then pass that ByteArray to FileReference.save() to save the encoded JPG / PNG to local disk, even from a plain old SWF.
I figure I’ll be using this functionality a lot to save BitmapDatas, so I created a new class called SavingBitmap which allows you to save the with a single method call. Here’s the class:
[as]package com.bit101
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.net.FileReference;
import flash.utils.ByteArray;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
public class SavingBitmap extends Bitmap
{
private var _fileRef:FileReference;
private var _encoder:IImageEncoder;
private var _jpegQuality:Number = 80;
private var _encoderType:String = JPEG;
public static const JPEG:String = “jpeg”;
public static const PNG:String = “png”;
public function SavingBitmap(bitmapData:BitmapData=null, pixelSnapping:String=”auto”, smoothing:Boolean=false)
{
super(bitmapData, pixelSnapping, smoothing);
_fileRef = new FileReference();
_encoder = new JPEGEncoder(_jpegQuality);
}
public function set jpegQuality(value:Number):void
{
_jpegQuality = value;
if(_encoder is JPEGEncoder)
{
_encoder = new JPEGEncoder(_jpegQuality);
}
}
public function get jpegQuality():Number
{
return _jpegQuality;
}
public function set encoderType(value:String):void
{
_encoderType = value;
if(_encoderType == JPEG)
{
_encoder = new JPEGEncoder(_jpegQuality);
}
else
{
_encoder = new PNGEncoder();
}
}
public function get encoderType():String
{
return _encoderType;
}
public function save(defaultFileName:String = null):void
{
var ba:ByteArray = _encoder.encode(bitmapData);
_fileRef.save(ba, defaultFileName);
ba.clear();
}
}
}[/as]
As you can see, the class extends Bitmap, so you can use it just like any other Bitmap. You create a usual BitmapData and instead of wrapping it in a Bitmap, you wrap it in a SavingBitmap. When you are ready to save it, you just call the SavingBitmap’s save() method. This encodes the BitmapData to either a PNG or JPG and sends it to the FileReference.save method. A dialog will open up for the user to select a place to save the file and it will be saved. There are other methods to choose what encoder to use and the JPG quality.
Here’s an example of it in use. A really rough example, but it shows that it works. In case you weren’t following along, Flash 10 player is required:
[kml_flashembed movie=”http://www.bit-101.com/blog/wp-content/uploads/2008/08/saving2.swf” height=”640″ width=”620″ /]
Draw on the white canvas then click save to save it. Open the saved file to see that it is what you drew. Here’s the code that went into making that:
[as]package
{
import com.bit101.SavingBitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
public class Saving2 extends Sprite
{
private var _bmp:SavingBitmap;
private var _color:uint;
private var _bmpd:BitmapData;
public function Saving2()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
_bmpd = new BitmapData(600, 600, false, 0xffffff);
_bmp = new SavingBitmap(_bmpd);
_bmp.x = 10;
_bmp.y = 10;
addChild(_bmp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
var saveBtn:TextField = new TextField();
saveBtn.text = “Save”;
saveBtn.selectable = false;
saveBtn.background = true;
saveBtn.border = true;
saveBtn.autoSize = TextFieldAutoSize.LEFT;
saveBtn.height = saveBtn.textHeight;
addChild(saveBtn);
saveBtn.x = 280;
saveBtn.y = 620;
saveBtn.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(event:MouseEvent):void
{
_bmp.save(“drawing.jpg”);
}
private function onMouseDown(event:MouseEvent):void
{
_color = Math.random() * 0xffffff;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onEnterFrame(event:Event):void
{
for(var i:int = 0; i < 100; i++)
{
_bmpd.setPixel(_bmp.mouseX + Math.random() * 50 - 25, _bmp.mouseY + Math.random() * 50 - 25, _color);
}
}
private function onMouseUp(event:MouseEvent):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
}[/as]
The whole saving functionality is encapsulated in this one method that gets called when you click the save button:
[as]private function onClick(event:MouseEvent):void
{
_bmp.save("drawing.jpg");
}[/as]
Pretty damn easy. One caveat is that the saving MUST happen on a button click / mouse event. Otherwise you'll get a security error.
Another caveat is that the Flash 10 compiling in Flex Builder is pretty rough still. It isn't the easiest thing to get set up, and as of this writing, a LOT of the code hinting is missing. Specifically, you won't get code hinting for BitmapData and you'll have to add the import statement manually. This happens for some other stuff as well. This had me convinced that it wasn't working at all, but once you add the imports and write the code, FB recognizes it as correct and it compiles just fine.
One final caveat is that once you call save, there is a lag while the encoding happens. You might want to try to sneak in some kind of user message there, that the encoding is happening. This might be a bit rough though because anything you try to display won't show up until the method completes. And if you try to delay the encoding, then the FileReference.save method won't be being called directly from the mouse click and will fail... So, you might want to amend the class to separate the encoding and the saving. But the above is all I need for my own purposes of personal use. Thought I'd share it.
Quite a while back I came across an image encoder here:
http://henryjones.us/articles/using-the-as3-jpeg-encoder
which I managed to implement.
Along the way I found this work for an asynchronous encoder:
http://www.switchonthecode.com/tutorials/flex-tutorial-an-asynchronous-jpeg-encoder
which I found made my head hurt.
I have not tried your encoder ( as Im still languishing in the dark ages of CS3) but perhaps the asynchronous component might be useful?
I was wondering if you knew a way to “output/convert or save” the generated art swf to an editable format such as e.i. an illustrator file, so that you can edit it manually… I know that Joshua Davis had a way of doing it in flash 5.
Thanks for reminding me about your sub-blog. Why not have a list of posts on this blog, so I won’t forget to go look? 🙂
Sorry for being nitpick, but wouldn’t “BitmapSave” be a better class name?
Matthias. Well it IS a Bitmap and it has the ability to save itself, so I was going to make it SaveableBitmap or SavableBitmap, but I wasn’t sure which was the correct way to spell it and knew I’d struggle with it every time I used it. 🙂 So it became SavingBitmap.
Sep, yeah, Josh used to print to PDF, which outputs as vector so it could be scaled up and edited etc. Not sure if that still works, but I’d think it should. The problem I have with that is often I’m putting thousands of particles or shapes down. Thousands of thousands. And Flash starts puking on that many vectors, so I draw to a large bitmap to keep the performance up. It’s not editable as a vector, but you can make it large enough that it still looks good at most sizes. And you can use tricks like my BigAssBitmap class or loading in a bigger empty bitmap to get bitmap sizes even larger than 2880×2880. Actually, in Flash 10, you can already make bigger bitmaps. I just remembered that. I’ll have to try it now!
Thanks, for that but i am really interested in getting an editable version as vector of my generated art swf, and after 2 and a half hours searching i haven t found any solution to that problem… all i am getting is a “Bipmap pdf” so far 🙁
Sep, if you are on a Mac, you can right click a swf, select print, and from the print dialog, save as pdf or save as postscript. If your swf contains vectors, those should be saved out as vectors. If you’re not on a mac, you should still be able to get some kind of pdf printer driver that should do the same thing.
Actually, I just tried it, and it seems the save to pdf / postscript converts vectors to bitmaps. I think that may have been different when Josh was doing it, or maybe he was doing it some other way. not sure.
Steps to save Flash vector art:
1. Add a PS printer to Windows (for example ‘Apple Color LW 12/660 PS’).
2. For the printer port choose FILE:(print to file).
3. Right-click on the Flash content and select ‘Print’.
Now when you print using the printer you just added it writes an .eps file to your desktop.
Nice, but where was the file saved to?
should a dialog box open? it didn’t for me 🙁
Strid, yes, if you have Flash 10 a save dialog should open up
I am on a Mac pro with leopard, i tried to download the adobeps drivers, but they wont work ” because the Classical environment is no longer supported”. So i moved to windows via bootcamp and got a eps generated with perfect vectors but in shades of grey. I will look at other drivers when i have the time, hopefully this will fix the color issue 🙂
Does anyone knows how to get this to work on a mac ? and what driver to use to keep the colors ?
Thanks guys
I forgot to mention that i lost the alpha as well in the conversion to eps. Don’t know if this is normal or not 🙁 ???
I took in a Josh Davis presentation where he talked about how he did his exporting (he was saving an EPS).
We had to go back to Flash Player 8 for the print funciton to work, and that feature was disabled for Flash Player 9.
I have managed to convert my “flash player 9 AS3 coded” swf to an eps following RV’s comments. I am getting perfectly formed vectors, the problem is the alpha and the colors…
I have tried Apple Color LW 12/660 PS, and now i have got colors 😉 but the alpha is still not working, and this solution is only working on windows for the moment, I will try with other drivers to see if I can get the alpha working…
Really cool stuff. Recently I have been thinking of making an AIR app that would do this for a scripted animation. I think that it would be useful for wild effects that take too long to render.
Norm, I’m going down exactly the same lines. Too bad AIR 1.0 doesn’t support Flash 10 yet.
About saving vectors, the PDF Printer works pretty smooth in Windows… for some reason it doesn’t in Mac. But in exchange, there is a great trick for exporting complex compositions ever since AS3.
You see, when we do such things, we usually lose the vectors at the end of each frame, after drawing them into a BitmapData… well, if instead of clearing them, you store them in an off-stage sprite, they will stay there, not rendering, thus not using the CPU (just some memory). You can later use printJob to send that sprite to the printer (Distiller for example) and get a full vector PDF in no time and without even rendering the vectors for the output… I’ve been able to export literally millions of vectors at once without crashing or even stalling the Flash Player (Distiller does takes some time creating the PDF, but FP doesn’t seem to suffer much 🙂 ).
You do lose alphas and colors, but I think I remember that nesting sprites for each lineStyle solved this issue.
Of course, there is always AlivePDF, or straight PostScript string creation for more elaborated things 😉
Cheers 🙂
This is very good example of image processing in Flash! No need server-side for image processing.
very very cool!
Nice work Keith!
One thing I noticed was the inability to overwrite existing images on a Mac. I tested in Firefox 2, Firefox 3, Safari 3.1.2 on Mac. I was able to overwrite an image just fine on Windows (tested on Firefox 2, and IE). It appears to be a Flash Player 10 bug on Mac.
Hi bit101,
Love your work! I wrote a simulair class (FlashPlayer 9 in combination with PHP) for saving as images (JPG / PNG using adobe’s encoders). I see your class extends the Bitmap class, but mine doesn’t extend anything, because it can save all types of displayobjects. Sometimes you just want to save a displayobject (movieclip, sprite, textfield, shape) directly, instead of drawing it into an bitmapdata object. Maybe you could take a look at it?
http://blog.stroep.nl/2008/08/as3-imagesaver-class-v10/
That’s cool Mark. I imagine it must be creating a bitmapdata behind the scenes and drawing to it in order to encode, right? But it’s cool that it saves you that step if you just want to save a non-bitmap. You should look at implementing the local save in Flash 10 with that.
Thanks. Yes, you are right, internal it created a bitmapdata (with the right bounds) and encodes the data. Saving as local file would be a great feature, it’s a pity saving to local isn’t supported in FP9 like FP10 does.
Hey did you people know this is possible with flash player 9? i once scripted flash too save webcam pics to a local server (other pc) in a clothes-store, there was a little server end php involved though, wich i see isnt beeing used here
great,it can also be available in air
yeah great but what do you do with such big pictures? I don’t really get it…
If you want to make prints of images generated in Flash, you get much better results with high resolution images. A high quality print would be up to 300 dpi. So to make a 24×24 inch print, you would need a 7200×7200 pixel bitmap. I’ve been printing 24 inch prints at 4000 pixels, which is about 167 dpi and it’s still pretty decent. 150 dpi is about as low as you would want to go.
That’s very nice indeedy. You know, you’re getting pretty good at this code stuff. Maybe you should write a book or something. Just a thought. 🙂
Hi!
Great post, it’s just what i was looking for. Works sweet, after installing f10 😀
However I can’t reproduce it of my flex builder (I must say i’m a newbie in flash/flex). How can I lauch the app. on fb3?
thanks in advance,
d
One question.When you click save , the “default.jpg” file appears. But there are users that will write instead only “mypicture” for example and will forget the file extension. Is there a workaround for this? I mean, how can I add by default the extension right BEFORE saving ?
Thanks
chris – that’s a flash player bug
see: http://bugs.adobe.com/jira/browse/FP-2014
i really wish that everything wouldn’t completely hang while the encoding is done. when working with something that is 2000×2000 pixels, it hangs for quite a few seconds, not allowing you to do anything else in the browser until it is done.
any recommendations about how to tackle that?
rocksteady,
danno~
cool,man~ thx~