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 = 100x75It worked like this:
1000x750 * .8 resize * .5 resize * .5 resize * .5 = 100x75Doing 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)
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;Enjoy!
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;
}
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.
0 comments:
Post a Comment