1. Description
With the introduction of flash 8 "flash.display.BitmapData" class thousands of new application can now be made using Flash..
In this tutorial we will focus our attention to the BitmapData.gePixel() method in order to transform a portion of flash movie into a JPEG (created with PHP/GD)
BitmapData has different ways to get pixel color informations:
- getPixel(x:Number, y:Number) : Number
Returns an integer representing an RGB pixel value from a BitmapData object at a specific point (x, y).
- getPixel32(x:Number, y:Number) : Number
Returns an ARGB color value that contains alpha channel data as well as RGB data.
- getColorBoundsRect(mask:Number, color:Number, [findColor:Boolean]) : Rectangle
Determines a rectangular region that fully encloses all pixels of a given color within the bitmap image.
What we will use in this tutorial is getPixel().
Here a basic example on how it can work:
|
import flash.display.*
var bmp:BitmapData = new BitmapData(this._width, this._height, false) bmp.draw(this); this.onMouseMove = function(){ var pColor:Number = bmp.getPixel(_xmouse, _ymouse) var hexColor:String = pColor.toString(16).toUpperCase() while(hexColor.length < 6){ hexColor = "0" + hexColor } var r = Number("0x" + hexColor.substr(0,2)) var g = Number("0x" + hexColor.substr(2,2)) var b = Number("0x" + hexColor.substr(4,2)) testo.text = "0x" + hexColor + ", {r:" + r + ", g:" + g + ", b:" + b + "}" } |
Remember to import the flash.display.BitmapData class.
Create a new bitmapdata instance and assign to it the same dimensions as the current Stage.
Then using draw() we're making an exact copy of the _root movieclip into the bitmapdata object.
Using:
| bmp.getPixel( _xmouse, _ymouse).toString(16).toUpperCase() |
we will have the hexadecimal color value of the pixel at those coordinates.
2. Advanced Example
Ok, we can make a copy of everything in a flash movie using this method.. and so we can also send all the pixel color values to an external application in order to recreate a JPEG of the copied movieclip. The bigger problem is that for a movie of 550x400 whe should collect 220000 colors values, and converting the value into an hexadecimal string this means that the final string to send will be 1320000 chars long. This means a lot of data!
Another problem is that we can't collect all the pixel color values in a single for loop, otherwise the flash player will dead suddenly!
I made this example ( 500x210 ), with a .flv video included.
You can also draw some lines above the .swf using mouse
As you can see in the previous example, once "print" button is pressed a loader window appears counting the percent progress.
This is because i used a setInterval function to copy all the pixels. This won't stress your cpu so much..
Here the class i use:
|
import flash.display.BitmapData;
import flash.geom.Rectangle; import flash.geom.ColorTransform; import flash.geom.Matrix; /** * Little and simple print flash screen class */ class it.sephiroth.PrintScreen { public var addListener:Function public var broadcastMessage:Function private var id: Number; public var record:LoadVars; function PrintScreen(){ AsBroadcaster.initialize( this ); } public function print(mc:MovieClip, x:Number, y:Number, w:Number, h:Number){ broadcastMessage("onStart", mc); if(x == undefined) x = 0; if(y == undefined) y = 0; if(w == undefined) w = mc._width; if(h == undefined) h = mc._height; var bmp:BitmapData = new BitmapData(w, h, false); record = new LoadVars(); record.width = w record.height = h record.cols = 0 record.rows = 0 var matrix = new Matrix(); matrix.translate(-x, -y) bmp.draw(mc, matrix, new ColorTransform(), 1, new Rectangle(0, 0, w, h)); id = setInterval(copysource, 5, this, mc, bmp); } private function copysource(scope, movie, bit){ var pixel:Number var str_pixel:String scope.record["px" + scope.record.rows] = new Array(); for(var a = 0; a < bit.width; a++){ pixel = bit.getPixel(a, scope.record.rows) str_pixel = pixel.toString(16) if(pixel == 0xFFFFFF) str_pixel = ""; // don't send blank pixel scope.record["px" + scope.record.rows].push(str_pixel) } scope.broadcastMessage("onProgress", movie, scope.record.rows, bit.height) // send back the progress status scope.record.rows += 1 if(scope.record.rows >= bit.height){ clearInterval(scope.id) scope.broadcastMessage("onComplete", movie, scope.record) // completed! bit.dispose(); } } } |
The ASBroadcaster class will be useful because i will use the addListener() method in order to receive events (onStart, onComplete and onProgress).
The main method is "print", which accepts there params:
- mc: the movieclip to copy
- x: x origin of the copy
- y: y origin of the copy
- w: width
- h: height
Then the setInterval will copy all the pixel in one single row in the BitmapData object, every 5 ms seconds.
Every array of colors (in each row) will be stored in a new array within a LoadVar object. In this way i will have 210 variables to post with the LoadVars object.
Please note that 0xFFFFFF pixels won't be added in the arrays, this beacuse the image i will create in PHP will have a blank background color, and for this reason blank pixels wont be created every time...
3. The .fla source
Now let's take a look at the flash file will use this "PrintScreen" class:
|
import it.sephiroth.mloaderWindow
import it.sephiroth.PrintScreen var loader:mloaderWindow = this.createClassObject(mloaderWindow, "loader", 10, {_x:-1000, _y:-1000}) loader.setStyle("borderColor", 0x006699) // listener which receives the broadcast message // from the PrintScreen class var listener:Object = new Object(); // copy in progress... listener.onProgress = function(target:MovieClip, loaded:Number, total:Number){ var perc = Math.round((loaded/total)*100) loader.label = "computing... " + perc + "%" loader.value = perc } // copy is complete, send the result LoadVars to PHP listener.onComplete = function(target:MovieClip, load_var:LoadVars){ loader.label = "sending to php..." load_var.send("pixels.php", "_blank", "POST") loader.close() } /** * Print Button has been clicked */ function print_me(){ video_mc.pause() // first pause the playing video pn = new PrintScreen(); // initialize the PrintScreen class pn.addListener( listener ); // assign a listener pn.print(_root, 0, 0, 500, 210) // copy the _root loader.label = "computing... 0%" loader.open(true, true, true); // open a loader } |
There's nothing particular to say here..
Once the "print" button is clicked call the print_me() function.
Stop the playing video, in order to copy the current video frame.
Initialize the PrintScreen class and assign a listener which will receive all the broadcaster messages.
I used pn.print(_root, 0,0, 500, 210) in order to print all the contents in _root.
Once the process is completed send the LoadVars object, which is returned by the onComplete function, to PHP using the POST method ( the posted Content-length is: 563024 )
4. Generate the image in PHP
Here the code of the "pixels.php" page
| <?php
error_reporting(0); /** * Get the width and height of the destination image * from the POST variables and convert them into * integer values */ $w = (int)$_POST['width']; $h = (int)$_POST['height']; // create the image with desired width and height $img = imagecreatetruecolor($w, $h); // now fill the image with blank color // do you remember i wont pass the 0xFFFFFF pixels // from flash? imagefill($img, 0, 0, 0xFFFFFF); $rows = 0; $cols = 0; // now process every POST variable which // contains a pixel color for($rows = 0; $rows < $h; $rows++){ // convert the string into an array of n elements $c_row = explode(",", $_POST['px' . $rows]); for($cols = 0; $cols < $w; $cols++){ // get the single pixel color value $value = $c_row[$cols]; // if value is not empty (empty values are the blank pixels) if($value != ""){ // get the hexadecimal string (must be 6 chars length) // so add the missing chars if needed $hex = $value; while(strlen($hex) < 6){ $hex = "0" . $hex; } // convert value from HEX to RGB $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); // allocate the new color // N.B. teorically if a color was already allocated // we dont need to allocate another time // but this is only an example $test = imagecolorallocate($img, $r, $g, $b); // and paste that color into the image // at the correct position imagesetpixel($img, $cols, $rows, $test); } } } // print out the correct header to the browser header("Content-type:image/jpeg"); // display the image imagejpeg($img, "", 90); ?> |
That's all.
This is only a demonstration, because this method require a lot of memory to be used from PHP in order to generate the image (ImageMagik should be a better solution in this case indeed), and also because the POST headers are very very big!
You can use for example the Live HTTP headers FireFox extension to see exactly what we're passing to the pixels.php page.
In the same way you can read pixels informations from a BitmapData many other things can be done, for example parsing an image and find streets
6. Download file source
Download files used in this tutorial here.






