Recipe 8.12. Using
Threshold
Problem
You want
to change the value of some pixels in a bitmap, based on their
current value.
Solution
Use the threshold( ) method of the
BitmapData class.
Discussion
The threshold( ) method is probably the
most complex in the BitmapData API, but quite powerful once
you understand how it works. The method uses two BitmapData
objects:
-
destBitmap, which is the bitmap
that will be altered.
-
sourceBitmap, which is the other
bitmap that gets passed in as a parameter. The method uses this
bitmap's pixel data for its calculations.
The method compares each pixel in
sourceBitmap against a specified value, using one of six
comparison operators. If the comparison fails, the corresponding
pixel in destBitmap is set to a specified color value. If
it passes the comparison, the pixel is either not changed, or you
have the option of copying over the sourceBitmap's value
for that pixel.
Here is the syntax for the method:
destBitmap.threshold(sourceBitmap,
sourceRect,
destPoint,
operation,
threshold,
color,
mask,
copySource)
We've already covered destBitmap and
sourceBitmap. The next parameter, sourceRect, is
an instance of the flash.geom.Rectangle class. It
defines what portion of the sourceBitmap you want to use
for comparison. If you want to use the entire bitmap, you can pass
in sourceBitmap.rect as a value to this parameter.
The destPoint parameter specifies
the point in the destBitmap at which the pixels start to
be affected. Picture the sourceBitmap overlaid on
destBitmap, with its top-left corner on this point. If you
want to use 0, 0 as the origin, just pass new Point( ) to
this parameter.
The operation parameter is one of
six strings that are equivalent to the comparison operators in
ActionScript. They are <, <=,
>, >=, ==, and !=. For
example, if you specify < as an operation, the test
passes for a given pixel if its value is less than the threshold
value, and it fails if it is greater than that.
Next is threshold. Each pixel is
compared against this value. You can pass a full 32-bit number in
here, and compare each pixel against that, but it may not give you
the results you expected. The reason is the way color values work.
For example, a pixel of 100 percent red (0xFFFF0000)
evaluates as "more than" a pixel of 100 percent blue
(0xFF0000FF) or green (0xFF00FF00). So normally
what you want to do is use a mask to isolate a specific channel and
compare against that.
The mask parameter is the hardest
for most people to grasp. All it is doing is isolating a particular
color channel. Normally you would just pass a hexadecimal value
here, with two zeros (00) for each color channel that you
want to mask out, and FF for the channel you want to use.
For example, 0x00FF0000 isolates the red channel, and
0xFF000000 isolates the alpha channel. See Figure
8-4 for a breakdown of the channels in a hexadecimal color
value.
The next two parameters determine what happens
when a pixel passes or fails a comparison. The color
parameter is the color the corresponding pixel is set to in the
destBitmap if the comparison passes. The copySource parameter determines
what happens if it fails. If this is true, the
sourceBitmap's pixel value for that pixel is copied over
to the destBitmap. If it is false, nothing
happens for that pixel in the destBitmap; it is left as
is.
Now let's see a few examples in action. Here's
some sample code that creates a source and destination bitmap and
creates some Perlin noise in the source. Then it adds the
destination bitmap to the display list and applies a threshold
using the source bitmap:
var srcBmp:BitmapData = new BitmapData(stage.stageWidth,
stage.stageHeight,
true, 0xffffffff);
srcBmp.perlinNoise(200, 100, 2, 1000, false, true, 1, true);
var destBmp:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0xffffffff);
var image:Bitmap = new Bitmap(destBmp);
addChild(image);
destBmp.threshold(srcBmp, // sourceBitmap
srcBmp.rect, // sourceRectangle
new Point( ), // destPoint
"<", // operator
0x00880000, // threshold
0x00000000, // color
0x00ff0000, // mask
true); // copySource
Here the method checks to see if a given pixel's
red channel (since this is defined in the mask) is less than
0x00880000. If so, it makes that pixel transparent. If
not, it copies over the source pixel. Since the Perlin noise was
created in grayscale, it doesn't matter which of the three color
channels (red, green, or blue) you use. If you are using a full
color image as a source, you might want to experiment with
different channels to see which gives you the desired effect:
-
As you can see if you run this code, it makes
all the darker areas of the Perlin noise pattern transparent.
-
Changing the operator to " >" has the opposite effect,
cutting out all the lighter value pixels.
-
Making the threshold value higher or
lower, say 0x00330000 or 0x00AA0000, cuts out
more or less pixels.
-
Try setting a different color value to
see what effect this has. Also try changing copySource to false and
see that it does not copy over the pixels, but keeps the original
values.
One thing to note is that there is no reason why
the source and destination bitmaps cannot be the same bitmap. You
can use a bitmap's own pixel data as a threshold. However, realize
that you are permanently altering that bitmap, so you won't be able
to repeat the operation with the same result, if needed.
As with many of the BitmapData methods,
they are often most powerful when used in combination. For example,
adding one line to the previous example creates a drop shadow,
which pops the pattern into 3D:
destBmp.applyFilter(destBmp, destBmp.rect,
new Point( ), new DropShadowFilter( ));
See Also
Recipes 8.13,
8.14,
and 8.15
for other ways to manipulate the content in a bitmap.
|