blog

Processing BitmapData.getPixels() With PHP

Have you ever worked with BitmapData.getPixels() method? This method generates a byte array from a rectangular region of pixel data. Writes an unsigned integer (a 32-bit unmultiplied pixel value) for each pixel into the byte array. Thats sounds good, but what is it good for to have a ByteArray full of integers? There is not even a decoder for this format on my backend?!… Well, this format is so cool, and easy to access that you do are able to write your own decoder e.g. in PHP.

The resulting bytes are 32-bit unsigned integers for each pixel represented by AARRGGBB format, followed by another pixels and so on e.g.:

AA RR GG BB:
-- -- -- --
FF FF 00 00
00 00 FF 00

First pixel is full opaque red color, second pixel is full transparent green color. That sounds good enough to use with imagesetpixel(), that requires color in almost identic format, however PHP GD library recognizes alpha values from 0 to 127 in reversed logic (127 full transparent, 0 full opaque). So we only need to change the alpha value and we are good to go:

AA -> AA (php)
--    --
FF -> 00
00 -> 7F

What are we going to do is:

this format does not know about width and height of image, we have to define it manualy

access pixels (unsigned integers) from byte array (unpack() is the fastest method for this in PHP i guess)

iterate over each pixel / color

get color value and transform to PHP alpha logic $color>>24 returns alpha value (0-255), what we need is to divide it by two (0-127 for php) so $color>>25 works fine inversed logic requires values to be changed from 0-127 to 127-0: 0x7f-AA shift alpha value back to its correct position AA<<24 add original RRGGBB values to our new alpha AA|($color&0xffffff)

pass transformed color value into imagesetpixel() with correct x, y coordinate

This process should return correct image identifier. Lets have a new PHP function imagecreatefrombmdstring()

<?php
function imagecreatefrombmdstring($bytes, $width, $height)
{
	$img=imagecreatetruecolor($width, $height);
	imagealphablending($img, false);
	imagesavealpha($img, true);

	$x=0;
	$y=0;
	$colors=unpack("N*", $bytes);
	foreach($colors as $color)
	{
		imagesetpixel($img, $x, $y, (0x7f-($color>>25)<<24)|($color&0xffffff));
		if(++$x==$width)
		{
			$x=0;
			$y++;
		}
	}
	return $img;
}

The ActionScript code may look something like this, this method is in fact very fast on client side:

var bitmapData:BitmapData = ...
var bytes:Bytes=bitmapData.getPixels(bitmapData.rect);
var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest();
request.url = "our.php";
request.contentType = "application/octet-stream";
request.method = URLRequestMethod.POST;
request.data = bytes;
loader.load(request);

To optimize it even more, use ByteArray.compress() to compress bytes before sending, now you should get binary request not much bigger than length of the same request containing PNG encoded image bytes. On PHP side use gzuncompress() to get raw bytes.

Why did I developer/used this method of sending image to server? Well, I was not able to find fast enought/asynchronous PNG encoder. By using this technique, I was able to scale the getPixels+compress process into multiple frames (asynchronously) in order to create smooth upload process (using MultipartURLLoader).