save bmpData as jpg using adobe flex in air application - actionscript

i use the following code:
protected function videoDisplay_playheadUpdateHandler(event:mx.events.VideoEvent):void
{
if(!captured && videoDisplay.playheadTime>=0){
capture();
}
}
private function capture():void
{
var bmpData:BitmapData = new BitmapData(videoDisplay.width, videoDisplay.height);
bmpData.draw(videoDisplay);
captured = true;
store(...); //????????
}
in order to capture a frame from a videoDisplay object
1) is it correct or am i doing something wrong?
2) what can i do to store the bmpData as .jpg at my computer?
i am using flex4.5 and it is an air app...
any ideas??
Thanks in advance!!

The following code should help you
var jpegEncoder:JPEGEncoder = new JPEGEncoder(90);
var jpgSource:BitmapData = new BitmapData(videoDisplay.width,videoDisplay.height);
jpgSource.draw(this);
var fileReference:FileReference = new FileReference();
fileReference.save(jpegEncoder.encode(jpgSource),"videoImage.jpg");
To use jpeg encode you need to have to import
import mx.graphics.codec.JPEGEncoder;
The above changes should be enough to allow the user to take a snapshot of a running video.
Please note, that with this the user will be prompted to select the location of the file.
Incase you want a silent save, let me know, I will put up the required code.

Somewhere in your application keep an image tag as follows.Most apt place should be just below the video.
<mx:Image scaleContent="true" width="150" height="120" maintainAspectRatio="false" id="myScaledSnapshot"/>
Now with this done, do the following changes in your code:
private function capture(filename:String):void
{
var bitmapData:BitmapData = new BitmapData(videoDisplay.width, videoDisplay.height);
bitmapData.draw(videoDisplay,new Matrix());
var bitmap : Bitmap = new Bitmap(bitmapData);
var jpg:JPEGEncoder = new JPEGEncoder();
var ba:ByteArray = jpg.encode(bitmapData);
myImageSnapshot.source=ba;
var jpegEncoder:JPEGEncoder = new JPEGEncoder(50);
var imageSnapshot:ImageSnapshot = ImageSnapshot.captureImage(this.myImageSnapshot,90,jpegEncoder);
var imageByteArray:ByteArray = imageSnapshot.data;
var newImage:File = File.desktopDirectory.resolvePath("KioskThumbs/"+filename+".jpg");
fileStream = new FileStream();
fileStream.open(newImage, FileMode.UPDATE);
fileStream.writeBytes(imageByteArray);
fileStream.close();
captured = true;
}
The above code really doesnt do anything special.
Its just using an image 'component' from flex, making it do the work for scaling the Video image, then taking the snapshot of this resized image component, and then writing it into a file.

My capture at last is:
private function capture(filename:String):void
{
var bitmapData:BitmapData = new BitmapData(videoDisplay.width, videoDisplay.height);
bitmapData.draw(videoDisplay,new Matrix());
var bitmap : Bitmap = new Bitmap(bitmapData);
var jpg:JPEGEncoder = new JPEGEncoder();
var ba:ByteArray = jpg.encode(bitmapData);
var newImage:File = File.desktopDirectory.resolvePath("KioskThumbs/"+filename+".jpg");
fileStream = new FileStream();
fileStream.open(newImage, FileMode.UPDATE);
fileStream.writeBytes(ba);
fileStream.close();
captured = true;
}
it works fine except the fact that i want to scale the photo lets say to 150 width 120height and not BitmapData(videoDisplay.width,videoDisplay.height); what can i do to solve that?
Thanks a lot all of you!

Related

Duplicating bitmapData from a loader

I'm trying to develop an app for iOS using flash CS6. I have imported an image using a loader. I now want to be able to create a duplicate instance of the loaders bitmap data and have been trying:
var my_loader:Loader = new Loader();
my_loader.load(new URLRequest("cats.jpg"));
my_loader.scaleX = 0.2;
my_loader.scaleY = 0.2;
addChild(my_loader);
var duplicationBitmap:Bitmap = new Bitmap(Bitmap(my_loader.content).bitmapData);
duplicationBitmap.x = 300;
duplicationBitmap.y = 300;
addChild(duplicationBitmap);
Unfortunately when I test the code it doesn't work. I get the initial loaded image but not the duplicate, I also get an output error of:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Main()
Any ideas would be greatly appreciated.
Bitmap(my_loader.content) is a DisplayObject, not neccessary a Bitmap, that gives you the nullpointer error.
For copying bitmapData, you should use BitmapData.clone().
You can draw the loader on to a BitmapData object when the loader initializes, then simply use it to create as many Bitmap objects as you need when the loader completes.
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
var loaderBitmapData:BitmapData;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, loaderInitEventHandler);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteEventHandler);
loader.load(new URLRequest("eXO-01.png"));
function loaderInitEventHandler(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.INIT, loaderInitEventHandler);
loaderBitmapData = new BitmapData(event.target.width, event.target.height);
loaderBitmapData.draw(event.target.loader as Loader);
}
function loaderCompleteEventHandler(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderCompleteEventHandler);
createBitmaps();
}
function createBitmaps():void
{
var image1:Bitmap = new Bitmap(loaderBitmapData);
image1.scaleX = image1.scaleY = 0.2;
var image2:Bitmap = new Bitmap(loaderBitmapData);
image2.scaleX = image2.scaleY = 0.4;
image2.x = image2.y = 100;
addChild(image1);
addChild(image2);
}

