How to do Binary Processing in Flash

By Fotios, http://fotios.cc/

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:

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 fotios@altavista.net