This weekend I needed to encode a rather large image as a jpeg using flex. And you know what I found? It was slow. And cpu hungry. While this was not entirely unexpected, it was disappointing. I could have dealt with the program being slow and unresponsive during the time it took to encode the image, except for one problem - after about 15 seconds, the flex app would run up against the script timeout error. You know, the error that goes along the lines of "A script has executed for longer than the default timeout period of 15 seconds." And because of this, the jpeg encode never actually finished.
Well, that was no good. I poked a bit at extending the default timeout period, but really that was only a partial solution. Who knows how long it might take to encode the image on a slow computer? So I started poking at what it would take to make the jpeg encoding asynchronous. Sadly, actionscript and flex do not have the concept of just saying "hey go do this and get back to me when your done." This is probably because actionscript is single threaded - everything just runs on the browser's main thread.
Above we have a small sample app showing off the asynchronous jpeg encoder that we are going to build in this tutorial. You see the spinning lag meter? Right now, it is probably spinning nice and smooth. This is because flex has plenty of cpu cycles to update the spinning circle. When there isn't enough cpu time available, that animation will start to look choppy, as Flex will only be able to update it once every few hundred milliseconds, possibly longer. If there are no spare cycles, the circle will freeze and stop spinning altogether. If you hit "Normal Encode", that is probably exactly what will happen, and your entire browser will freeze along with it. That is because by clicking "normal encode", you told flex to encode the image displayed in the sample app (which is a 2800x2100 pixels) as a jpeg. If your computer is slow like mine, you will eventually get the "script timeout" error, and the encoding will never finish.
On the right hand side, however, is the button to trigger the asynchronous jpeg encoding. When you click that, the app won't freeze and the circle will keep spinning (although not quite as smoothly). You will also get a progress bar that shows the progress of the encoding. By moving the "pixels per iteration" slider bar to the right, you will increase the speed of the encoding, but decrease the responsiveness of the interface, and if you move the slider to the left, you get the opposite effect. A little farther down I will explain how that is accomplished.
So how do you even go about making something like this asynchronous? As
I said above, we only have one thread to work with, so it is all
hopeless, right? Not quite - there are ways to act like a multi-threaded
system, such as using something like setTimeout. You can use
setTimeout to emulate threading - essentially do a little work, but
then you queue more work for the future (not immediately). The app then
has a chance to breathe and deal with things like UI input during the
pauses between work items.
It was here that I ran up against another issue. How was I going to break the act of encoding a large jpeg into manageable little pieces? Well, there is no way to do that from outside the jpeg encoding object, so I went into the Flex source code and found the jpeg encoder. And this is where the real meat of this article starts.
Below we have the problem loop in the original jpeg encoder:
for (var ypos:int = 0; ypos < height; ypos += 8)
{
for (var xpos:int = 0; xpos < width; xpos += 8)
{
RGB2YUV(sourceBitmapData, sourceByteArray, xpos, ypos, width, height);
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
This loop can take a long time on large images - or at least on my computer it does. So the essential idea here is that we don't want to do this whole loop at once. We want to process the image a chunk at a time, giving the rest of the app time to work between chunks. So how do we do this? Well, we write a function that can process the loop a chunk at a time:
private function AsyncLoop(xpos:int, ypos:int):void
{
for(var i:int=0; i < PixelsPerIter; i++)
{
RGB2YUV(Source, xpos, ypos, SrcWidth, SrcHeight);
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
xpos += 8;
if(xpos >= SrcWidth)
{
xpos = 0;
ypos += 8;
}
if(ypos >= SrcHeight)
{
setTimeout(FinishEncode, 10);
return;
}
}
setTimeout(AsyncLoop, 10, xpos, ypos);
}
This AsyncLoop function takes a xpos and ypos into the image, and
starts processing from that point. But it only loops PixelsPerIter
times. After it has processed that many chunks, it leaves the loop, and
calls setTimeout on AsyncLoop. In this setTimeout call, it hands the
current x and y position in the image - so when the function is called
again, it starts up in the right place.
By setting a 10 millisecond timeout, the application will have time to
do other things before it gets back to processing this image data. The
PixelsPerIter value is very important - it determines how responsive
the application is during the image processing. If PixelsPerIter is 1,
the application will seem perfectly responsive. But it will also take a
long time for a large image to encode, because the encoding is pausing
for 10 milliseconds between event chunk. If you set the value to, say,
10000, the app will become unresponsive, because it is taking multiple
seconds to process data before it responds to any UI input. Playing
around, I've found that 128 is a pretty good value - the app slows down
a little bit, and it takes under 2 minutes to encode a 2880x2880 jpeg.
So what do we do here when we are done processing the pixels? Well we do
a setTimeout to FinishEncode, and return out of AsyncLoop.
FinishEncode holds all the code that was later than the main loop in
the original encode function:
private function FinishEncode():void
{
//EOI
if (bytepos >= 0)
{
var fillbits:BitString = new BitString();
fillbits.len = bytepos + 1;
fillbits.val = (1 << (bytepos + 1)) - 1;
writeBits(fillbits);
}
writeWord(0xFFD9);
}
Ok, well, that makes sense, but how to we signal the rest of the application that the encoding has completed? Its not like we can just wait for a function call to return. Because the encoding is asynchronous, the original encode call will return immediately - long before the actual encoding is finished. So instead, we use an event. In this case, I created my own event, because I wanted the event object to hold the encoded image:
public class JPEGAsyncCompleteEvent extends Event
{
public static const JPEGASYNC_COMPLETE:String = "JPEGAsyncComplete";
public var ImageData:ByteArray;
public function JPEGAsyncCompleteEvent(data:ByteArray)
{
ImageData = data;
super(JPEGASYNC_COMPLETE);
}
}
To use this event on the new jpeg encoding class, we add an attribute at
the top of the class, and make the class extend EventDispatcher:
[Event(name=JPEGAsyncCompleteEvent.JPEGASYNC_COMPLETE, type="JPEGAsyncCompleteEvent")]
public class JPEGAsyncEncoder extends EventDispatcher
{
...
And now to fire the event, we add a dispatchEvent call to the
FinishEncode function:
private function FinishEncode():void
{
//EOI
if (bytepos >= 0)
{
var fillbits:BitString = new BitString();
fillbits.len = bytepos + 1;
fillbits.val = (1 << (bytepos + 1)) - 1;
writeBits(fillbits);
}
writeWord(0xFFD9);
this.dispatchEvent(new JPEGAsyncCompleteEvent(byteout));
}
I also wanted to add a progress event, so that I could show a progress bar to the user as the jpeg was encoding. To do this, I added another event to the class:
[Event(name=JPEGAsyncCompleteEvent.JPEGASYNC_COMPLETE, type="JPEGAsyncCompleteEvent")]
[Event(name=ProgressEvent.PROGRESS, type="flash.events.ProgressEvent")]
public class JPEGAsyncEncoder extends EventDispatcher
{
...
And with this progress event, I added a bunch of logic to the
AsyncLoop code to fire the progress event at appropriate points:
private function AsyncLoop(xpos:int, ypos:int):void
{
for(var i:int=0; i < ChunksPerIter; i++)
{
RGB2YUV(Source, xpos, ypos, SrcWidth, SrcHeight);
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
xpos += 8;
if(xpos >= SrcWidth)
{
xpos = 0;
ypos += 8;
}
if(ypos >= SrcHeight)
{
setTimeout(FinishEncode, 10);
return;
}
CurrentTotalPos += 64;
if(CurrentTotalPos >= NextProgressAt)
{
this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,
false, false, CurrentTotalPos, TotalSize));
NextProgressAt += PercentageInc;
}
}
setTimeout(AsyncLoop, 10, xpos, ypos);
}
That piece of code actually uses a bunch of fields that I added to the class to keep track of total progress, so that the progress event gets fired only about once a percent or so.
Those are pretty much all the changes that I needed to make (barring the couple added fields). Below you can see the entirety of what I did to the code. I put "...." where code from the original jpeg encoder class would be - there is a lot of it, so I didn't want to paste it all here:
[Event(name=JPEGAsyncCompleteEvent.JPEGASYNC_COMPLETE, type="JPEGAsyncCompleteEvent")]
[Event(name=ProgressEvent.PROGRESS, type="flash.events.ProgressEvent")]
public class JPEGAsyncEncoder extends EventDispatcher
{
......
private var DCY:Number = 0;
private var DCU:Number = 0;
private var DCV:Number = 0;
private var SrcWidth:int = 0;
private var SrcHeight:int = 0;
private var Source:Object = null;
private var TotalSize:int = 0;
private var PixelsPerIter:int = 128;
private var PercentageInc:int = 0;
private var NextProgressAt:int = 0;
private var CurrentTotalPos:int = 0;
private var Working:Boolean = false;
.....
public function set PixelsPerIteration(val:int):void
{ PixelsPerIter = val; }
public function get ImageData():ByteArray
{ return byteout; }
public function encodeByteArray(raw:ByteArray, width:int, height:int):Boolean
{ return internalEncode(raw, width, height); }
public function encode(image:BitmapData):Boolean
{ return internalEncode(image, image.width, image.height); }
private function internalEncode(newSource:Object, width:int, height:int):Boolean
{
if(Working)
return false;
Working = true;
Source = newSource;
SrcWidth = width;
SrcHeight = height;
TotalSize = width*height;
PercentageInc = TotalSize/100;
NextProgressAt = PercentageInc;
CurrentTotalPos = 0;
setTimeout(StartEncode, 10);
return true;
}
private function StartEncode():void
{
// Initialize bit writer
byteout = new ByteArray();
bytenew = 0;
bytepos = 7;
// Add JPEG headers
writeWord(0xFFD8); // SOI
writeAPP0();
writeDQT();
writeSOF0(SrcWidth, SrcHeight);
writeDHT();
writeSOS();
DCY = 0;
DCV = 0;
DCU = 0;
bytenew = 0;
bytepos = 7;
this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,
false, false, 0, TotalSize));
setTimeout(AsyncLoop, 10, 0, 0);
}
private function AsyncLoop(xpos:int, ypos:int):void
{
for(var i:int=0; i < PixelsPerIter; i++)
{
RGB2YUV(Source, xpos, ypos, SrcWidth, SrcHeight);
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
xpos += 8;
if(xpos >= SrcWidth)
{
xpos = 0;
ypos += 8;
}
if(ypos >= SrcHeight)
{
setTimeout(FinishEncode, 10);
return;
}
CurrentTotalPos += 64;
if(CurrentTotalPos >= NextProgressAt)
{
this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,
false, false, CurrentTotalPos, TotalSize));
NextProgressAt += PercentageInc;
}
}
setTimeout(AsyncLoop, 10, xpos, ypos);
}
private function FinishEncode():void
{
//EOI
if (bytepos >= 0)
{
var fillbits:BitString = new BitString();
fillbits.len = bytepos + 1;
fillbits.val = (1 << (bytepos + 1)) - 1;
writeBits(fillbits);
}
writeWord(0xFFD9);
this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,
false, false, TotalSize, TotalSize));
this.dispatchEvent(new JPEGAsyncCompleteEvent(byteout));
Working = false;
}
.......
}
The methods internalEncode, encode, and encodeByteArray replace
methods in the original jpeg encoder class. Everything else shown above
is an addition. I added all of those fields show at the top because
unlike the original encoder (where almost everything was done in a
single function call), I needed to remember things across function
calls.
One thing to note, this encoder does not implement IImageEncoder like
the original jpeg encoder does. This is because that interface expects
the encode and encodeByteArray to return a byte array containing the
encoded image. Obviously, since this class does all the encoding
asynchronously, it can't return the byte array straight out of the
original call, because the encoding hasn't actually been done yet.
Instead, the functions return a boolean. If the call to encode returns
false, it is because this instance of the class is already encoding an
image - so it can't start encoding another one yet.
So we have this awesome asynchronous jpeg encoder class. How do we use it? Well, lets take a look at some sample code:
private function startEncode(imageBitmapData:BitmapData):void
{
var encoder:JPEGAsyncEncoder = new JPEGAsyncEncoder(80);
encoder.PixelsPerIteration = 128;
encoder.addEventListener(JPEGAsyncCompleteEvent.JPEGASYNC_COMPLETE, encodeDone);
encoder.addEventListener(ProgressEvent.PROGRESS, encodeProg);
encoder.encode(imageBitmapData);
}
private function encodeProg(event:ProgressEvent):void
{
var percentage:String = ((event.bytesLoaded / event.bytesTotal)*100) + "%";
//Display the percentage somewhere
}
private function encodeDone(event:JPEGAsyncCompleteEvent):void
{
var data:ByteArray = event.ImageData;
//Do something with the encoded image
}
And there you go. Thats all you need to use this asynchronous jpeg encoder class. You can grab the source code for the whole example above here, and feel free to use it for whatever you want.
Source Files:
Indeed a gr8 way to work on images, but what about charts. asyncEncoder.encode(Bitmap(img.content).bitmapData); for charts we cant get .content, There we get a bitmap as
But this is not giving advantages of async encoding, Screen still hangs and timeout is reached. let me know if u have any solution/workaround
Just want to say that this is an awesome tutorial!
This article is great! This helped me solve a huge problem. I had to iterate through several thousand items in a datagrid and the operation was timing out. Not anymore! Thank you very much for posting this!
Thanks, Blake Eaton
Hi there:
I'm working on a project in Flash MX 2004 for a client and I'm having a problem that is similar to the one that you described in your tutorial. Unfortunately setTimeout is not supported in MX04 and setInterval requires quite a bit of tricky garbage collection. Do you have any recommendations for what I might be able to do in MX04 to faux-multithread? The application I'm working on is targeted for FlashPlayer 7.
Hi, Thank you for this! I was about to write something very similar when I ran across yours. I'm using it in the latest version of the CleVR Stitcher and would like to mention you in the credits. How would you like to be credited? Incidentally, the Stitcher uses the same method for fake threading, and it's an absolute nightmare to keep track of. We have literally dozens of different loops that are broken up like this! Oh how I wish there were an easier way. Regards,
Matt
Hello, thanks for this incredible class, i've been using in a personal project and i have a problem to free the memory that i use. I use it to encode 6 images and in the process i put the byteArray in 6 global variables, so then i pass them to MySQL to store them. But i've notice with the profiling that the memory doesn't free after the process. If you have any clue, please let me know, thanks very much in advance for your time. Sory about my english, is not my native language. Agustin
I too have memory issues with the code when being executed on multiple images How can I free the memory ?
So you are running it multiple times in a row?
This code resides in a function that is being called in a for loop multiple times (once for each image:)
tmploader is simply a loader (air.loader()). I am using Javascript/HTML by the way for my lack of knowledge in FLEX, and its lack of support for my language.
Thanks in advance for your help.
good article, good idea to overcome single threaded structure of flash. for these kind of problems, there is a function named callLater, which calls the function after the screen is drawn. So instead of using a timer, you could use callLater which would be more consistent&efficient.
I have noticed that the quality of the images produced by the JPEGAsynencoder does not match that of other encoders available (i.e. php's built in image compression functions from the gd library)
Any explanation ? or hints/workarounds for improving the quality of compressed images by JPEGAsyncEncoder ??
Please have a look at the following samples:
original image: http://img212.imageshack.us/img212/5930/67624460qh5.jpg Size: 110KB
Image encoded with JPEGAsync: http://img136.imageshack.us/img136/3554/77570198qp0.jpg Size:46.7KB
Image encoded with Php's built-in functions (gd library): http://img382.imageshack.us/img382/7751/1492if8.jpg Size:33.4KB Much smoother than the JPEGAsync version.. and smaller too
In the images I posted in my previous comment,, Please pay attention to the white board in the picture. In one image ,, the word "hotmail" can be easily read,, while this is not the case in the other.
I tried increasing the quality measure to 80 and 100 (as an argument passed to JPEGAsyncEncoder) and it only resulted in increasing the file size.. The picture did not get any smoother..
Hmm, yeah, that is a big quality difference.
In any case, I don't actually know how the code behind the JPEG encoder works - when I wrote this asyc encoder, I just took the bulk of the code from Flex. You might want to try encoding that image with the regular Flex JPEG encoder, and see if it has the same issues - I bet it does.
May I ask how you are creating the encoded image and saving it using the JPEGAsyncEncoder? I am quite curious because I haven't noticed any quality issues especially like the ones you have.
Thanks again for the fast response..
Below is the whole code I have :
Basically, fileList is an array storing several files (all images). They are collected by a drag and drop function.
Then,, I have:
Inside saveTheFile,, I have:
This is the last bit that writes the images after being encoded
ok, I missed these two lines,,
they are right above the line: var bd = new air.BitmapData(600,tmploader[i].content.height);
It looks to me that you are resizing the image before you encode it (the lines where you set the height and width). I'm not sure what algorithm is being used underneath the covers to do the resize, but I bet it is that algorithm that is causing the artifacts you see in the encoded image.
‘Imagesmoothing’ is disabled by default… You can enable it by using the parameters when calling the ‘draw’ function.
Example:
Thanks for the AsyncJPEGEncoder! Works like a charm. Using it to encode multiple images in a loop as well.
Nice, thank you.
But what on earth is “a bunch of logic” ?!?
What would it take to get you to write a tutorial showing how to integrate this with AlivePDF? I’ve tried unsuccessfully all day long and need some expert help. Pleeeeeeeease? :D
Hey,
Any idea if jpeb compression works with better speed in FP10?
I am trying to use this to process a bunch of images, and I’m noticing that a bunch of BitString objects are hanging around and don’t seem to be garbage collected.
If I do a single image (3888×2592 which has been resized to 150×100), there are 65882 instances left in memory that don’t seem to get garbage collected.
If I do multiple images, then there are bigger issues, as each image causes about the same number of instances left in memory.
Has anyone found a fix for the memory leak in this Class? I love this Encoder and am using it in a project but after encoding multiple images the memory keeps building up… I currently running it in debug and trying to figure it out where the leak is. If I find anything I will post it here.
I used the term Memory Leak in my last post. What I really mean is the loiterring objects in memory. The fact that after you are done encoding a JPEG the memory usage doesn’t go back down.
Do you know if the memory issue is just for this async version of the jpeg encoder, or is it also a problem with the regular Flex one?
Maybe the memory issues are related to the setTimeout() call. Usually a corresponding clearTimeout() should be called. From the docs:
"If you intend to use the clearTimeout() method to cancel the setTimeout() call, be sure to assign the setTimeout() call to a variable (which the clearTimeout() function will later reference). If you do not call the clearTimeout() function to cancel the setTimeout() call, the object containing the set timeout closure function will not be garbage collected."
Hmm, that's interesting. From what I understood, you only needed to call clearTimeout if you wanted to cancel the pending call. I'll have to run some tests at some point and find out.
Thanks for the nice and detailed tutorial. Hats off..! :-)
Just wanted to say thanks for a really brilliant solution to a very annoying problem. The code works like a charm (no modification required on my part). I wanted an easy way for my wife to upload images to our family site via an Adobe AIR application that auto resizes the images on drop, but was having trouble with the image loading using the JPEGEncoder (as it was freezing up the app until it was completed). Your class fixed the problem and even allowed me to add progress bars to each image load. Love it! Thanks again.
Downloading the code...
Great work! Thank you veeeeeeeery much.
Brilliant work man...
Good stuff :)
http://www.actionscriptdeveloper.co.uk
I'm also having the memory problems Dennis noted when running a batch of images. The bitstrings seem to pile up in the memory profiler, and loiter around.
The virtual memory only my system keeps increasing while this is running until eventually the app crashes.
As for Seme1's comment about the image quality problems...I also had the same problem when I downsized the bitmaps before encoding. The image smoothing noted by "2am" didn't work to fix this. I also tried using a special biLinear and BiCubic sampling class, which also didn't work.
The fix for this was to apply a blur filter BEFORE the bitmap is resized. The amount of blur must be adjusted according to the initial image size.
Any help with the loitering bitstrings would be a big help.
Thanks all and thanks again for this extremely useful class.
Using this very usefull class since Flash 9 I run into one major problem now, using Flash 10: Security policy of Flash 10 requires real user action (click or key press) to upload the async encoded jpg.
Calling upload function inside of "function encodeDone()" doesn't really upload the encoded *.jpg anymore now, because calling that from complete handler isn't the required first call of upload function...
... and because it isn't really a result of an user action.
May be I misunderstand some here... Are there any notes, or expiriences about that available? Some helpfull hints to this problem?
Anyway, many thanks for JPEGAsyncEncoder...
-- Regards from Germany Endur Unixman
Have fixed that by my self using application/octet-stream instead of multipart/form-data. Had to change our server sided perl too to match this.
-- Thanks Endur
Hi,
and thanks for this class. It works great but, when batching/resizing a lot of images (with a resize external class), I experience memory leaks that in the end cause the application to crash. I tried all the (very few) garbage collection techniques I know, but as of know memory can easily bypass my 2Gb of RAM simply resizing 10 digital camera photos to 320x240,640x480 and encoding them.
Any ideas?
Thanks in advance,
Mark
SOLVED. I was not doing the bd.dispose() method. Thanks,
Mark
i just want to set the record straight on my earlier comment.
after reading all over about the memory leaks in flashplayer, and my relative inexperience, I freaked out and was convinced something was wrong in the the jpeg encoding itself, that little bits of memory were being used each time encoding.
In fact, the problem wasn't with the async encoder...it was actually a problem with some of the code I had lifted for grabbing a list of files from the users filesystem into flash.
It seems the fileReferenceList object was storing not only the information to reference the file (like the file name and location) but it was also storing the *entire contents of the file* as it was uploaded into flash. This property is read only so there was way to delete it.
The odd thing is that I wasn't able to see this happening in profiler...it wasn't registering as a big user of memory, and I think this is because the data is somehow cached by the browser.
Regardless, I just wanted to write that the jpeg async encoder was NOT the source of my woes. My app can now upload and resize 1000's of 21 megapixel images without a hitch.
Thanks again for this tip!
(Now if anyone knows how to perserve the exif data...)
Hi, how did you fix your issue with the filereference ?
thanks
Could you explain why you are doing this , thanks.
Yeah we originally wrote this code because we found encoding a large image (10000 x 7500) was taking well forever and would actually lock up the browser. This allows the browser and the rest of the flex application to update itself and allow for user interaction to happen while encoding. Not to mention we can show a progress bar during the encoding.
hi all, i want to capture a application screenshot and i tried to encode with the asynchronous encoder . i had taken the screenshot with Timer control (400 ms ) . still it has some hangover(maybe memory leakage ). kindly help me out
hi boss why you posted with your company information.. they are going to kick off you...
awesome!! thanks! :)
Asynchronicity is nice, but the encoding time still seems very slow versus something like 'Shrink-O-Matic' or 'ImageSizer'. Anyone know what those apps are doing differently?
I am not really sure what is different. They certainly could be using their own encoding. Also, have you tried running this code in AIR? I don't know if that makes a performance difference or not.
Had problems posting a long "ThankYou" post (hashcash issue O_o), so here's the short one: You Guys are Awsome!
Thanks for this solution, however if you want to save the encoded jpg with FileReference.save it won't work, as FP10 throws a security exception if you call fr.save from anywhere except a onClick function. So I can't save my jpg after encoding it using your asynchronous encoder, since I cannot call the fr.save until the encoder is complete and there is no way to know it's complete except by the complete event it throws. Any idea how to get around this?
I would just listen to the complete event and then enable a download button. You can show a progress bar so the user knows that something is happening. You could even be a little bit more intrusive by popping up a window saying it's encoding is complete and they can download it now.
Yes, it looks like having two buttons for the user to click is the way to go. A "prepare download" button to start the encoding, and then a save button can appear when it's ready to be saved.
Hi, is the "missing" cleartimeout was an issue after all ?
Wow...What a tutorial..great. Thanks for sharing the knowledge.
hi,
i am suing this code to generate multiple screen shots so i have to manage all completed encodings is there any way to manage at once....
thanks
This is simply amazing, many thanks for such a great tool.
Great tutorial, thanks! How to abort the enocding progress after it started? Can you help me with that?
Sarah
Superb tutorial, very clever! Thank you & well done :D
Hey Great tutorial!! But I am not getting the same quality as I get from Synchronous JPEG encoder. My Application is taking snapshot of movie clip and pasting into pdf using alive pdf When I use ImageSnapshot.captureImage(source,250,new JPEGEncoder(),true) it gives me better quality campare to imageSnap:BitmapData = ImageSnapshot.captureBitmapData(source) and pass this bitmap data in asnycronous JPEG encoder
I guess due to conversion of movie clip into bitmap, quality of image degrades. AnyBody knows how can i get the same quality as i got frm Synchronous JPEG encoder
hi... we are testing the flex application using qtp repeatedly i am getting the error 1502.... i didnt use any loops. and passing single set of user name and password.... kindly help me to get out of this issue
Thnx a lot for this tutorial :)
Dude... your a life saver!!!!!!!!!!!!!! This should be a default library included in flex