Flex Image Resize Produces Poor Quality Images
Posted by admin on March 9th, 2009
Flash/Flex/AIR produces a poor quality(pixelated) image when you load a larger image and try to re size it in the player to a much smaller size.
I searched the web and found this post http://www.cafesilencio.net/blog/bitmapdata-resize-quality-in-flex-and-air, tried the code but I didn’t see any improvements on the image quality.
It seams that Flash player doesn’t use a bilinear/bicubic interpolation algorithm to resize images, probably that’s why the produced image is so bad.
I came up with a solution, it seams that if you first apply a Blur effect on the image, then resize it, the produced image will appear much nicer. Of course the amount of Blur has to be calculated, otherwise we will end up with a poor quality image…again.
Check out the image before/after my solution, also you can try out the solution here
(don’t forget to check the “Apply Blur” check box to see my re size solution)
Here is the code for resize images using blur.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="applyEffects()"> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.core.UIComponent; [Bindable] private var blurQualityDP:ArrayCollection = new ArrayCollection([{label: 'Low', value: BitmapFilterQuality.LOW}, {label: 'Medium', value: BitmapFilterQuality.MEDIUM}, {label: 'High', value: BitmapFilterQuality.HIGH} ]); public static function getUIComponentBitmapData( target:UIComponent ):BitmapData { var bd:BitmapData = new BitmapData( target.width, target.height, true, 0 ); var m:Matrix = new Matrix(); bd.draw( target, m, null, null, null, true ); return bd; } private function applyEffects():void { var w:int = widthSlider.value; var ratio:Number = w / originalImage.width; var h:int = originalImage.height * ratio; if (applyBlur.selected){ var blurXValue:Number = Math.max(1, originalImage.width / w) * 1.25; var blurYValue:Number = Math.max(1, originalImage.height / h) * 1.25; var blurFilter:BlurFilter = new BlurFilter(blurXValue, blurYValue, int(blurQuality.selectedItem.value)); originalImage.filters = [blurFilter]; } else { originalImage.filters = []; } var bd:BitmapData = getUIComponentBitmapData(originalImage); var rbd:BitmapData = resizeImageBD(bd, w, h); img1.source = new Bitmap(rbd, PixelSnapping.AUTO, true); } public static function resizeImageBD( bitmapData:BitmapData, width:Number, height:Number ):BitmapData { var newBitmapData:BitmapData = new BitmapData( width, height, true, 0x000000 ); var matrix:Matrix = new Matrix(); matrix.identity(); matrix.createBox( width / bitmapData.width, height / bitmapData.height ); newBitmapData.draw( bitmapData, matrix, null, null, null, true ); return newBitmapData; } ]]> </mx:Script> <mx:VBox> <mx:HBox> <mx:Label text="Image Width" /> <mx:HSlider id="widthSlider" value="300" minimum="0" maximum="1600" width="800" change="applyEffects()" liveDragging="true" snapInterval="5" dataTipPrecision="0"/> <mx:CheckBox id="applyBlur" label="Apply Blur" change="applyEffects()"/> <mx:ComboBox id="blurQuality" dataProvider="{blurQualityDP}" change="applyEffects()"/> </mx:HBox> <mx:Label text="Image Width: {widthSlider.value}" /> <mx:Canvas> <mx:Image id="originalImage" source="@Embed(source='test7.jpg')" x="100" y="100"/> <mx:Image id="img1" /> </mx:Canvas> </mx:VBox> </mx:Application>
September 12th, 2009 at 6:02 pm
Hi Janos,
Thanks for the post, in my search to high-quality resizing with flex/air, it was very helpfull. But I also found this way to resample bitmap data.
envrac.org
Greetz,
September 21st, 2009 at 9:22 pm
Thanks for this source code! I was looking for a Flex component which would support bi cubic interpolation while scaling the original image. Based on your source code I managed to make a new component which supports this. Here’s the source code:
package custom_components
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.IBitmapDrawable;
import flash.display.LoaderInfo;
import flash.display.MovieClip;
import flash.display.PixelSnapping;
import flash.events.Event;
import flash.filters.*;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.controls.Image;
import mx.core.UIComponent;
import mx.core.mx_internal;
use namespace mx_internal;
public class SmoothImage extends Image {
public function SmoothImage():void {
super();
}
override mx_internal function contentLoaderInfo_completeEventHandler(evObj:Event):void {
var currTarget:LoaderInfo = evObj.currentTarget as LoaderInfo;
// create a copy of original bitmap
var originalImage:BitmapData = new BitmapData(currTarget.width, currTarget.height);
originalImage.draw(evObj.target.loader as IBitmapDrawable);
// blur values are arbitrary - for me 7 worked fine - you can experiment with these
var blurXValue:Number = 7;
var blurYValue:Number = 7;
// setting up the blur filter
var blurFilter:BlurFilter = new BlurFilter(blurXValue, blurYValue, int(BitmapFilterQuality.LOW));
// applying the blur filter to the copy of the original image
originalImage.applyFilter(originalImage,
new Rectangle(0, 0, originalImage.width, originalImage.height),
new Point(0, 0),
blurFilter);
// resizing the original image
var rbd:BitmapData = resizeImageBD(originalImage, this.width, this.height);
// assigining the resized version of the image to the Image component
this.source = new Bitmap(rbd, PixelSnapping.AUTO, true);
super.contentLoaderInfo_completeEventHandler(evObj);
}
public static function resizeImageBD( bitmapData:BitmapData, width:Number, height:Number ):BitmapData {
var newBitmapData:BitmapData = new BitmapData( width, height, true, 0x000000 );
var matrix:Matrix = new Matrix();
matrix.identity();
matrix.createBox( width / bitmapData.width, height / bitmapData.height );
newBitmapData.draw( bitmapData, matrix, null, null, null, true );
return newBitmapData;
}
}
}