Adobe AIR Mobile on iOS - processing text very slow

I am writing an iOS app in Flash Builder 4.6 that will need to process a large text file. So far, I am just trying to get the version information from the file, a simple task that runs very fast in the simulator but takes a very long time (on the order of 10 seconds) on my iPad 2.
As the file is large (about 11MB), I am trying to only read far enough into the file to get the version data. Is it possible that the filestream loads the whole file into memory anyway? If so, how do I avoid that?
Note that the code works correctly, and displays the correct version info.
Any advice on how to speed it up would be greatly appreciated.
The file being processed can be found here: http://oanda.sca.org:80/oanda.db
The code is as follows:
Calling code:
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
var DT:DatabaseTools = new DatabaseTools();
var version:String;
//Get version information from local file:
version = DT.GetLocalVer("oanda.db");
lblLclVer.text=version;
}
Actual function:
public function GetLocalVer(fileName:String):String
{
var out:ByteArray = new ByteArray();
var line_Out:String;
var char:int;
var line:ByteArray = new ByteArray();
var stream:FileStream = new FileStream();
var file:File = File.documentsDirectory.resolvePath(fileName);
var lineString:String;
var result:String = "Error";
stream.open(file,FileMode.READ);
var found:Boolean=false;
while((stream.bytesAvailable > 0) && !found)
{
char=stream.readByte();
if(String.fromCharCode(char) == "\n")
{
//reset the line
out=line;
line = new ByteArray();
//process the line to see if its the version info.
lineString=out.toString(); //since the version line never has UTF characters, convert it to a regular string
if(lineString.length > 25) //Any line shorter than this can't be the version line
{
var startOfLine:String = lineString.substr(0,17);
if(startOfLine == " ||C|Last update:")
{
result = lineString.substr(19,6);
found = true;
}
}
}
else
{
line.writeByte(char);
}
}
stream.close();
return result;
}
Thanks.

Converting a saved photo into a WritableBitmap in WinRT

I have taken a photo and saved it, but I need to do image processing on it. I tried using the following code, but WritableBitmap does not accept a bitmap, it needs a stream.
var writeableBitmap = new WritableBitmap(bitmap);
Here is the code:
CameraCaptureUI cam = new CameraCaptureUI();
var capturedImage = await cam.CaptureFileAsync(CameraCaptureUIMode.Photo);
if (capturedImage != null)
{
var img = new BitmapImage();
img.SetSource(await capturedImage.OpenReadAsync());
}
This should do the trick - You need to use the OpenAsync instead.
var dialog = new CameraCaptureUI();
var file = await dialog.CaptureFileAsync(CameraCaptureUIMode.Photo);
if (file != null)
{
var stream = await file.OpenAsync(FileAccessMode.Read);
var img = new BitmapImage();
img.SetSource(stream);
AccountPictureImage.Source = img;
}

AS3 Save SWF to iOS device and then load it back into a MovieClip

