“Multithreading” in ActionScript, PartII – JPEGEncoder Example

This article is following “Multithreading” in ActionScript

Again, I’m going to start the atricle with:
Multithreading in ActionScript, no such thing!

Yes, they cooked PixelBender, that is very cool. You can do computations in the bender, they run asynchronously and are faster than ActionScript.

But now, let’s come back to our stuff. We are talking about ActionScript and code slicing, so the interface doesn’t get locked while your ActionScript program runs.

Here is one example, maybe this particular one can be better done with Bender, but it can serve as an example of how you can change a double “for” loop into slices for IThread.
We take an image and feed it into the JPGEncoder. The encoder outputs the jpeg file into a BitmapData instance. Then we load the BitmapData using a Loader, just to check the process was completed successfully.
You can see the progressbar advancing only in the threaded version. In the standard one, you can’t even move the browser window while processing. The threaded version takes a bit longer, depending on how much you interact with the interface.

Note: Someone suggested that dispatching events is slower than using function references directly. This example shows that the time penalty for using events is negligible, because events are not sent at each slice execution, but rather only on frame event and ready event, and those are quite few.

Here is the original main loop in Adobe’s JPGEncoder (corelib http://code.google.com/p/as3corelib/)

public function encode(image:BitmapData):ByteArray
{
	// Initialize bit writer
	byteout = new ByteArray();
	bytenew=0;
	bytepos=7;
 
	// Add JPEG headers
	writeWord(0xFFD8); // SOI
	writeAPP0();
	writeDQT();
	writeSOF0(image.width,image.height);
	writeDHT();
	writeSOS();
 
 
	// Encode 8x8 macroblocks
	var DCY:Number=0;
	var DCU:Number=0;
	var DCV:Number=0;
	bytenew=0;
	bytepos=7;
	for (var ypos:int=0; ypos<image.height; ypos+=8) {
	    for (var xpos:int=0; xpos<image.width; xpos+=8) {
	    RGB2YUV(image, xpos, ypos);
	    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);
	  }
	}
 
	// Do the bit alignment of the EOI marker
	if ( bytepos >= 0 ) {
		var fillbits:BitString = new BitString();
		fillbits.len = bytepos+1;
		fillbits.val = (1<<(bytepos+1))-1;
		writeBits(fillbits);
	}
 
	writeWord(0xFFD9); //EOI
	return byteout;
}

And here is the modified code

//thread job context
private var started:Boolean = false;
private var DCY:Number;
private var DCU:Number;
private var DCV:Number;
private var image:BitmapData;
private var ypos:int;
private var xpos:int;
 
public var progress:Number;
 
public function runSlice():Boolean
{
	if (!started)
	{
		// Initialize bit writer
		byteout = new ByteArray();
		bytenew=0;
		bytepos=7;
 
		DCY=0;
		DCU=0;
		DCV=0;
 
		// Add JPEG headers
		writeWord(0xFFD8); // SOI
		writeAPP0();
		writeDQT();
		writeSOF0(image.width,image.height);
		writeDHT();
		writeSOS();
 
		xpos = 0;
		ypos = 0;
		started = true;
	}
	progress = ypos / image.height;
 
	RGB2YUV(image, xpos, ypos);
	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 >= image.width)
	{
		ypos += 8;
		xpos = 0;		
	}
 
	//we are ready
	if (ypos >= image.height)
	{
		// Do the bit alignment of the EOI marker
		if ( bytepos >= 0 ) {
			var fillbits:BitString = new BitString();
			fillbits.len = bytepos+1;
			fillbits.val = (1<<(bytepos+1))-1;
			writeBits(fillbits);
		}
		writeWord(0xFFD9); //EOI
 
		return false;//job finished
	}
	//wait for next slice
	return true;
}

Test the application here: example
You can compare the execution time. Note how the interface is still responsive while processing the threaded version.

Download: ThreadsExample ActionScript project


You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

AddThis Social Bookmark Button

7 Responses to ““Multithreading” in ActionScript, PartII – JPEGEncoder Example”

  1. Nice example on ‘chunking’ hungry processes… have you tested performance on swfs that have either dynamic or purposefully slow frame rates?

    Just for reference – this approach is commonly referred to as ‘pseudo threading.’ Alex Harui has some additional thoughts on general applications as well.

    http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html

  2. Rick Winscot, no, I only tested for fixed framerate. Also, some constants in the ThreadsController are very sensitive, depending on the rest of your application. One may fine-tune those to get desired results. For example, if you’re showing animations, you will give more time to rendering, or if the “threaded” task is more important, you do the other way..

    10x for the comment, and yes, I read Alex Harui’s article long time ago. I should have posted the link myself but I couldn’t find the article any more.

  3. [...] “Multithreading” in ActionScript, PartII – JPEGEncoder Example [...]

  4. Nice work

    I made something very similar a while back

    http://blog.barncar.com/?p=10

    I made my version dispatch progress events as each chunk was processed – this gives you the ability to show feedback too on large processing jobs.

  5. @mattjpoole
    thank you
    the concept is obvious, nothing special in my article, just an example. just before writing the “library”, i searched the web for something similar and because i didn’t find something good enough, i shared mine just as i used it in minibuilder. lots of the stuff in minibuilder have a minimalistic approach, i am concerned about speed and size.

    a side note, i don’t think dispatching progress events is a good idea, i left the progress query to be done outside the framework, as some tasks don’t need to dispatch progress state and it would be a waste of precious time.
    a better option, i think, would be to enforce the task to provide the value of it’s progress through the interface. but that would also be an overhead, i like the frameworks to be as light as possible.

  6. Evan Gifford Says:

    Nice work! I was just attempting to do the same when I though “Ah, someone must have done this already >> Google”.

    One thing you might try is updating JPEG encoder per this link: http://www.bytearray.org/?p=775 if FP10 is an option, this makes encoding 3-4 times faster!

  7. thank you for the link, great enhancement!
    note that there are more complex “threading” solutions out there to use

    as for me, i tend to use minimalistic stuff – sometimes not the best idea, sometimes a life savior.

Leave a Reply

Powered by WP Hashcash