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.
![]()
The above image shows my test results as follows taking a snapshot of my homepage in an HTMLLoader in AIR.
- Regular BitmapData.draw() method without smoothing (0 miliseconds)
- Regular BitmapData.draw() method with smoothing (1 miliseconds)
- Using my own method that resizes by the odd scale first, then by halves (8 milliseconds)
- Using my method but after the odd scale, only scaling down by quarters (33 miliseconds)
- Using the voq Lanczos3 method with easyScaling at .25 and sharpening (694 miliseconds)
- Using the voq Lanczos3 method with easyScaling at .25 and no sharping (676 miliseconds)
- Using the voq Triangle method with easyScaling at .25 and sharpening (275 miliseconds)
- 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.
July 17th, 2009 at 3:44 pm
That’s awesome Jac! It’s almost like you have this really cool twin brother who helped you a little too.
;) No shame in crediting myself.
Seriously, I’m glad to see this. It ended up looking very nice.
It might be a good idea to optimize and use the same (large) bitmapData to do the scale reduction (just draw over previous pixels) and then dump() it after doing a final copy over to the new thumbnail. Just a suggestion
July 19th, 2009 at 12:42 am
Jac,
This is awesome, I have been looking into this very thing for a Photo Gallery I have been working on. Until now I have loaded both the Images and the Thumbnails but this will make it a lot easier to update and maintain. Thanks,
Alan
July 20th, 2009 at 7:20 pm
Does setting the stage quality from good to best make much difference in the times?
July 21st, 2009 at 11:01 pm
Tyler: I tried reusing the bitmap, but unexpected and random articles would make it into the final image when doing that. Good idea though.
Bjorn: The stage quality I believe only affects rendering to screen and not the pixel data of the images. I wouldn’t think changing the screen quality makes any difference.
July 27th, 2009 at 5:43 am
Very neat thingy you wrote. Thanks for sharing. I think one thing should be added. Bitmap-Function just works with your own images (same server). Caching images from third party isn’t working.
If well, please tell me how :) I ran into that problem once and couldn’t solve it.
July 27th, 2009 at 10:13 am
Max: Try loading your images with URLLoader first, then take the byteArray you get from that and load it into Loader using Loader.loadBytes(byteArray);
While this isn’t very secure for your application, if you know you are accessing safe content this should work out. The security issue comes because instead of an image, you may be loading a SWF with code that does bad things. But if you own both servers then you should be fine.
August 11th, 2009 at 11:16 am
Thanks to You for sharing this awesome idea. I tryed to get more perfect result, and if you use some sharpening _before_ the last resizing (i did not know yet, that it will required every pass, or enough before the last), then it will be more clear result, but not too sharp, like sample 6.
August 27th, 2009 at 10:37 pm
Wow! good idea!! Thanks!! :D
September 23rd, 2009 at 9:04 pm
I had be struggling to get decent thumbs without sacrificing huge amounts of time. You just saved me a bunch of work. Thank You.
November 28th, 2009 at 9:03 am
[...] I’m using my ImageUtils class which ensures it looks good when resized (more about that in another post). Then I turn the BitmapData object into a PNG with AS3CoreLib’s PNGEncoder. I now have the [...]
December 14th, 2009 at 11:11 am
Hello Jacob,
First of all please excuse my bad English and thank you for sharing this amazing idea and code :)
I have used your code to build a thumbnail gallery and everything works and looks perfect. Unfortunately I met one problem when decided to scale down (80% for example) these thumbs. I mean to scale down a movieclips that contain resized ( with resizeImage() ) JPGs. I did this because I wanted to make a simple RollOver effect for each thumb. On RollOver I have tween of the given movieclip _xscale=_yscale=100 and on RollOut I have tween _xscale=_yscale=80 … (just for the record I am using TweenMax of greensock.com )
So when I scale down these movieclips I have the problem with antiliasing although all used .draw() methods have parameter smooth=true … unfortunately movieclip’s parameter forceSmoothing=true is working only with externally loaded (with loadMovie()) images but not with images created with BitmapData and attachBitmap …
I am very curious if there is some solution of that problem and will be very thankful to hear some opinions/ideas
Thanks in advance and once again big congrats for you work :)
December 14th, 2009 at 11:41 am
Deniss, when you are using the code I provided above and BitmapData.draw() you have complete control over how you want it resized. (e.g. You can do several iterations to bring it smaller or whatever, like this post shows).
However, when using Flash’s scaling, 3D, rotation, etc. Flash has control over how it gets drawn. You have no control over it. So when you adjust the scale of the MovieClip Flash is doing the drawing.
If you want the best quality, you’ll need to redraw this manually. A simple option would be to have the thumb start out at 80% of the size and the _xscale/_yscale at 100. Then on rollover replace the thumb with the 100% size and set the scale to 80, and let it tween to 100. When you roll out, tween the scale back to 80 and once it is done, replace the thumb with an 80% sized thumb and set the scale to 100.
This way you are only using the default Flash rendering while it is tweening, but you use the bitmap rendering when it is at rest.
I hope that makes sense. Good luck!
December 14th, 2009 at 12:12 pm
Wow this is so simple and sounds so logical … I can’t believe how fast you answer me and even gave me so good idea …
Thanks I wish you everything best :)
January 19th, 2010 at 6:25 pm
Jacob,
I stumbled upon your class today and found it quite a nice little utility. I ran into one major issue that I wanted to pass on, in terms of memory leaks with giant .jpg files.
Within ImageUtils.as if you add after the following lines the following code, your memory usage will go from 100′s of Megabytes to 100′s of Kilobytes:
After Line 146: source.dispose();
After Line 160: bitmapData.dispose();
Even though you are reassigning your temp value to bitmapData and source, the bytes allocated for the initial giant images are still kept in memory.
Doing dispose() before a reassignment ensures that all byte data is destroyed.
I can send you an updated file if you like.
Best,
-Scott
January 19th, 2010 at 8:44 pm
Thanks Scott! I’ve updated it in the repo. Good catch. I’m ashamed I didn’t think about that before. BitmapData 101. Or maybe 201.
January 27th, 2010 at 5:45 pm
Hi Jacob,
Great class! :)
I was trying to see the images but they are too small. Can you upload a large version so we can do comparisons? THX
January 28th, 2010 at 10:29 am
They are small because the were resized using the class. I didn’t make them smaller to fit in my blog post. The original size was 1000×750. I was using a HTMLLoader in AIR, loaded up my website at the time, and made thumbnails of the page using the different mechanisms down to 100×75.
So my class in the end resizes this particular size from 1000×750 to 800×600 first (the odd size first and halves after that), then 400×300, then 200×150, then 100×75. This was the best result of all the methods.
February 19th, 2010 at 12:04 pm
Keep in mind that if the global stage.quality is set to BEST, the quality of your down scaled thumbnails equates to what the player will give you in this context.
Your approach is still of value of course, because a stage.quality of BEST has *many* performance implications, certainly valuable to be able to opt-in to HQ as needed.
March 9th, 2010 at 5:24 am
Hi Jacob, i’m trying to use it on Flash CS4 but I’m having some errors: 1120: Access of undefined property ResizeStyle.
March 10th, 2010 at 11:42 am
The utils class has a dependency on the ResizeStyle for constants. It’s all in the image package: http://code.google.com/p/jacwright/source/browse/trunk/flash/jac/src/jac/image/
Be sure to include ImageUtils and ResizeStyle in your classpath.
March 20th, 2010 at 6:53 pm
Thank you so much for this. I was shocked how bad the quality was by default and now with this I’m resizing 10mb 18 megapixel files to 800px and the quality and speed is just about perfect for my needs.
April 8th, 2010 at 7:33 pm
Wow – what a difference. And it was so easy to plug into my code. Thank you so much!!!!
May 27th, 2010 at 4:09 am
hey,
thanks for sharing that, the result is realy better ! I’ve been looking for a clue about simple resizing method for a while…
July 28th, 2010 at 10:29 pm
Thanks from me too.
August 19th, 2010 at 8:27 am
Jac, listen to your really cool twin brother!
August 19th, 2010 at 11:29 am
I wish I HAD a really cool twin brother. All I have is Tyler for a twin. :D
August 25th, 2010 at 6:33 pm
If you try and load and scale an image from a nonsecure website (no crossdomail.xml), the code throws a secure error. Any suggestions other than add crossdomain.xml
August 26th, 2010 at 7:36 am
So owesome jac ! nice job, it saved for me a lot of time searching for a way creating thumbnails.
Thanks again :)
August 26th, 2010 at 8:40 am
Sorry Mark, the only way to get the bitmap data from images in other domains is a crossdomain.xml file. The only alternative for displaying a thumb is to simply set the width and height of the bitmap, but you can’t save the thumb in that case, only display it.
October 28th, 2010 at 1:30 pm
Hi Jacob, I’m having problem when image is bigger than 4096px, apparently that’s a problem on the draw method. Is there a way to fix that? Tanks a lot!
Cheers,
Cleverson
November 15th, 2010 at 1:05 pm
hi Jacob,
I embedded your code in a modified version of mx:Image class, however I noticed that when the image is scaled and then rotated then it looks a little bit blurry, whereas when rotation = 0 it looks just great.
Are you aware of this?
Is it because in this why I’m relying on flex to do the rotation and using your code for scaling instead?
Do you have a solution for this?
Do you think that sharpening the image (at the last iteration maybe) will do any better?
November 15th, 2010 at 2:41 pm
Hey Saverio, there might be a couple problems.
One, be sure the width/height you’re passing to the resize method are the unrotated width/height. For example, if I had a bitmap that was 1000×1000 and I rotate it by 45 deg. the width now reports 1414.2 instead of 1000. If you size it down to 141.4 (10%) you’ll want to pass the resize width/height as 100×100, not 141.4×141.4. I hope that makes sense.
Two, it might just be Flash’s redrawing for rotations. I’ve optimized Flash’s redraw for resizing, but haven’t looked at rotation and how that affects it. So I don’t know what would help in this case.
I hope the problem is #1 and you can fix it easily. The other would need more research and testing to figure out how to get it to work best. Let us know what you figure out.
November 15th, 2010 at 3:18 pm
Cleverson, there is a bitmap limitation in Flash. In flash player >= 10 the maximum with/height is 8,191 with maximum total pixels of 16,777,215. In player 9 the max size is 2,880 for width or height. I’m assuming you’re bumping into these limitations.
See: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html
November 16th, 2010 at 4:06 pm
hi Jacob,
thank you very much for your prompt reply.
Out of the 2 problems you suggested, #1 is not the case, although it would have been nice if it was that one!
yes, I also think it has to do with flash taking care of the rotation…
In case you’d like to see an example of this problem I have put live a test app here:
http://ec2-174-129-102-91.compute-1.amazonaws.com/misc/test/prov.html
as you can see, the two rotated images look a little blurry if compared to the original unrotated one… I have enabled “view source” so if you’d like you can see the Picture class that I have created, this class extends mx:Image and adds a
“scale” method based on your fabulous ‘resizeImage’ method.
I’m doing several attempts to find a solution to get rid of that ugly blur, I think that if we solved this issue your method can be used anywhere to create high quality scaled images even when they are rotated
November 16th, 2010 at 4:35 pm
Saverio,
It IS flash’s rendering. I took a moment to check out your test as well as put my own together.
I have an idea how to fix it, but I don’t have the time to do it right now and it would require a little hackery or deception. What you’d want to do is pass in the rotation to the resizeImage method and rotate the image first, then scale it down rotated. You should get a nice looking image, only, it’s not rotate anymore. You could then override the rotation method to report what your rotation is, even though the actual display object is not rotated. Does that make sense? The pixels would contain the image rotated, but the Bitmap object would be unrotated. It’s width and height would be adjusted to fit the rotated bitmap inside it.
If you do it and want to contribute your code back let me know and I’ll be sure to add it here and to my ImageUtils class. Otherwise I can do it when I get the time.
November 17th, 2010 at 2:53 am
hi Jacob,
I understand what you said and it’s a cool idea, however before doing it let’s consider this: I think that the ideal solution would be to find a fix that works regardless of the way that the image is used in a application. What I mean is that, the blurry problem I showed you occurs also when the actual image object is not rotated, but the image is a child of a component (e.g. a canvas) that as a non-zero rotation.
If you try to put an instance of the Picture class in my post inside a canvas and then rotate the canvas but NOT the picture then you will see that it’s still looking blurry.
So I think we cannot really rely only on the
‘rotation’ attribute of the Image class and just override that one… Maybe for this problem, given that we have to fight against flash player and prevent it from doing things, there cannot be a solution that works in all cases and just involves a modification of the Image class.. what do you think ?
November 17th, 2010 at 7:00 am
That one’s easy. I’m already doing it in some projects minus the rotation part. The only downside is it isn’t automatic because you don’t get events when one of your parents change scale/rotation.
Introducing concatenatedMatrix. You can get the absolute scale and rotation if you know how to read matrices with bitmap.transform.concatenatedMatrix which is a rollup of all the matrices from root to bitmap.
November 19th, 2010 at 7:21 am
Greetings from Russia and thanks for this great post! Thumbs are indeed getting better with your code!
November 19th, 2010 at 9:05 am
hi Jacob,
I’m not sure I fully understand the solution you proposed, however you seem to be sure so it might be worth try to implement it anyway.
I thought that if you set a bitmap from a rotated and then scaled bitmapdata, the bitmap would be rotated too…
Also, the problem about the container being rotated is that, as you also confirmed, the picture object in it will not get notified and so I would not know when to apply this fix..
February 3rd, 2011 at 9:42 am
Hi!
Thanks for your work. Could you please explain better how to use your class (i’m pretty new to as3)?
Thanks
April 7th, 2011 at 3:14 am
Thanks! I am just about to try this with some PaperVision materials. Will let you know if I have success! :-)
April 7th, 2011 at 4:55 am
Great stuff thanks!!!!! :-)
August 19th, 2011 at 9:14 am
Hi,
thanks for that great tool… free´d 30 mb of memory for my application Great… i will use it in some more place hope to get even better performence.
cheers
hendrik
August 28th, 2011 at 5:58 pm
Hi
you are number one
thanks
September 6th, 2011 at 9:00 pm
Hi!, your solution works fine!
Thanks!
November 1st, 2011 at 7:14 pm
Hey, sorry to not be more specific….but using this on Android and it is still pixelated. Strangely, setting the scaleX for the image allows me to have a scaled smooth image, which of course, isn’t nearly as efficient.
November 29th, 2011 at 2:53 am
[...] und durch 8). Der hat einige Performancetests gemacht und seine #3 schneidet mit 8ms am Besten ab. High quality, high performance thumbnails in Flash | Jacob Wright Absolutely awesome! Haut rein, bis denn __________________ Erst wenn der letzte Tween gespielt, [...]
November 29th, 2011 at 2:54 am
Works very good and efficient. Special thanXs from Germany