Save image with pure Flash? - actionscript-3

Very simple: How do I save an image with pure Flash only?
Example code:
var bitmap: BitmapData = new BitmapData(100, 100, true, 0);
bitmap.draw(this);
var data: ByteArray = bitmap.getPixels(bitmap.rect);
var f: FileReference = new FileReference();
f.save(data, "test.bmp");
This saves me a file of 40,000 bytes (4 * 100 * 100). I checked it with hex editor and it is the pixels of my bitmap/sprite. Now I don't care what image type comes out of this. I just want it saved in a format that I can display with common image editors. I don't want to install extra stuff. Can I do it with Flash only, no AIR, no JPGEncoder, no nothing extra?
(This function is not for customers to use anyways.)

I found a solution.
First off, you need to make sure that you are targeting a Flash Player with at least version 11.3, and compile the SWF with at least a version of 18 (or at least, 18 worked for me).
The BitmapData Class has its own method called encode which will handle the encoding work for you, either for JPEG or PNG. Here is how to do it:
var bitmap:BitmapData = new BitmapData(100,100,true,0);
bitmap.draw(this);
var data:ByteArray = new ByteArray();
var o:PNGEncoderOptions = new PNGEncoderOptions();
bitmap.encode(bitmap.rect, o, data);
var f:FileReference= new FileReference();
f.save(data, "test.png");
The third parameter of encode is the output Bytearray where the encoded Bytearray will get saved.
AS3 Documentation

You can still use PNGEncoder or JPGEncoder and still be "pure flash". If you're missing the libraries you can get them (and others) in the as3corelib swc library. Just include the swc in your library path.
If for some reason it's not pure enough, I suppose you could always go in to the source code of the library and copy out the specific PNGEncoder or JPGEncoder class and repurpose it for your uses.

Related

Actionscript 3, reading two files then triggering a function, best practice

I need to read a part of two files, and once both parts of both files are loaded to trigger a function that does some work.
What is the best way to approach this problem.
I am currently triggering the second file load once the first is loaded, however it seems poor coding style, I think this because my OO code starts looking procedural.
EDIT: So its an Air based app so using filestream.
In the end I found I actually needed to read each file in a different way. Because I need the same part of both files, and I dont know the file size, I need to read one file first and once I have fileStream.bytesAvailable and position, I can then look for the same data from the second file. I found I must handle files smaller than my read size and the end of files beyond multiples of my read size.
You don't specified what file and from where you wont to load the file but you can actually load multiples files in parallel.
If you want to read only part of file from local machine you can use AIR's FileStream class - very easy and you don't have to load whole few hundreds MB file:
import flash.filesystem.*;
var file:File = File.documentsDirectory;
file = file.resolvePath("Apollo Test/test.txt");
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var str:String = fileStream.readMultiByte(file.size, File.systemCharset);
trace(str);
fileStream.close();
Another option is to use URLStream and listen for ProgressEvent.PROGRESSevents to read data of partially loaded file.
You may also want to see NetStream class which is used to stream video.
there is many options, using File, FileStream is only available on air applications.
The File class extends the FileReference class. The FileReference
class, which is available in Flash® Player as well as Adobe® AIR®,
represents a pointer to a file, but the File class adds properties and
methods that are not exposed in Flash Player (in a SWF running in a
browser), due to security considerations.
as noted above, if you are creating a non-AIR application, FileReference should be used instead of FileStream and File classes, as you dont tagged AIR in your question.
FileReference does not provide any open("path") to you (due to security considerations), but a browse method will be available and ask's your client's for selecting a file. here is an example, which also explain how to trigger a function when opening is done:
var filereference:FileReference = new FileReference();
filereference.addEventListener(Event.SELECT, onFileSelected);
var text_files:FileFilter = new FileFilter("Text Files","*.txt; *.html;*.htm;*.php");
var all_files:FileFilter = new FileFilter("All Files (*.*)","*.*");
filereference.browse([text_files, all_files]);
// triggered when a file is selected by user
function onFileSelected(e:Event):void {
filereference.removeEventListener(Event.SELECT, onFileSelected);
filereference.addEventListener(Event.COMPLETE, onFileLoaded);
filereference.load();
}
// triggered when file loading is complete
function onFileSelected(e:Event):void {
var data:ByteArray = fileReference["data"];
filereference.removeEventListener(Event.COMPLETE, onFileSelected);
}
two more events to be listened for suddenly error's occurred and displaying a progress bar for loading progress (its sync):
IOErrorEvent.IO_ERROR and ProgressEvent.PROGRESS