I have an iOS app that's using AS3 and AIR. I have external swfs downloading from a server and playing properly--all of this works perfectly fine.
I don't want the user to have to download these swfs every time they launch the app, though, so after the swf is downloaded the first time I want to save it to disk. That way I can load it from the local copy when I need it in the future, instead of downloading it again. I found this example online for saving files in AS3:
var file:File = File.applicationStorageDirectory.resolvePath("Swfs/" + "mySwfName" + ".swf");
var wr:File = new File(file.nativePath);
var stream:FileStream = new FileStream();
stream.open(wr, FileMode.WRITE);
stream.writeBytes(SWF_FILE_GOES_HERE, 0, SWF_FILE_DATA_LENGTH_GOES_HERE);
stream.close();
The problem is in the writeBytes line. The sample I found was for saving images to disk, and that line read like this:
stream.writeBytes(imageData, 0, imageData.length);
But I need to do this for a swf, not an image, and I'm not sure how to get the requisite data from my swf file. I've searched online for saving swf files, getting data from swf files, etc... But I haven't found anything. I need to get the swf's data and data length (I'm assuming this is a number of bytes?). Has anyone solved this problem before, or have a resource they can point me in the direction of?
UPDATE: Based on #LondonDrugs_MediaServices' comments, I figured out the writeBytes line:
stream.writeBytes(mySwf.loaderInfo.bytes, 0, mySwf.loaderInfo.bytesTotal);
This seems to work, in as much as it compiles and runs without errors. But when I try to grab the file and put it back into a movie clip, I get this error:
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
Which implies that the file was not, in fact, saved correctly. When I print the file locations, however, they seem to be right! When I save the file to the applicationStorageDirectory:
/Users/MyName/Library/Preferences/com.appdir/Local Store/Swfs/mySwName.swf
And the filename when I try and load it out of storage and into a movie clip:
/Users/MyName/Library/Preferences/com.appdir/Local Store/Swfs/mySwName.swf
This is the code I'm using to try and load it back into a movie clip:
var videoFileName:String = "Swfs/" + ENVAR.currentVideoSelected + ".swf";
var videoFilePath:File = File.applicationStorageDirectory.resolvePath(videoFileName);
var inFileStream:FileStream = new FileStream();
trace("[Loading] filepath: ", videoFilePath.nativePath);
inFileStream.open(videoFilePath, FileMode.READ);
var fileContents:String = inFileStream.readUTFBytes(inFileStream.bytesAvailable);
inFileStream.close();
var req:URLRequest = new URLRequest(videoFilePath.nativePath);
var mLoader:Loader = new Loader();
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onVideoLoadComplete);
mLoader.load(req);
And my onVideoLoadComplete function contains these lines:
var myMC:MovieClip = e.target.content as MovieClip;
addChild(myMC);
But I'm getting that error 2044 and it doesn't think the file exists. Can anyone spot the problem? Thanks!
You can get the raw bytes of a loaded swf file by using it's loaderInfo.bytes parameter. So for you example you could do this:
var file:File = File.applicationStorageDirectory.resolvePath("Swfs/" + key + ".swf");
var wr:File = new File(file.nativePath);
var stream:FileStream = new FileStream();
stream.open(wr, FileMode.WRITE);
stream.writeBytes(mySwf.loaderInfo.bytes, 0, mySwf.loaderInfo.bytesTotal);
stream.close();
Then to pull it back out into a MovieClip:
var videoFileName:String = "Swfs/" + ENVAR.currentVideoSelected + ".swf";
var videoFilePath:File = File.applicationStorageDirectory.resolvePath(videoFileName);
var inFileStream:FileStream = new FileStream();
inFileStream.open(videoFilePath, FileMode.READ);
var swfBytes:ByteArray = new ByteArray();
inFileStream.readBytes(swfBytes);
inFileStream.close();
var mLoader:Loader = new Loader();
var loaderContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loaderContext.allowCodeImport = true;
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onVideoLoadComplete);
mLoader.loadBytes(swfBytes, loaderContext);

In Actionscript, is there a way to create a copy or cached version of a Loader?

I'm loading an SWF animation and want to display it in multiple places concurrently, but the only way I could figure out how to do that is to load it every time I display it, as seen here:
private function playSounds():void {
for (var i:Number = 0; i < 14; i++)
{
for (var a:Number = 0; a < 16; a++)
{
if (boxes[i][a].x == linePos && boxes[i][a].selected == true && played[i][a] == false)
{
played[i][a] = true;
MovieClip();
var swf:URLRequest = new URLRequest("../assets/glow2.swf")
var glow:Loader = new Loader()
glow.load(swf)
glow.x = boxes[i][a].x - 25*0.7;
glow.y = boxes[i][a].y - 27*0.7;
glow.scaleX = 0.7;
glow.scaleY = 0.7;
this.addChild(glow);
glows.push(glow)
glowTime.push(0)
var sc:SoundChannel = new SoundChannel();
sc = (sounds[i] as Sound).play();
}
}
}
}
This is very very slow when it's being displayed more than, say, 5 times at once so I'm wondering if there's a way to only have to load it once and use it in multiple places.
You have the content property of the Loader.
Thus, load once, use the content many times.
edit: you may want to add a listener to know when the loading completes, before you use the loaded content:
addEventListener(Event.COMPLETE, completeHandler);
edit2:
var mc1:MovieClip = new MovieClip();
var mc2:MovieClip = new MovieClip();
var my_Loader:Loader = new Loader();
mc1.addChild(my_Loader);
mc2.addChild(my_Loader);
(haven't tried though).
One quick workaround is to create a second loader and pass the loaded bytes to that via the loadBytes() method:
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE,ready);
l.load(new URLRequest("../assets/glow2.swf"));
function ready(event:Event):void{
var mc:MovieClip = event.currentTarget.content as MovieClip;
var clone:Loader = new Loader();
clone.loadBytes(event.currentTarget.bytes);
addChild(mc);
addChild(clone);
clone.x = 100;
}
This will work in most cases. In some cases you should be able to get away with something as simple as:
var clone:MovieClip = MovieClip(new mc.constructor());
And there is also a 3rd option: 'blitting' your moviclip, which means you'll store one or more (depending how many MoveClip frames you need to cache) of BitmapData objects in memory which will draw() from the source MovieClip you want to draw (in multiple locations at the same time).
The loadBytes approach achieves what the question suggests: creates a copy of the Loader, so it re-uses the bytes loaded, but initializes new content, so uses memory for that.
If this is not what you need, caching the MovieClip using BitmapData is your best bet.

Resources