High quality, high performance thumbnails in Flash

Ever need a thumbnail of an image in Flash? I do, and honestly speaking, the resampling that Flash does is less than ideal. Unless you only need to resize by half or bigger. But my thumbnails usually need to be smaller.

I searched for a solution and found on voq.com a promising library with some algorithms that worked quite nicely and a demo. The quality was nice, but the speed was slow. It also fiddled with the color a little bit. If I put the “easyScaling” parameter down from .5 to .25 I ended up with a nicer thumbnail but was slower to make and had more color disfiguration. You could tell what the original piece was better though.

After some more searching I found that Brooks Andrus played with some algorithms using Pixel Bender. The conclusion to that was he found he could do the same thing with the “smoothing” option in the BitmapData.draw() method. And this still leaves me with yucky thumbnails at small sizes.

I began thinking I would need to take the algorithms from the first source and port them into pixel bender like Brooks did for his ThumbGenie application.

I thought I’d try one more option first. I had played around in my mind with the idea of resizing a bitmap in half, then in half again, until I reached my destination size, since resizing by half still had good results. After discussing it with Tyler I tried it out and ended up with some great results. That last resize to get to the final thumbnail size wasn’t half because in an imperfect world your thumbnails aren’t always a power of two times smaller than your original. So I had an odd-man-out scale at the end I was applying. Tyler suggested I put that odd-man-out scale at the first instead. So instead of:

1000x750 * .5 resize * .5 resize * .5 resize * .8 = 100x75

It worked like this:

1000x750 * .8 resize * .5 resize * .5 resize * .5 = 100x75

Doing it that way landed me with even better results! It seems that Flash does it’s best work when resizing by exactly 1/2.

Thumbnail tests

The above image shows my test results as follows taking a snapshot of my homepage in an HTMLLoader in AIR.

  1. Regular BitmapData.draw() method without smoothing (0 miliseconds)
  2. Regular BitmapData.draw() method with smoothing (1 miliseconds)
  3. Using my own method that resizes by the odd scale first, then by halves (8 milliseconds)
  4. Using my method but after the odd scale, only scaling down by quarters (33 miliseconds)
  5. Using the voq Lanczos3 method with easyScaling at .25 and sharpening (694 miliseconds)
  6. Using the voq Lanczos3 method with easyScaling at .25 and no sharping (676 miliseconds)
  7. Using the voq Triangle method with easyScaling at .25 and sharpening (275 miliseconds)
  8. Using the voq Triangle method with easyScaling at .25 and no sharpening (266 miliseconds)

My personal opinion is that the best looking thumb is #3. I was surprised that #4 wouldn’t be as good. I knew the performance would be lower because I was doing more iterations of BitmapData.draw(), but I thought that scaling it to 75% (down a quarter) each time would end up nicer. Looks like 50% is the best scale to use.

The voq.com algos looked pretty decent, but obviously were slow. I’m quite happy with the solution I’ve found. I only had to write a little bit of code, it’s fast enough, and looks better than any other solution I’ve found (even Fireworks resizing IMO).

Here is the method I wrote for this. Note, when scaling up, it seemed to look better to just use smoothing and do it in one draw() and not iterations of 2.

private static const IDEAL_RESIZE_PERCENT:Number = .5;

public static function resizeImage(source:BitmapData, width:uint, height:uint, constrainProportions:Boolean = true):BitmapData
{
	var scaleX:Number = width/source.width;
	var scaleY:Number = height/source.height;
	if (constrainProportions) {
		if (scaleX > scaleY) scaleX = scaleY;
		else scaleY = scaleX;
	}

	var bitmapData:BitmapData = source;

	if (scaleX >= 1 && scaleY >= 1) {
		bitmapData = new BitmapData(Math.ceil(source.width*scaleX), Math.ceil(source.height*scaleY), true, 0);
		bitmapData.draw(source, new Matrix(scaleX, 0, 0, scaleY), null, null, null, true);
		return bitmapData;
	}

	// scale it by the IDEAL for best quality
	var nextScaleX:Number = scaleX;
	var nextScaleY:Number = scaleY;
	while (nextScaleX < 1) nextScaleX /= IDEAL_RESIZE_PERCENT;
	while (nextScaleY < 1) nextScaleY /= IDEAL_RESIZE_PERCENT;

	if (scaleX < IDEAL_RESIZE_PERCENT) nextScaleX *= IDEAL_RESIZE_PERCENT;
	if (scaleY < IDEAL_RESIZE_PERCENT) nextScaleY *= IDEAL_RESIZE_PERCENT;

	var temp:BitmapData = new BitmapData(bitmapData.width*nextScaleX, bitmapData.height*nextScaleY, true, 0);
	temp.draw(bitmapData, new Matrix(nextScaleX, 0, 0, nextScaleY), null, null, null, true);
	bitmapData = temp;

	nextScaleX *= IDEAL_RESIZE_PERCENT;
	nextScaleY *= IDEAL_RESIZE_PERCENT;

	while (nextScaleX >= scaleX || nextScaleY >= scaleY) {
		var actualScaleX:Number = nextScaleX >= scaleX ? IDEAL_RESIZE_PERCENT : 1;
		var actualScaleY:Number = nextScaleY >= scaleY ? IDEAL_RESIZE_PERCENT : 1;
		temp = new BitmapData(bitmapData.width*actualScaleX, bitmapData.height*actualScaleY, true, 0);
		temp.draw(bitmapData, new Matrix(actualScaleX, 0, 0, actualScaleY), null, null, null, true);
		bitmapData.dispose();
		nextScaleX *= IDEAL_RESIZE_PERCENT;
		nextScaleY *= IDEAL_RESIZE_PERCENT;
		bitmapData = temp;
	}

	return bitmapData;
}

Enjoy!

Update: I was getting unsatisfactory images when the resize scale was more than 50%. For example, the original code posted would size an image from 100×100 to 60×60 using one BitmapData.draw() step. And it didn’t look that great.

I found that if I sized the image up to a scale that allowed it to be size back down by exactly 50% that the results were much better. In the above example, the 100×100 would scale up by 120% to 120×120, then scale down 50% to 60×60. The final image looks much better this way. The code has been updated to work like this. It also had the option to turn constrain proportions off.

Update: I’ve posted my code library to Google Code. You can see my final implementation of bitmap resizing in the ImageUtils class.

56 Responses to “High quality, high performance thumbnails in Flash”

  1. fatih şahinoğlu Says:

    as2 pleas ?

  2. Antoine Says:

    Hi,

    I remember using another class a bit similar to you a few years ago (same idea of several little resizing instead of one big) but yours is definitely better. Easy to set up, easy to use. Thank you for sharing! :)

  3. Flash thumbnails | Myactorstore Says:

    […] High quality, high performance thumbnails in Flash | Jacob WrightJul 17, 2009 … Ever need a thumbnail of an image in Flash? I do, and honestly speaking, the resampling that Flash does is less than ideal. Unless you only … […]

  4. Pier Says:

    I’m comparing some thumbnails in photoshop using your method, and draw() with smoothing on/off.

    In my experience the results are practically identical… pixel by pixel. Also in my experience the performance of your function is closer to native code than the results you are posting. I’m using GPU rendering in Adobe Air for iOS.

    It seems Adobe might have upgraded their code from 2009.

  5. Arne Says:

    Wow, you made my day! That really helped me out. I tried out so many other ways but didnt get the result. I also can not confirm Piers comparison. I tried all the latest drawing methods including drawWithQuality() and smoothing on and the results wiht PNGs were really poor.

  6. Anton Says:

    thank you so much for this post! the quality of my thumbnails has improved drastically and without increasing time to load.