AS3, Flash,

In part 2 we had managed to upload an image from the local file system, resized it and placed it on the screen inside a holder, about 30% of the main functionality. Next we need to create a JPG of the holder image.

Part 3 has arrived. Yey. Let’s just start with what actually happened when I got to this stage in the project; it didn’t work, to put the matter simply, my 30ish minute slot turned into a 120 minute slot, and that’s exactly why we have the fuckup factor, it’s just impossible to know when these situations will occur. The good news is that we factored for this to happen. The problem was that I didn’t type cast an array into a string before passing it to the PHP. The problem is identifying why these things happen, in this case trace and reporting fields before and after the PHP call identified the problem.

The next stage is of course the one where we create a bitmap object of an image and then create a new jpeg based on this image on the server using PHP and the GD library. I mentioned earlier about not integrating the elements in an application too early and so, while we are working on the same fla (incremented naturally) we are now using a seperate layer to do the next phase.

Note: The requirements for this project were to create a server version of the new image. This is quite a lengthy process due to the hoops we have to run through as well as moving the data from the flash to the PHP. As an alternative I also made a version of the tool which uses the JpegEncoder class and which is instant – can be viewed and tested here

As a set up I have created a copy of the holder movieclip structure, which inside has a mask on top for the correct dimensions and then another layer for some vector graphics (watermark) and at the bottom of the pile is a backgound vector and above that is the holder with our bitmap in it. For the test case I just uploaded a small graphic into this. The size of the jpg I will be creating for the test case is quite small, 50×50, and thats because I want this to run fast during the development process. And we also need a button to start the process, called cv. The clip called pt is the holder containing the mask, the vector layer, the backgound and the bitmap holder. [NB I replaced the test code with actual here]

var snap:BitmapData=new BitmapData(450,350);
var aa:Number=0; //used as the chunking variable

cv.addEventListener(MouseEvent.CLICK, capture);

function capture(me:MouseEvent) {
snap.draw(pt);
aa=0;
addEventListener(Event.ENTER_FRAME,oefcp);
}

The next code when run will loop through the (x,y) pixel coordinates of the clip (the top one) and will record the pixel ARGB values for the clip and store them in an array of hexadecimal values e.g. 0xffe456. So for a 50×50 image this is 2500 operations, which is not that many. However, the final image is going to be about 450×350 = 157500 operations. I dont want to just overload the comiler so I am going to chunk the operations into blocks of 10X50 (this will be 10×350 in the final version) and I am going to use an EnterFrame operation to do this.

var pixels:Array = new Array();
var tmp:String;
var w:Number=450;
var h:Number=350;

function oefcp(ee:Event) {

for (var a:int = aa; a<(aa+10); a++) { for (var b = 0; b<=h; b++) { var alpha:String = (snap.getPixel32(a, b) >> 24 & 0xFF).toString(16);
var red:String = (snap.getPixel32(a, b) >> 16 & 0xFF).toString(16);
var green:String = (snap.getPixel32(a, b) >> 8 & 0xFF).toString(16);
var blue:String = (snap.getPixel32(a, b) & 0xFF).toString(16);
if (alpha.length==1) {
alpha="0"+alpha;
}
if (red.length==1) {
red="0"+red;
}
if (green.length==1) {
green="0"+green;
}
if (blue.length==1) {
blue="0"+blue;
}
tmp="0x"+red+green+blue;
pixels.push(tmp);
}
}
statust.text="loop "+aa+" "+w;

aa+=10;
if (a>w) {
statust.text="pl "+pixels.length;
removeEventListener(Event.ENTER_FRAME,oefcp);
aa=0;
sendUSERrq();
}
}

The PHP function requires the data to be delivered from top left image pixel then all the way to the bottom, next is top left + 1 then all the way down again. So the loop I have is starting with a base value of 0, base + loop 0-9, then loop 0 to height, do a getPixel32, grab the RGB values (important sep here as this can cause problems, if its a single value hex that comes back, say 0xa then we need to add a leading zero) and then we are going to make up a hex value string in the format the PHP is expecting and add it to an array. After the process is finished I increment my base by 10 and check against the expected width to see if it’s finished. NB this function is NOT optimised, in fact its the opposite thereof, by about a factor of 10. Usually this would be changed later but it works well as an illustration.

The next function is a good illustration of how to access a PHP file with arguments and then call the return function on return. Re optimisation its bad, for a 120K data set I am in reality sending 10x that amount.

function sendUSERrq() {

var phpTG:String="show.php";

var requestO:URLRequest=new URLRequest(phpTG);
requestO.method=URLRequestMethod.POST;

var loaderO:URLLoader=new URLLoader(requestO);
loaderO.addEventListener(IOErrorEvent.IO_ERROR, serverDirectoryIOError);
var variables:URLVariables = new URLVariables();

variables.nname = String(new Date().getTime());
variables.img=String(pixels);
variables.width=w;
variables.height=h;

requestO.data=variables;

loaderO.addEventListener(Event.COMPLETE,afterCreateJPG);
loaderO.load(requestO);
}

function serverDirectoryIOError(ee:Event) {
// in case we can't find the php file
}

Above we are then set up to pass the full array along with the width and height to the PHP and use GD to make an image from it. I also passed up the new filename, which was just the current time in ms (if this were a generic tool I would add in a username coefficient to the filename, but in this case current time is sufficient. The following PHP file handles the creation of the JPG file

Finally the PHP comes back and we check the checksum return values to make sure its all in place. Where did my mistake from earlier come in? I forgot to typecast the array being sent to the php as a string value. That’s all, and the whole thing went tits up. The trace statement is your friend here, and also some dummy textfields on the screen. Unfortunately you can never really be sure what is happening in the php, but you can set the return to tell you if variables were calculated correctly.

function afterCreateJPG(event:Event):void {

var vars:URLVariables=new URLVariables(event.target.data);

var UOLv:int=vars["fin"];
var hh:int=vars["hh"];
var ww:int=vars["ww"];
statust.text="finished "+UOLv+" "+ww+" "+hh;
}

NB: An interesting thing here is we can do our own bitmap filtering of each pixel if we do so require, its completely up to you, orphasing images together. Because the image we are looking at is a composite we can add a vector watermark to all images, or text or anything really – just play away

Once I had this working for the small version, all I needed to do was to point the bitmapdata at the other widget created earlier to load an image from the fileserver and then change the width and height settings and it was all a gogo – the test now lets me load in a graphic and then another button analyses the composite and resized image and creates a brand new jpeg on the server with the PHP, pretty cool if I say so myself.

As a test I looked on my drive and found a 5.46 mb image which was 3648px by 2736px. I hit the load button and brought it to the browser with no timelag (remember its NOT uploading!) and resized to 450×350. I then hit the create button and the process from start to finish took 30 seconds and the image was 96kb for a full complex image.

Just to upload the original in another test, took me just under 3 minutes alone. Also I created the image to 100% quality with the PHP, a quick test with photoshop and it couldnt reduce that number.

So where are we now? About 4.5 hours in, 2.5 hours left! The good news is as we have been going on we have been unit testing and also we have integrated the two main development objectives. What’s left to do ? On the Flash development side we still have image manipulation, notification or save to local, and a restart option – thats about an hour budgeted. The rest is doing design on the UI, writing some copy, system test. All together we might just make it as long as there aren’t any new catastrophic failures. (which there probably will be)