Accessing multiple bitmap images in a swc file by using getdefinitionbyname

I received from my client a SWC file with hundreds of png images that all have AS Linkage such as image1_1_1, image1_1_2, image1_2_1, image2_1_3 and so on. I have these same linkage names in an XML file that i'm loading.
All the images are linked with the BitmapData class as base class.
The problem is that when i try to dynamically create new bitmaps from the xml file and the linkage names by using getDefinitionByName i get the following error:
ReferenceError: Error #1065: Variable image1_1_1 is not defined.
My code for creating the bitmaps is as follows:
var BmDataClass:Class = getDefinitionByName(xmlImage) as Class;
var image:Bitmap = new Bitmap(new BmDataClass());
where xmlImage is the variable in a for each loop, looping through the xml file.
I trace xmlImage so i know it coresponds to the correct name in the SWC file.
Does anyone have any idea why i get this error?
Would appreciate any hints or solutions :-)
try this:
var imageName:String = "xxx";
var myClass:Class = getDefinitionByName(imageName) as Class;
var bmp:BitmapData = new myClass(0, 0) as BitmapData;
var img:Bitmap = new Bitmap(bmp);
The answer seems to have been as described in the accepted Answer to this thread
Instantiate a class from a string in ActionScript 3
And also slightly explained on this page
http://producerism.com/blog/flashpunk-dame-and-lua-tutorial-part-6/
under the paragraph titled getDefinitionByName
Which for me sadly messed up a large part of the project. Ah well... new solutions presents themselves every day :-)

How to save BitmapData to Bitmap *.bmp file, or much faster JPE Encoding method

