Blog

Oct
13
Transform BitmapData into JPEGs (AS3, JPGEncoder, Rails, Rmagick)
by Cary Dunn | Tutorial

Do you ever have nightmares where you are back in the Actionscript 2 days and trying to save BitmapData to a JPEG? … ok, so maybe its just me, but who cares because it’s easy now!

Old news…

ByteArray rocks! If you aren’t familiar, it was introduced in AS3 for playing with binary data and has endless uses. Compression being the most obvious, but speed, custom protocols, and streaming data are no less important. zlib and deflate support are built in as well.

Capturing BitmapData, encoding it as a JPG, and saving it through Rails…

Step 1. We need to download the as3corelib. The library we will be using from this package is com.adobe.images.JPGEncoder (a port of this C algorithm), but look around! There are some awesome libraries in here.

Some imports…

import flash.display.MovieClip;
import flash.display.Stage;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.JPGEncoder;
import flash.events.*;
import flash.net.*;

Capture BitmapData, encode as a JPG, convert to ByteArray, send to Rails

// Capture the BitmapData of the stage for example
var stage_snapshot:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
stage_snapshot.draw(stage);

// Setup the JPGEncoder, run the algorithm on the BitmapData, and retrieve the ByteArray
var encoded_jpg:JPGEncoder = new JPGEncoder(100);
var jpg_binary:ByteArray = encoded_jpg.encode(stage_snapshot);

var header:URLRequestHeader = new URLRequestHeader ("Content-type", "application/octet-stream");
var request:URLRequest = new URLRequest("/generate_jpg");
request.requestHeaders.push(header);
request.method = URLRequestMethod.POST;
request.data = jpg_binary;

var loader:URLLoader = new URLLoader();
loader.load(request);

Rails n’ RMagick…
// Example RMagick usage...
image = Magick::Image.from_blob(request.body.read).first
image.write("#{RAILS_ROOT}/public/jpgs_from_as/test.jpg")

Why it rocks…

Well, just think about what you would have to do without it?
Data transfer – 800×600 sized image…480000 pixels…*~4 (RGBA)…~2Megs? For a ~80kb compressed jpg? No dice.
Data processing – calculating this per pixel color array makes your machine sound like a lawnmower…
I suppose I’ll miss dealing with color palettes and encoding pixel data…NOT!

15 Comments
maximillion
November 4, 2008

GREAT TUTORIAL!! i wanted to say thank you for that. i have another problem. how can i specify where to store the encoded JPG without using rails

Cary Dunn
November 4, 2008

What language were you thinking of using to output the JPG?

You could make the request to a PHP script with somethink like the following…


$jpg = $GLOBALS["HTTP_RAW_POST_DATA"];
header('Content-Type: image/jpeg');
header("Content-Disposition: attachment; filename=".$_GET['filename']);
echo $jpg;

Where $_GET['filename'] comes from the get paramters of the request.

Does that help?

Brett Taylor
November 25, 2008

The PHP code that Cary Dunn provides above works fine, with some modification for saving it to a file.

< ?
/*
*** THIS CODE IS UNSAFE FOR PRODUCTION ***
This code does absolutely no error checking on the validity of the
expected jpg file coming through. It should be checking that the data
coming in is in fact a valid JPEG file of appropriate dimensions.
*/
$jpg = $GLOBALS["HTTP_RAW_POST_DATA"];
$filename = 'uploads/'.date('r').'.jpg';
file_put_contents($filename, $jpg);
echo "saved to ".$filename;
?>

GarretT
November 25, 2008

SORRY, I’M A BIT OF A NEWBIE WHEN IT COMES TO THIS STUFF. YOU WOULD NEED TO CHANGE THE URLREQUEST IN THE FLASH FILE TO RUN THIS PHP SCRIPT, WOULDN’T YOU? AND, IS USING A “LOADER” THE BEST METHOD? OR, WOULD YOU JUST USE “SENDTOURL?”

Aaron
February 24, 2009

This code is great!! I am experiencing a problem though. I am using it to capture a video object which is bound to a RTMP stream via netstream/netconnection. Every time I run the above code to capture an image it saves a file with the correct dimensions of the video object (240×180), but image itself is only 160×120 on top of a white canvas. Does this ring any bells?? Any idea whats going on??

