Binary Processing in Flash - BMP Creator

Demonstrator currently not working as functionality points to ASP scripts under the fotios.cc domain that is not available any more.

 

How to do Binary Processing in Flash

Binary processing in Flash is possible. This is especially
true since bitwise operations are supported. Bitwise operations are not required
though and in fact the above example (Flash BMP Creator) does not use bitwise
ops at all. However, for certain binary formats that use bitpacking (such as
the Flash swf format), they can help a great deal (otherwise one would have
to emulate bitpacking with appropriate arithmetic operations on the byte values
and that would not be efficient at all).

Flash was not designed to do such processing though and therefore
there are important restrictions. These take the following forms:

  • Processing in Flash is done by interpreted ActionScript.
    This means that binary processing (and any processing) is potentially very
    slow. However, considering that you distribute processing by offloading it
    to the client instead of burdening the server with it, you could have a very
    large number of clients doing binary processing tasks with very little
    processing power required on the server side.
  • There is a limit on the number of actions the movie can
    perform in a single frame. If it is exceeded then a dialog comes up giving
    the user the opportunity to interrupt the processing. This means that only
    small binary files (like the 40x30 BMPs created by the movie above) can be
    processed within a Flash movie without considerable user irritation. One can
    of course split the processing in more than one frames but that adds to the
    complexity of the required code and once more shows that Flash is certainly
    not meant for such things. However, if you are determined to do large binary
    files processing in Flash this is the way out.
  • Web based flash players as well as latest flash projectors
    and stand-alone players do not directly support saving text or binary files.
    This means that even though you can do all processing on the client side (within
    Flash), you will still need a server component if you want to save the generated
    binary data.

Following is an explanation - by example - of how to do binary
processing in Flash. The example used is the Flash Bitmap Creator movie,
as seen in action just above. I put this lil flash movie together mainly for
demonstration purposes but I can see similar implementations being used mainly
in handheld platforms for communication purposes (sending lil drawings, creating
icons, etc.).

 

Creating a Binary File (BMP) in Flash

File Formats

In order to create a binary file of a certain type you first
need to know the binary specification of the file. Here is the ActionScript
function used within the Flash movie above in order to encode, in BMP format,
the color values of the one-color (red, green or blue) pixels painted in the
canvas area of the movie. Following the code (and inline comments) in this function
one can see how a BMP file is structured.

//////////////////////////////////////////////////////////////////////////////////////////////

//Pass width and height of the bmp, as well as the array containing the pixel color values

//of the image. Then this function will encode this data in an appropriate 24bit BMP envelope

//and push the byte values representing the resulting BMP file in the passed output array.

//The BMP is a simple uncompressed 24bit color Windows BMP (no color table indexing is used)

//

//Assumption: All bitmap scanlines are of equal size

function create24bitBMP(width, height, inarr, outarr)

