Update #2 : I fixed another bug where non-square images would not be rendered properly because the height and width were being processed backwards. I fixed the source code in the article, as well as in the ZIP file.
Update #1 : I realized that the ActionScript GetPixel() function doesn’t always return a 6 characters long string, which will cause the pixel data sent to the ASPX page to be corrupted.
Introduction
In this article, I’m going to demonstrate how to export the content of a movie clip from Flash to a JPEG file.
Flash is a great tool that I used for years now. I recently worked on a project involving Flash, and got asked if it was possible to save the content displayed inside Flash to a file. Yes, it’s possible, and actually not very difficult to achieve.
The key element here is to convert the vector graphics to a bitmap, then to get color information for every pixel of that bitmap, and send them to the server where a small program will re-compose the image.
The source code for this article is available for download.
Let’s get started.
Step 1: Create a Flash movie
I created an empty Flash movie (ExportBitmapData.fla), and added some code to its first and unique frame. The code does 3 things:
- Generate a movieclip and draw some shape in it.
- Create a BitmapData object and copy the content of the movieclip into it.
- Send the pixel values to the server by clicking on a button
Please note that you need to manually add a Button component to the library, or it won’t be instanciated.
The first part of the code is about creating variables and calling the functions when the Flash loads:
import flash.display.BitmapData;
var container:MovieClip = null;
var fx:MovieClip = null;
var bmp:BitmapData = null;
var btnExport:mx.controls.Button = null;
function movie_load():Void
{
// Create a container movieclip
container = this.createEmptyMovieClip("container", this.getNextHighestDepth());
// Create a BitmapData object that will hold the copy of the container movieclip
bmp = new BitmapData(200, 200);
fx = this.createEmptyMovieClip("fx", this.getNextHighestDepth());
DrawMovieClip();
DrawButton();
}
Then there is the method that draws some image as a regular vector graphic, and then creates a copy in the BitmapData object.
function DrawMovieClip():Void
{
// Draw two overlapping square to the "container" movieclip
container.clear();
container.beginFill(0x000000, 50);
container.moveTo(0,0);
container.lineTo(200,0);
container.lineTo(200,200);
container.lineTo(0,200);
container.endFill();
container.beginFill(0x800000, 50);
container.moveTo(50,50);
container.lineTo(150,50);
container.lineTo(150,150);
container.lineTo(50,150);
container.endFill();
fx.clear();
// copy the movieclip into the BitmapData
bmp.draw(container);
fx.attachBitmap(bmp, fx.getNextHighestDepth());
fx._x = 250;
}
Add a button that will trigger exporting
function DrawButton():Void
{
btnExport = this.createClassObject(mx.controls.Button, "btnExport", this.getNextHighestDepth(), {label:"Export"});
btnExport.move(10, 210);
btnExport.addEventListener("click", ExportBitmap);
}
Create the exporting function, the one that is called when the button is clicked. It works by iterating through each pixel of the BitmapData object, and building a string that contains all the colors value. In this case, the string looks like “8080808080808080808…”. That string is transmitted to the server, and will be processed by C#.
function ExportBitmap(evt:Object):Void
{
var output:String = "";
var col = "";
for(var i:Number=0;i<bmp.height;i++)
{
for(var j:Number=0;j<bmp.width;j++)
{
col = bmp.getPixel(j,i).toString(16);
// In some cases, the color will be truncated (e.g. "00FF00" becomes "FF00")
// so we are adding the missing zeros.
while(col.length<6)
{
col = "0" + col;
}
output+=col;
}
}
// Use a LoadVars to transmit the data to the ASPX page using POST
var lv:LoadVars = new LoadVars();
lv.pixels = output;
lv.height = 200;
lv.width = 200;
lv.send("GetPixelData.aspx", "_blank", "POST");
}[/sourcecode]
Then just add a call to the main function
[sourcecode language='jscript']
movie_load();
stop();[/sourcecode]
If I run the flash movie, here is what I get:<img src="https://saezndaree.files.wordpress.com/2007/10/bitmapdatatransform.gif" alt="Vector movieclip and bitmap copy side by side" />The vector movieclip is on the left, and its bitmap copy is on the right.We now have a way for flash to transmit the image to the server. Let's look at how these pixel information should be processed on the server-side.
<h2>Part 2: Save the image with ASP.NET</h2>
I created a new Web Project in Visual Studio 2005, and copied the flash files to the project's directory. The project will contain two web pages, the first one (default.aspx) will contain the flash, while the second (GetPixelData.aspx), will receive the pixel data and gather them to form the image.
The default.aspx page is very simple. It only contains the code necessary to display the flash object:
<body>
<form id="form1" runat="server">
<div>
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="ExportBitmapData" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="ExportBitmapData.swf" /><param name="quality" value="high" /><param name="bgcolor" value="#ffffff" /><embed src="ExportBitmapData.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="ExportBitmapData" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
</div>
</form>
</body>
The HTML code for GetPixelData.aspx is even simpler:
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblPixelData" runat="server" />
</div>
</form>
</body>
The really intersting part is in its code-behind file, GetPixelData.aspx.cs:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
public partial class GetPixelData : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Retrieve the pixel data from Flash
string pixelData = Request[“pixels”];
// If there is no data, simply display a message
if (pixelData == null)
{
lblPixelData.Text = “No data available”;
return;
}
//The width and height of the image have been transmitted as well
int width = int.Parse(Request[“width”]);
int height = int.Parse(Request[“height”]);
Bitmap bmp = null;
string curPixelColor = String.Empty;
int x = 0;
int y = 0;
if (pixelData != String.Empty)
{
// Here we create a Bitmap object that has the same size as the one in Flash
bmp = new Bitmap(width, height);
// Iterate through each group of six characters and convert them to a Color
// then assign that color to the pixel of the Bitmap object
for (int i = 0; i < pixelData.Length / 6; i++)
{
curPixelColor = pixelData.Substring(i * 6, 6);
bmp.SetPixel(x,y, ColorTranslator.FromHtml("0x" + curPixelColor));
if(x==width-1)
{
x = 0;
y++;
}
else
{
x++;
}
}
}
// Set the content type as jpeg
Response.ContentType="image/jpeg";
// Save the Bitmap object to the ouput stream, and flush the stream
bmp.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
Response.End();
}
}[/sourcecode]
Here is a little summary of what is going on:
- When the user clicks the “export” button, the pixels from the BitmapData are sent to the GetPixelData.aspx page (which opens in a new window) as a long string. Each color value is a sequence of six characters, representing an hexdecimal value.
- GetPixelData.aspx reads the string from POST.
- The program loops through the content of the string, extracting the characters by groups of six.
- At every iteration, the horizontal pixel position is incremented by one, so we’re moving from left to right, then every time we reach the end of a line, the vertical position is incremented as well.
- The 6 characters group is converted to a color using the ColorTranslator.FromHtml() method, and its value is assigned to the pixel.
- We set the content of the page as being a JPEG image and flush the content to send it back to the client.
The result looks like this, the new image has been created and is displayed in the browser:
Conclusion
This is a very primitive example of what can be done to send an image from Flash to the server. We could as well save the content to the disk. The major drawback of saving images in such way is that the server load is pretty big. That technique is therefore not suitable for big images.
The end.