You love Chroma Key, don’t you? As you know, this method is used to replace a background color with what you want. Today this technique is used everywhere also where you don’t expect. Have a look at the video below.
So, let’s see how to implement the Chroma Key in Adobe Flash. In my demo, I used a video object linked to the camera. Also, you’ll see that I used the black as chroma key color. But as you know, in a real application we will use a blue color instead (do you know blue screen?) or a green color (Pantone 354). Further, you’ll notice that the values uint
are in the format 0xAARRGGBB
, where AA
is the alpha channel used for transparency.
In order to process the image, I used the BitmapData threshold method. This is a very powerful method. It is easy to use as well. It can check every pixel, one to one, with a mask in order to verify if the colors are equals, greats than or less than and replace them with a specified color:
public function threshold(sourceBitmapData:BitmapData,
sourceRect:Rectangle,
destPoint:Point,
operation:String,
threshold:uint,
color:uint = 0,
mask:uint = 0xFFFFFFFF,
copySource:Boolean = false):uint
sourceBitmapData
the source bitmap. You can use a different sourcesourceRect
an instance ofRectangle
class used as source size delimiterdestPoint
an instance ofPoint
class used as destination coordinate, usually0,0
operation
this is a string that define the operation to apply on every pixel (see below for detail) “<“, “<=”, “>”, “>=”, “==”, “!=”threshold
is aunsigned int
, used in compare, for example0x00FF0000
color
is aunsigned int
. This is the color that will be replaced when the compared is successfullymask
is aunsigned int
. This is the mask to apply to threshold and source bitmap pixelscopySource
is a boolean used to copy the original value when compared fail
This method returns the number of pixels changed. Now, let me show you the steps of this method:
1. Start from sourceRect
2. Read the first-pixel value from sourceBitmap
and apply mask
value on it. A logical AND is made: ( readPixel & mask )
3. The result value is compared with ( threshold & mask )
using the operation
4. If the result is true, then starting from destPoint
that pixel is set to color
5. If the result is false, then nothing happen
In the demo you’ll see 4 boxes with camera image:

In the first box (top left), you’ll see the original video capture. It is as it is in the Camera
object.
var cam:Camera = Camera.getCamera();
vid_video.attachCamera( cam );
Below (bottom left) you’ll see the Threshold box:
// Threshold
var simpleBitmapData:BitmapData = new BitmapData( vid_video.width, vid_video.height, true);
var simpleBitmap:Bitmap = new Bitmap(simpleBitmapData);
addChild(simpleBitmap);
And then:
addEventListener( Event.ENTER_FRAME,
function (e:Event):void {
simpleBitmap.bitmapData.draw( vid_video );
simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData,
new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height),
new Point(0,0),
"==",
0x00000000,
0x00000000,
0x00FFFFFF,
true );
}
);
As you can see we got a simple chroma key for black color. Beware because a color value as 0x00000001
will be ignored! To fix it, we have to use a different value and operator, for example:
addEventListener( Event.ENTER_FRAME,
function (e:Event):void {
simpleBitmap.bitmapData.draw( vid_video );
simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData,
new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height),
new Point(0,0),
"<=",
0x00222222,
0x00000000,
0x00FFFFFF,
true );
}
);
The code above uses the <=
operator. However, we have an issue again! In fact, a color value as 0x00002200
(dark green) will get as chroma color because we are using <= 0x00222222
. To fix that, we have to create a special function in order to get only black and gray shades:
function applyThresholdRGB( myBitmapData:BitmapData, channel:String, op:String, threshold:uint ):void {
var maskColor:uint = (channel == "red") ? 0x00FF0000 : ( (channel == "green") ? 0x0000FF00 : 0x000000FF );
myBitmapData.threshold(myBitmapData,
new Rectangle(0, 0, myBitmapData.width, myBitmapData.height),
new Point(0,0),
op,
threshold,
0x00000000,
maskColor,
false );
}
next:
function createBitmapMask(myBitmapData:BitmapData, colorLow:uint, colorHi:uint):void {
//red
applyThresholdRGB( myBitmapData, "red", "<", colorLow );
applyThresholdRGB( myBitmapData, "red", ">", colorHi );
//green
applyThresholdRGB( myBitmapData, "green", "<", colorLow );
applyThresholdRGB( myBitmapData, "green", ">", colorHi );
//blue
applyThresholdRGB( myBitmapData, "blue", "<", colorLow );
applyThresholdRGB( myBitmapData, "blue", ">", colorHi );
}
You can see this bitmap mask in the box Bitmap Mask (top right), and finally:
addEventListener( Event.ENTER_FRAME,
function (e:Event):void {
// clone video
simpleBitmap.bitmapData.draw( vid_video );
// create a new bitmap with the same original data
var bitmapMask:Bitmap = new Bitmap(simpleBitmap.bitmapData.clone());
// create the mask
createBitmapMask(bitmapMask.bitmapData, 0x00000000, 0x00222222);
// apply the mask to original with BlendMode.ERASE
simpleBitmap.bitmapData.draw(bitmapMask, null, null, BlendMode.ERASE, null, false);
}
);
You’ll see the final result in the Bitmap Merge box (bottom right) of the demo. That’s all.