{

  outarr.length = 0; //reset output array



  //Determine number of padding bytes for each scanline

  var padnum = Math.floor( (width * 3)  % 4 );



  var inarrsize = inarr.length;

  var datasize  = inarrsize + height * padnum; //pixel bytes plus padding bytes



  //Push the 'BM' identifier byte sequence

  //since this is a Windows BMP

  outarr.push(66); //'B'

  outarr.push(77); //'M'



  //filesize -> 4 bytes (full header size + pixel data)

  var filesize = datasize + 54;

  pushDWord(outarr, filesize, 0);



  //reserved -> 4 bytes - zero

  pushDWord(outarr, 0, 0);



  //bmp data offset -> 4 bytes - file start to start of bmp data (54)

  pushDWord(outarr, 54, 0);



  //bmp header size -> 4 bytes - / 28h - Windows 3.1x, 95, NT, … / 0Ch - OS/2 1.x / F0h - OS/2 2.x

  pushDWord(outarr, 40, 0);

  

  //width -> 4 bytes

  pushDWord(outarr, width, 0);



  //height -> 4 bytes

  pushDWord(outarr, height, 0);



  //planes -> 2 bytes (use just 1 plane)

  pushWord(outarr, 1, 0);



  //bits per pixel -> 2 bytes (24 for 24-bit bmp)

  pushWord(outarr, 24, 0);



  //compression spec -> 4 bytes (0 for no compression)

  pushDWord(outarr, 0, 0);



  //bmp data size -> 4 bytes (size of passed pixel array,                      

  pushDWord(outarr, datasize, 0);



  //HRes -> 4 bytes - pixels per meter (usually 3780)

  pushDWord(outarr, 3780, 0);



  //VRes -> 4 bytes - pixels per meter (usually 3780)

  pushDWord(outarr, 3780, 0);



  //Number of Colors in color table-> 4 bytes

  //All zero, since 24bit bmps do not use color table (palette)

  pushDWord(outarr, 0, 0);

  

  //Important Colors -> 4 bytes (all zero, since we use no palette)

  pushDWord(outarr, 0, 0);



  //Palette - Palette colors are expressed by RGBByteQuads

  //There is nothing here since we use no palette in 24bit BMPs



  //BMP pixel data. 3 bytes per pixel (RGB Byte Triad) since this is a 24bit BMP).

  //Pad end of each scanline to next 32 bit boundary if needed (scanlines must be DWORD aligned)

  //RGB triad bytes should be Lil-Endian aligned. This means that the B byte is stored

  //in the lower address and the R byte in the higher

  var i=0, j=0, k;



  while(i < inarrsize)

  {

    outarr.push(inarr[i++]); 



    j++; 



    //if we reach the end of a scanline

    if (j/3 == width)

    {             

      if (padnum)

        for(k=0;k<padnum;k++)

          outarr.push(0); //pad  



      j = 0;

    }



  }

}

All that this function does is push appropriate arithmetic
values (representing individual bytes) into an array. When the function terminates,
the array contains all the bytes (byte values) that represent the complete BMP
file. The function needs to be passed the width and height of the BMP as well
as an array containing the RGB values of each painted pixel. For instance a
green pixel is represented by three values in the array: 0x00,0xFF,0x00 or 0,255,0.
An interesting fact about uncompressed 24bit BMPs is that they store the pixel
scanlines bottom up. Another one is that they are lil-endian aligned on all
platforms (the latter is true for all BMPs).

 

Pushing Bytes

The create24bitBMP() function calls three other
functions: pushByte(), pushWord(), pushDWord().
These functions take a single arithmetic value, break it into bytes and then
push the byte values in the specified endian order. They can be used for general
binary processing in Flash.

When doing binary processing in Flash the used code needs
to be as efficient as possible, for slight inefficiencies, that would be virtually
insubstantial when using C or C++, can make a lot of difference in Flash processing
times. Here is the implementation of the pushWord() function.
You can find the implementation of the rest of the functions in the fla source
of the bitmap creator movie (as seen above) that you can download at the bottom
of this page.

//////////////////////////////////////////////////////////////////////////////////////////////

//This function will push a value between 0 and 65535 in the passed array

//as 2 consecutive byte values (representing a full word value) in endian order

function pushWord(arr, val, endian)

{

  var MSB, LSB;



  LSB = Math.floor(val % 256);

  MSB = Math.floor(val / 256); 



  //Order bytes in big

  //or lil endian order

  if (endian) 

  {

    arr.push(MSB);

    arr.push(LSB);

  }

  else

  {

    arr.push(LSB);

    arr.push(MSB);

  }

}

 

URI Encoding

After we have all the values that represent the individual
bytes of the BMP file we need to URI encode them into a long string and send
them to the server side via a loadVariables() Flash action. I
chose a full URI encoding scheme whereby all bytes (with no exceptions) are
encoded as %HH char sequences (where 'H' stands for a hexadecimal digit
in capital letters). I chose to use full URI encoding instead of normal URI
encoding because it simplifies the server side code considerably and I wanted
to keep the server side of this lil demo project (that focuses on client side
processing possibilities with Flash) as simple, less demanding and short as
possible. Here is the function that performs the URI encoding of the BMP byte
values:

//////////////////////////////////////////////////////////////////////////////////////////////