March 15, 2009

Hi I would like to try and just write straight to file from running my flash in an app. I wont be running my classes in a swf in a browser, instead it will be just running on one machine in an art installation. I would like to do something similar to this, but I just need to write straight to a file in a folder, without any sort of prompt or server side scripting. Is this possible?

March 15, 2009

@Sarah : Reading and writing to local files was added in FP10, so if you are comfortable using 10 (>60% of machines at beginning of 2009) you can checkout the FileReference API.

http://www.mikechambers.com/blog/2008/08/20/reading-and-writing-local-files-in-flash-player-10/ is a good article starter article as well on the topic.

If you need any more help let me know and I can put together an example post.

Yau
March 30, 2009

Thanks man, Great Stuff, this is how i get it worked

image = Magick::Image.from_blob(request.body.read)

image[0].write(“#{RAILS_ROOT}/public/test.jpg”)

thomas
April 14, 2009

you don’t need RMagick for this to work:

File.open(“cockpit_test.jpg”, “wb”) { |f| f.write(request.body.read) }

will work fine

pushpa
June 11, 2009

hi

i am new to as3. thanku for the post. can u show an example in which i can send a bitmap as email.thanku once again

Pushpa

Samar
July 6, 2009

Thanks Cary dunn.

Ato12
August 3, 2009

Hi, thabks a lot. i have one question, instead of PHP is posible get the image in javascript? is it possible?

by the way, thanks again.

MickyMike
August 10, 2009

Hi there.

I’m exporting a JPG using AS3/PHP, but my server version is 4.x and it doesn’t work with the function “file_put_contents” :(

Somebody can’t any idea how to resolve it? (I can’t change of server provider by the momment)

Many thanks!

Micky Mike
August 10, 2009

( Somebody HAVE any idea…)
:)

vikram
June 28, 2010

Great article indeed…! I got a little problem while dealing it.
I wanted to capture an image and send it to a .aspx file.
Fine! I captured the video into a bitmap data object and encoded into a byte Array.
If I print the byte array it displays ÿØÿà. I dont know what is wrong with the code..

Here is What I have done so far.

import com.adobe.images.JPGEncoder;
//package used to encode the bitmapdata into jpg format..
/* The following lines of code adds camera and attaches camera to the video*/
var cam:Camera;
var vid:Video;
var bitmapData:BitmapData;
var bitmap:Bitmap;
startCam();
function startCam()
{
try
{

cam = Camera.getCamera();
vid = new Video(320,240);

cam.setQuality(0,80);
cam.setMode(320,240,24,true);
vid.attachCamera(cam);
vid.x = 0;
vid.y = 0;
addChildAt(vid,0);

//adds bitmap data and draws it into bitmadata

bitmapData = new BitmapData(vid.width,vid.height);
bitmap = new Bitmap(bitmapData);
bitmap.x = 0;
bitmap.y = 0;
var buttons:Buttons = new Buttons();
addChild(buttons);
buttons.x = 160;
buttons.y = 208;
errorMes.visible = false;
}
catch(e:Error)
{
errorMes.visible = true;
var retryBtn:Retry = new Retry();
retryBtn.x = 160;
retryBtn.y = 170;
addChild(retryBtn);
retryBtn.addEventListener(MouseEvent.CLICK,retryConnecting);

}
}
function retryConnecting(me:MouseEvent)
{
me.target.removeEventListener(MouseEvent.CLICK,retryConnecting);
me.target.parent.removeChild(me.target);
startCam();

}

function captureImage()
{
addChildAt(bitmap,1);

bitmapData.draw(vid);
}
function cancelImage()
{
removeChild(bitmap);
}

function saveImage()
{
var encoded_jpg:JPGEncoder = new JPGEncoder(100);
var binary_jpg:ByteArray = encoded_jpg.encode(bitmapData);
var string_jpg:String = binary_jpg.toString();
output.text = binary_jpg.toString();
var header:URLRequestHeader = new URLRequestHeader (“Content-type”, “application/octet-stream”);
var request:URLRequest = new URLRequest(“http://localhost/webcampicture/uploadpic.aspx”);
request.requestHeaders.push(header);
request.method = URLRequestMethod.POST;
request.data = string_jpg;

var loader:URLLoader = new URLLoader();
loader.load(request);
}