Now that we’ve determined the best PNG compressors to create PNG images with, let’s delve into the world of JPEG compressors. As with PNG, we have multiple options to choose from in our Flash apps when we’re looking to encode images such as screenshots. Which is best? Today’s article delves into each compressor’s performance and file size efficiency.

Here are the contestants in today’s roundup, each with three JPEG quality settings in the normal range:

  • BitmapData.encode (JPEGEncoderOptions.quality=25)
  • BitmapData.encode (JPEGEncoderOptions.quality=50)
  • BitmapData.encode (JPEGEncoderOptions.quality=75)
  • Bloddy Crypto (quality=25)
  • Bloddy Crypto (quality=50)
  • Bloddy Crypto (quality=75)
  • as3corelib (quality=25)
  • as3corelib (quality=50)
  • as3corelib (quality=75)

Each of these was used to compress three types of images:

  • The Flash Pro CS5 icon from previous PNG test articles
  • A random photo with the same width and height as the icon
  • Noise generated by BitmapData.noise with the same width and height as the icon

Here’s the code for the test app:

package
{
	import by.blooddy.crypto.image.JPEGEncoder;
	import flash.display.JPEGEncoderOptions;
	import com.adobe.images.JPGEncoder;
	import flash.utils.ByteArray;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.text.TextField;
 
	public class CompressJPEGPerformance extends Sprite
	{
		private var tf:TextField = new TextField();
 
		public function CompressJPEGPerformance()
		{
			tf.width = stage.stageWidth;
			tf.height = stage.stageHeight;
			addChild(tf);
 
			load("Adobe_Flash_Professional_CS5_icon.png", onIconLoaded);
		}
 
		private function onIconLoaded(ev:Event): void
		{
			var bmd:BitmapData = ((ev.target as LoaderInfo).content as Bitmap).bitmapData;
			test("Icon", bmd);
 
			load("10082934002.jpg", onPhotoLoaded);
		}
 
		private function onPhotoLoaded(ev:Event): void
		{
			var bmd:BitmapData = ((ev.target as LoaderInfo).content as Bitmap).bitmapData;
			test("Photo", bmd);
 
			load("Adobe_Flash_Professional_CS5_icon.png", onIconLoadedAgain);
		}
 
		private function onIconLoadedAgain(ev:Event): void
		{
			var bmd:BitmapData = ((ev.target as LoaderInfo).content as Bitmap).bitmapData;
			bmd.noise(Math.random()*int.MAX_VALUE);
 
			test("Noise", bmd);
		}
 
		private function row(...cols): void
		{
			tf.appendText(cols.join(",")+"\n");
			tf.scrollV = tf.maxScrollV;
		}
 
		private function load(url:String, callback:Function): void
		{
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, callback);
			loader.load(new URLRequest(url));
		}
 
		private function test(title:String, bmd:BitmapData): void
		{
			var beforeTime:int;
			var afterTime:int;
			var time:int;
			var rect:Rectangle = new Rectangle(0, 0, bmd.width, bmd.height);
			var bytes:ByteArray;
 
			row(title);
			row("Compressor,Time,Size");
			bmd.getPixel(0, 0);
 
			beforeTime = getTimer();
			bytes = bmd.encode(rect, new JPEGEncoderOptions(25));
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("BitmapData.encode (q=25)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = bmd.encode(rect, new JPEGEncoderOptions(50));
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("BitmapData.encode (q=50)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = bmd.encode(rect, new JPEGEncoderOptions(75));
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("BitmapData.encode (q=75)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = JPEGEncoder.encode(bmd, 25);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("Blooddy (q=25)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = JPEGEncoder.encode(bmd, 50);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("Blooddy (q=50)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = JPEGEncoder.encode(bmd, 75);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("Blooddy (q=75)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = new JPGEncoder(25).encode(bmd);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("as3corelib (q=25)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = new JPGEncoder(50).encode(bmd);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("as3corelib (q=50)", time, bytes.length);
 
			beforeTime = getTimer();
			bytes = new JPGEncoder(75).encode(bmd);
			afterTime = getTimer();
			time = afterTime - beforeTime;
			row("as3corelib (q=75)", time, bytes.length);
 
			row();
		}
	}
}

Launch the app
Download the app

I ran this test in the following environment:

  • Flex SDK (MXMLC) 4.6.0.23201, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.6.602.171
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.8.2

And here are the results I got:

Compressor Time Size
BitmapData.encode (q=25) 23 26588
BitmapData.encode (q=50) 24 31188
BitmapData.encode (q=75) 24 38327
Blooddy (q=25) 83 40613
Blooddy (q=50) 83 46948
Blooddy (q=75) 84 56018
as3corelib (q=25) 640 40541
as3corelib (q=50) 649 46876
as3corelib (q=75) 651 55946

JPEG Compression Performance (icon)
JPEG Compression Size (icon)

Compressor Time Size
BitmapData.encode (q=25) 31 65996
BitmapData.encode (q=50) 35 129991
BitmapData.encode (q=75) 35 159547
Blooddy (q=25) 96 86675
Blooddy (q=50) 107 155782
Blooddy (q=75) 108 199564
as3corelib (q=25) 734 86603
as3corelib (q=50) 766 155724
as3corelib (q=75) 769 199493

JPEG Compression Performance (photo)
JPEG Compression Size (photo)

Compressor Time Size
BitmapData.encode (q=25) 44 272445
BitmapData.encode (q=50) 53 462318
BitmapData.encode (q=75) 63 676571
Blooddy (q=25) 141 507160
Blooddy (q=50) 180 956231
Blooddy (q=75) 219 1407526
as3corelib (q=25) 860 507088
as3corelib (q=50) 1005 956159
as3corelib (q=75) 1128 1407454

JPEG Compression Performance (noise)
JPEG Compression Size (noise)

In stark contrast to the complicated findings for PNG compressors, it’s easy to draw conclusions about the JPEG compressor options. The fastest compressor for all types of images, by a margin of about 3-4x, is BitmapData.encode. The smallest file sizes are also achieved with BitmapData.encode. So if you’re targeting Flash Player 11.3+, just use BitmapData.encode. If you aren’t then you won’t have that option available to you. In that case, choose Blooddy crypto since it is takes the #2 spot for both performance and size on all images. The JPEG compressor in as3corelib is the slowest and produces the largest file sizes, so there really isn’t any reason to use it.

Spot a bug? Have a question or suggestion? Post a comment!