//This function will return a string that represents the values

//in the passed array (arr) in fully URI encoded form.

//Values are assumed to represent individual byte values (0-255)

function fullURIEncode(arr)

{

  var i; 

  var len = arr.length;

  var uri;

  var d1, d2;

  var val;



  var hexdigit = new Array("A","B","C","D","E","F");



  for (i = 0; i < Len; i++)

  {

    val = arr[i]; 



    d1 = Math.floor(val / 16);

    d2 = Math.floor(val % 16);



    if (d1 > 9)

      d1 = hexdigit[d1 - 10]



    if (d2 > 9)

      d2 = hexdigit[d2 - 10]



    uri += "%" + d1 + d2;

  }



  return uri;

}

If one chooses to encode the string using normal URI encoding
the length of the resulting string will be significantly reduced (depending
on the data being encoded) but the complexity of the server script will increase.
Also, caution will be needed when encoding null bytes (zero value bytes) as
trying to add chr(0) to a string will add nothing because chr(0)
is treated as an empty string (""). Therefore, in this case, you will
need to explicitly encode null bytes as %00.

 

The Server Side (ASP)

I implemented the server side script in ASP, and not compiled
CGI or ISAPI which would be my first choice for binary data, because I wanted
to show that binary processing can even be done using tools, frameworks, technologies
and languages that are not really meant for binary processing. The added advantage
in such an approach is that it is implementable by the widest possible set of
developers; not just seasoned programmers with a variety of weapons in their
arsenal but also rookie web scripters who only know Flash, JavaScript and ASP
and have only dealt with text files (in fact, straight ASP is supposed to only
support text files). Some added motivation came from the existential sugar of
doing what they say you cannot or are not supposed to do.

So, I chose to try and use normal ASP TextFile code
to save binary data. Well, turns out you can do that too, although it is better
to use ADO Streams if you require speed (ADO 2.5 required). The method illustrated
below uses the Form ASP object. This object is limited to 100K. This
means that your URI encoded string cannot be longer than 100K if you choose
to use the Form object. You can receive more than 100K of data if you
use the Request.BinaryRead() ASP method. Here is the ASP
code:

<% 

  Option Explicit 

  Dim i, fso, File, Byte_String, Byte_Array, Byte_Number, FileName

  

  Sub toDec(arr, max)

    dim j, temp 

    

    for j = 1 to max

      temp = "&H" & arr(j)

      arr(j) = Int(temp)

    next 

  End Sub

   

  Byte_String = request.Form("bmp_string")

  

  Byte_Array = Split(Byte_String, "%", -1, 0)

  

  Byte_Number = Len(Byte_String) / 3

  

  call toDec(Byte_Array, Byte_Number) 

  

  randomize

  Filename = "file" & Int(200 * Rnd + 1) & ".bmp"

  

  Set fso = Server.CreateObject("Scripting.FileSystemObject")

  Set File = fso.OpenTextFile(Server.MapPath(Filename), 2, true) 

  

  for i = 1 to Byte_Number

    File.Write( String( 1, Int(Byte_Array(i)) ) ) 

  next 

  

  File.close 

  

  Response.write("&url=http://daemon/test/" & Filename & "&end=1&")

  

  Set File = Nothing 

  Set fso  = Nothing

%>

An Alternative

Instead of URI encoding the array, containing the final values
representing the full binary file, before sending it with the loadVariables()
call, we could send it as it is. Flash would sent the contents of the
array as a comma delimited string of values that we could easily parse and binarily
store on the server side.

This would potentially make the sent string slightly longer
but would significantly decrease the server side script complexity and require
less processing time on both the client and server sides

Conclusion

A flash movie can emulate binary processing (and generate
bytestreams that represent BMP, SWF or other binary files) but it still needs
a server component that will save this content in a file system as a binary
file.

Here is the fla source of the Flash Bitmap Creator
movie as see above: movie

Contact me for bugs, suggestions and corrections at f_bass@yahoo.com

Post new comment

The content of this field is kept private and will not be shown publicly.

Share

  submit to reddit