08.24
There are many cool server-side image manipulation libraries out there including the very popular GD for PHP. The problem is that your server has to have it installed for you to be able to utilize it and if your server does not ( and you don’t admin your own), it’s often difficult or even impossible for you to install it yourself. Another pitfall of using the server-side approach for image processing is that you still have to upload the image before it can be processed, and with cameras these days, end users will most likely upload some pretty large images.
Ideally we would process the image locally before sending it to the server. I did some hefty research and found a lot of cool things we can do in the flash player, but no solid step by step to doing exactly what I wanted to achieve. My goal was to do the following.
1. Allow user to browse for image on their local system
2. Convert file reference into BitmapData
3. Scale BitmapData to fit inside a MAX resolution
4. Compress and encode the BitmapData into a ByteArray
5. Send ByteArray to server-side script to create and save the image file
After finding several articles covering pieces of these steps, I was able to stitch together the code to achieve the above goal. To use my code examples, you will need to use Flex 3 targeted for Flash Player 10 and Zend AMF to send the image data to your server.
<mx:Application xmlns:mx=“http://www.adobe.com/2006/mxml” layout=“absolute”>
<mx:Script>
<![CDATA[
import mx.graphics.codec.JPEGEncoder;
private var _fileRef:FileReference;
private var _netConnection:NetConnection;
private var _imageByteArray:ByteArray;
private static const MAX_SIZE:Number = 300;
private function getFile():void{
_fileRef = new FileReference();
_fileRef.addEventListener(Event.SELECT,onFileSelect);
_fileRef.browse();
}
private function onFileSelect(e:Event):void{
_fileRef.addEventListener(Event.COMPLETE,onFileLoad);
_fileRef.load();
}
private function onFileLoad(e:Event):void{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.loadBytes (_fileRef.data);
}
private function onImageLoaded(e:Event):void{
var loadedBitmap:Bitmap = Bitmap(e.target.content);
var scale:Number = getScale(loadedBitmap);
var bmpData:BitmapData = new BitmapData(loadedBitmap.width * scale, loadedBitmap.height * scale,false);
var matrix:Matrix = new Matrix();
matrix.scale(scale,scale);
bmpData.draw(loadedBitmap,matrix)
var jpg:JPEGEncoder = new JPEGEncoder(60);
_imageByteArray = jpg.encode(bmpData);
uploadImage();
}
private function getScale(img:Bitmap):Number{
var scale:Number;
if(img.width > MAX_SIZE){
scale = MAX_SIZE / img.width;
}
else if(img.height > MAX_SIZE){
scale = MAX_SIZE / img.height;
}
else{
scale = 1;
}
return scale;
}
private function uploadImage():void{
_netConnection = new NetConnection();
_netConnection.connect(‘http://localhost:8888/_PHP/classes/’);
var res:Responder = new Responder(onDataResult, onDataError);
_netConnection.call(“KingHippo.saveImage”,res,_imageByteArray);
}
public function onDataResult(obj:Object):void{
trace(obj);/////FILE SAVED
}
public function onDataError(obj:Object):void{
trace(‘error’);///////OOPS
}
]]>
</mx:Script>
<mx:Button label=“browse” click=“getFile()” />
</mx:Application>
That pretty much wraps up the Flex code. I’ll explain the code referring to our original goal list.
1. Allow user to browse for image
Here we have a simple Button that calls a function that will instantiate a file reference object and use its browse() method to prompt the user with their local file browser.
2. Convert file reference into BitmapData
Upon file select, we will need to get this file loaded in to grab its BitmapData. This is where Flash Player 10 comes in. We now have some cool FileReference additions to play with and can now load in the file selected. First we call the load() method. When COMPLETE, we have access to the data of that file which means we can use a Loader to load the bytes of the data using the loadBytes() method from our Loader.
3. Scale BitmapData to fit inside a MAX resolution
OK, so now we have our image data and its time to do some BitmapData trickery. First we need to set the incoming data to a Bitmap object. Next we determine how much we need to scale down the image by using our MAX_SIZE variable. Here I’m using 300 so the width or height will not exceed that number. Once I determine the scale value (using the getScale function) , I can now create and manipulate my Bitmap Data. I create a new BitmapData object and set it to the appropriate size using my scale value against the image’s original size. Now I need to draw from the loaded image. It’s not enough to just draw it b/c it will just grab that rect from the original image data, essentially cropping it. We need to use Matrix to set the scale of the original data within our draw() method.
Now we have our image data and appropriate resolution.
4. Compress and encode BitmapData into a ByteArray
Next, using Flex’s JPEGEncoder, we can compress the data and encode it to a ByteArray. Simply create a new JPEGEncoder object and pass a quality value to its constructor. The values are 1 – 100. Here I’m using 60. Then we call the JPEGEncoder’s encode() method which will then give us our long-awaited ByteArray. Now we can pass it on to the server.
5. Send ByteArray to server-side script to create image file
Lastly, we need to send off the image data to the server. Here I’m using Zend AMF and sending the ByteArray to a PHP class.
Now we are done on the Flex end. The last thing we need to do is create the image file from the data sent to the server and save it.
PHP code below..
class KingHippo{
public function __construct(){
//////construct
}
public function saveImage($obj){
$fp = fopen(‘myNewImage.jpg’, ‘wb’);
fwrite( $fp, implode(”,$obj));
fclose( $fp );
return “FILE SAVED”;
}
}
<?
That’s pretty much it. You’ll most likely want to add to it by sending a unique path and file name for the saved image.
Great! this is exactly what i wanted to do, thankyou!!!
One note though, your scale calculation is flawed, in some cases it will not scale along the correct edge, the correct formula is:
if (img.width / img.height > MAX_SIZE / MAX_SIZE) return MAX_SIZE / img.width;
else return MAX_SIZE / img.height;
Thanks again!
Two more things…
the BitmapData width and height should not be the original images width and height… it should be the scaled width and height, and if the new width or height are < 1 then need to be rounded up to 1, thus the below max function…
w = Math.max(1, loadedBitmap.width * scale);
h = Math.max(1, loadedBitmap.height * scale);
bmpData = new BitmapData(w, h, false);
hehe, ya, I definitely slacked on the scale function here. When I had to use this for my actual project I made a better scale function. I’ve been meaning to update the code here.
Thanks for the comments.
fwrite( $fp, implode(”,$obj));
That line is giving me an error as it’s incorrectly parsing the quote. Is that a typo?
Thanks
try without the explode:
fwrite($fp,$obj);
Thanks. Only problem I have is that the JPEGEncoder.encode() function isn’t asynchronous, so it tends to take about 8-15 seconds on large images, and results in the Flash Player throwing a timeout.
Hello! Please e-mail me your contacts. I have a question zachary@complective.ru” rel=”nofollow”>……
Thanks!…
Buy:Prozac.Lasix.Lipothin.Seroquel.Acomplia.Benicar.Nymphomax.Wellbutrin SR.Zetia.Buspar.Advair.Zocor.SleepWell.Lipitor.Female Cialis.Cozaar.Female Pink Viagra.Ventolin.Amoxicillin.Aricept….
LCD http://jlcdmlvttrq.bestpartsstore.info/tag/AC+Adapter+Supply+LCD/ : AC…
LCD…