I have a Flash / Actionscript 3 based desktop app wrapped in an *.exe using Zinc 4.0. I am using Flash Pro CS5.
I need to start saving very large image files locally. I have messed around with JPG Encoding these images before saving them to a local file via Zinc. I solved the actionscirpt timeout issue using This "asyncronous like" method. Encoding a 1.5 MP image takes about 5 seconds which is alright, but encoding an 8 MP image file takes about 40 seconds, which is not acceptable.
One idea I had is to save the BitmapData locally to a temporary Bitmap file (*.bmp), without having the end user to wait for JPG Encoding in Flash, and then use my already existing image processor (written in C#) to read the bitmap file and encode it without waiting on Flash to do it, effectively offloading the task away from the user.
I have used BitmapData.getPixels() to try and write the byte array directly to the file, using the same Zinc method as I do successfully with encoded JPGs, but the *.bmp file is unreadable. Are there some file headers that would need to be included in addition to the BitmapData getPixel()'s byte array to successfully save a bitmap image? If so how could I successfully add them to the byte array before writing to file?
Any guidance, clarification or other solutions much appreciated.
I've found a solution for my needs, and just in case others have similar needs:
To save an actual Bitmap (*.bmp) file, Engineer's suggested Btimap encoder class was awesome. Very fast on the actual encoding; however, since my file writing call in Zinc is synchronous and bitmap files are a lot larger than JPGs it really just moved my bottle neck from encoding to the file saving, so I decided to look elsewhere. If Zinc had an asynchronous binary file writing method that would not lock up the GUI I would have been happy, but until then this is not the solution for me.
I stumbled across a Flash Alchemy solution, with great results. Instead of waitint abour 40 seconds to encode an 8 MP image, it now only takes a few seconds. This is what I did:
Downloaded the jpegencoder.swc from this page and saved it in my project directory
Added the swc: Publish Settings > Flash (tab) > Script: Actionscript 3.0 "Settings..." button > Library path (tab)> and added that .swc with Link Type = "Merged into code"
Then used it :
(below is my modified code with just the basics)
import flash.utils.ByteArray;
import flash.display.BitmapData;
import cmodule.aircall.CLibInit; //Important: This namespace changed from previous versions
var byteArrayResults:ByteArray; //Holds the encoded byte array results
public static function startEncoding(bitmapData:BitmapData):void {
var jpeginit:CLibInit = new CLibInit(); // get library
var jpeglib:Object = jpeginit.init(); // initialize library exported class to an object
var imageBA:ByteArray = bitmapData.getPixels(bitmapData.rect); //Getpixels of bitmapData
byteArrayResults = new ByteArray();
imageBA.position = 0;
jpeglib.encodeAsync(encodeComplete, imageBA, byteArrayResults, bitmapData.width, bitmapData.height, 80);
}
private static function encodeComplete(thing:*):void
{
// Do stuff with byteArrayResults
}
You may find this link useful as well:
http://last.instinct.se/graphics-and-effects/using-the-fast-asynchronous-alchemy-jpeg-encoder-in-flash/640
my answer is late but maby it helps.
i developed a AIR mobile app to save imgs from the device camera on the device and upload it to the server.
since air 3.3 you have this bitmapdata encode functionality:
var ba:ByteArray = new ByteArray();
var bd:BitmapData = new BitmapData(_lastCameraPhotoTmpBmp.width, _lastCameraPhotoTmpBmp.height);
bd.draw(_lastCameraPhotoTmpBmp);
bd.encode(new Rectangle(0, 0, 1024, 768), new JPEGEncoderOptions(80), ba);
var localFile:File = File.applicationStorageDirectory.resolvePath("bild.jpg");
var fileAccess:FileStream = new FileStream();
fileAccess.open(localFile, FileMode.WRITE);
fileAccess.writeBytes(ba, 0, ba.length);
fileAccess.close();
the encode to jpg takes ~100ms on mobile devices in my tests.
greetings stefan

Access all symbols in a *.swf file

Is there a way to access all exported symbols in a *.swf file?
I mean all the symbols that are marked Export to Actionscript in the Library of Flash IDE. That way I could use getDefinition() class without having to know the name of the symbol beforehand.
This is for an internal tool made in AS3 that will work along a framework, which needs to perform some operations on every symbol of a *.swf file.
So performance isn't required, and a hackish solution (accessing ByteArray of the swf or something like that) is valid.
Thanks
As Daniel suggested, I ended up using as3swf to parse the ByteCode of the *.swf file and read the class.
Here's a simple function I made that returns an array containing the symbol names, ready to be used with getDefinition(). You must pass in a ByteArray of the *.swf file.
private function getSymbolList(bytes:ByteArray):Array {
var swf:SWF = new SWF(bytes);
var ret:Array = [];
for each(var tag:ITag in swf.tags) {
if(tag is TagSymbolClass) {
var symbolClass:TagSymbolClass = tag as TagSymbolClass;
for (var i:int = 0; i < symbolClass.symbols.length; i++) {
ret.push(symbolClass.symbols[i].name);
} return ret;
}
} return ret;
}
With the new 11.3 Flash API, this feature is built-in.
var definitions:Vector.<String> =
this.loaderInfo.applicationDomain.getQualifiedDefinitionNames();
It is possible by reading the bytecode of the swf, de-serializing it and reading the class names.
This is complicated work though, so it's best to look to low level libraries that already do that. the as3commons bytecode class does this.
http://www.google.com/codesearch#Ueq88nAe-j0/trunk/as3-commons-bytecode/
as3swf can also get at this data. you can have a look through the classes and see how they do it, or just use the classes.
I haven't actually used them, so I don't have any code to share, but hope this helps.

Loader.load and Loader.loadBytes differences

I'm loading as2 swf into AIR application. It works properly when loaded from file. But when loaded from bytes, it is broken in some way (it reacts to mouse, but some elements are inactive)
var bytes:ByteArray = ... //loaded from resources
var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext(false);
context.allowCodeImport = true; //this is neccessary
// Method 1 - blocks some scripts in loaded SWF
//context.applicationDomain = new ApplicationDomain();
// (application domain has no effect with as2 swf)
//context.securityDomain = SecurityDomain.currentDomain; //gives error 2114: securityDomain must be null
loader.loadBytes(bytes, context);
// Method 2 - loads properly
//loader.load(new URLRequest(file.url));
So why not just load it from file? My resources are protected with encryption and I can't dump them to disk - they must still be protected.
What tricks may exist to load from bytes properly?
There is similar question, but in my case as2 causes more problems.
AS2 and AS3 use different runtimes (bytecode is different) so you won't be able to properly execute any AS2 bytecode in the AS3 runtime. you are basically injecting AS2 code into your AS3 application, so it ain't gonna work :/
According the the documentation for LoaderContext you should only use the applicationDomain property only when loading ActionScript 3.0 SWFs. Try dropping that parameter (or setting it to null) and see what happens.
It seems that old SWF movies (AS1 and AS2, which require AVM1) loaded into an AIR app with load get put in their own domains, but those loaded with loadBytes share a domain. So if you have multiple AVM1 SWFs loaded with loadBytes their _global properties will clobber each other. This affects the Flash MX UI components (ca. 2002).
I can't be the only one trying to package ancient Flash files in AIR apps, so I figure this info may be useful to someone.