Accessibility

Using Bitwise Operators to Manipulate Bits and Colors

Grant Skinner

CEO and chief architect
gskinner.com

Note: This article is part of a series of articles on new Flash 8 features written by the staff of gskinner.com, a Flash development and consulting company working with leading new media agencies and progressive corporate clients to create cutting-edge applications, games and, multimedia pieces. You can learn more by visiting gskinner.com or gskinner.com/blog/.

As the T-shirt says, there are 10 types of people in the world: those who understand binary and those who don't. If you got that (somewhat lame) joke, you've got a leg up. If not, don't worry; you will shortly.

For the most part, Flash developers have been able to avoid learning binary math or bitwise operators. This is partly because of the highly abstracted nature of Flash (we're not exactly writing assembly code here), partly due to the slow performance of bitwise operators in Flash relative to other languages, and partly due to the less formal programming educations that many of us have. Most importantly, until now there have been very few real reasons to utilize bitwise operations in Flash development.

That has all changed with Macromedia Flash 8. You now have the ability to work directly with pixels and color data, but are still very constrained by the processing limitations of the player. Bitwise operators offer a fast, relatively simple way to manipulate color data—once you figure out how to use them.

So I've dusted off my rusty knowledge of bitwise operators and how to apply them to color manipulation, and humbly present it in this article—just for you.

Requirements

To complete this tutorial you will need to install the following software and files:

Macromedia Flash 8 Professional

Tutorials and sample files:

Prerequisites

Basic knowledge of ActionScript and Flash

Binary Basics

In everyday life, you deal with numbers using a base 10, or decimal, representation. This means that each digit in the number is given a value 10 times greater than the digit to its right. This becomes clear when you break a number into the sum of its digits:

89,403 = (8 x 10,000) + (9 x 1000) + (4 x 100) + (0 x 10) + (3 x 1)

Looking at the above, you can see that a number represented in base 10 is in fact the sum of each of its digits multiplied by powers of 10, beginning with 10 to the power of 0 (anything to the power of 0 equals 1) for the least significant (rightmost) digit, and increasing successively with the significance of the digit:

89,403 = (8 x 10^4) + (9 x 10^3) + (4 x 10^2) + (0 x 10^1) + (3 x 10^0)

Most likely, humans use the base 10 number system because of our physiology: 10 fingers, base 10. Makes sense.

Computers, on the other hand, only work with two "fingers," the on and off states of a Boolean switch. As such, computers work natively with numbers in a base 2 system that uses only two digits: 1 and 0. As with base 10, base 2 numbers are equal to the sum of each digit multiplied by successive powers of 2:

110010 = (1 x 2^5) + (1 x 2^4) + (0 x 2^3) + (0 x 2^2) + (1 x 2^1) + (0 x 2^0)
110010 = (1 x 32) + (1 x 16) + (0 x 8) + (0 x 4) + (1 x 2) + (0 x 1)
110010 = 32 + 16 + 2 = 50

An excellent understanding of binary math used to be a requirement for any developer. As programming languages have become more mature, however, they have abstracted the computer's native number model to the point where many developers can go years—or even a whole career—without ever having to work directly with binary values.

There are certain things that are still easier to do in a binary system, though, and working with color values is one of them.

Color and Hexadecimal Numbers

Every web designer knows that colors are typically represented as a hex value, but what is a "hex value," exactly?

Hex is short for hexadecimal, which is another common number system in programming. Hexadecimal is a base 16 number system, which of course means that it uses powers of 16 to determine the value of each digit. Because there are only 10 numeric symbols (0–9), and hexadecimal allows up to 16 values for each digit, it uses letters for the higher digit values: A represents a value of 10, B is 11, and so on, up to F which has a value of 15. Here's a simple hexadecimal example:

B0A2 = (11 x 16^3) + (0 x 16^2) + (10 x 16^1) + (2 x 16^0)
B0A2 = (11 x 4096) + (0 x 256) + (10 x 16) + (2 x 1)
B0A2 = 45056 + 160 + 2 = 45218

A hex value is simply a hexadecimal number that represents a color. This representation is divided into discrete digits that represent the intensity of a particular color channel. For RGB colors, these are red, green and blue, of course. In Flash 8, you can often work with a fourth channel for alpha (transparency), which gives us ARGB. Each channel is represented by two digits, so in an ARGB color value the first two digits indicate the alpha value, the next two represent the red, then green, and finally blue, like so:

alpha (transparency) hexadecimal example

How does all of this tie back to binary? Well, 16 equals 2 to the power of 4, which means that one hexadecimal digit can be used to represent four binary digits. So you can think of an ARGB color value as an eight-digit hex value, or a 32-bit binary value. Likewise, an RGB value can be represented as a 24-bit binary value. This is, of course, why we call it 24-bit or 32-bit color depth.

Take a look at the demo below (requires Flash Player 8). Enter an ARGB color value into the fields at the top to see it converted to binary and decimal:

AlertThis content requires Flash

To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Download the free Flash Player now!

Get Adobe Flash Player


There is no way to enter a binary value directly in ActionScript but you can enter a hexadecimal value by prefixing the number with "0x", such as:

var myColorValue:Number = 0xFF00CC;

Being able to work with colors as binary values is convenient because it means you can utilize a powerful set of tools to manipulate colors: bitwise operators.


Tip: By default, Flash displays numbers in decimal format. If you would like display a number as a binary or hexadecimal value, you have to convert it to a string by using toString and passing in the radix (another word for base) as the first parameter like so:

trace(myColorValue); // decimal: 16711884
trace(myColorValue.toString(16)); // hexadecimal: ff00cc
trace(myColorValue.toString(2)); // binary: 111111110000000011001100

This only works well with RGB values because the toString method has a maximum value of 2147483647 (0x7FFFFFFF), which ARGB values will exceed.


Using Bitwise Operators

Before you begin reading this section, you might want to download the source files linked to on the first page of this article so that you can reference examples of these operators in action as you read.

Bitwise operators are utilized in a similar fashion to any other mathematical operator. What makes them unique is that rather than operating on an entire number, they affect each bit (or digit) in a binary number individually. That is, they are wise to the bits—bitwise!

For instance, if I were to apply a boolean NOT operator to the number 9, I would get "false" back because 9 would be converted to a single boolean value ("true"), and then have the NOT operator applied. With a bitwise NOT operator, I would get 6 back. This is because the bitwise NOT flips each bit in the binary representation of 9 separately, so 1001 (9 in binary) becomes 0110, which is 6 in decimal.

Just as with most mathematical operators, you can apply all bitwise operators except ~ (NOT) normally, such as:

result = source1 & source2
result = source << 3

Or by using shortcut assignment operators like the following:

source1 &= source2
source <<= 3

In the next sections, Tables 1 and 2 explain most of the bitwise operators available in Flash. The first two examples for each operator appear in pseudo code because there is no way to type a binary value directly into ActionScript. The third example uses hex values in ActionScript.

Combinational Operators

The operators in Table 1 are used to combine two binary numbers. For each bit in the two numbers, there are four possible combinations (1 and 1, 1 and 0, 0 and 1, 0 and 0). The truth table for each operator shows these combinations and their outcomes.

Table 1. Combinational Operators
  Name Description Truth Table Examples
A _ B =
& AND Sets the result bit to 1 only if both source bits are 1. 0 & 0 0

1001 & 1100 equals 1000

11 & 101 equals 1 (001)

0xF0F & 0xFF0 == 0xF00

0 & 1 0
1 & 0 0
1 & 1 1
| OR Sets the result bit to 1 if either of the source bits is 1. 0 | 0 0

1001 | 1100 equals 1101

11 | 101 equals 111

0xF0F & 0xFF0 == 0xFFF

0 | 1 1
1 | 0 1
1 | 1 1
^ XOR Sets the result bit to 1 only if either, but not both, of the source bits is 1. Hence the name eXclusive OR. 0 ^ 0 0

1001 | 1100 equals 1101

11 | 101 equals 111

0xF0F & 0xFF0 == 0x0FF

0 ^ 1 1
1 ^ 0 1
1 ^ 1 0

Other Operators

The operators in Table 2 all manipulate a single binary number.

Table 2. Other Operators
  Name Description Examples
~ NOT Flips each bit. Ones become zeroes and zeroes become ones.

~10010 equals 1101 (01101)

~111 equals 0 (000)

~0xF0F == 0xF0 (0x0F0)

<< Left shift Shifts each bit the specified number of positions to the left, filling the new (least significant) positions with zeroes. 10010 << 1 equals 100100

10010 << 2 equals 1001000

0xF0F << 2 == 0xF0F00
>> Right shift Shifts each bit the specified number of position to the right, discarding the rightmost (least significant) bits.

10010 >> 1 equals 1001

10010 >> 2 equals 100

0xF0F >> 2 == 0xF

By combining these operators, you can isolate and manipulate individual bits—and ultimately individual color channels—efficiently.

Extracting Color Channels

When I first started messing around with manipulating colors in Flash 5, I couldn't figure out a simple way to extract individual channels from a hex color value. Initially I used a horridly messy and inefficient system of converting the color value to a hex string, using substr to extract the channels and then converting back to numbers. Not so smart. As I continued playing, I switched to a math-based solution before finally realizing that bitwise operators were ideal for this task.

Consider what you need to achieve to extract a color channel. You need to isolate just the bits that represent that channel—meaning that you want to specify a set of bits to turn off (regardless of their original state) and a set of bits to leave in their existing state. If you look at the combinational operator table, the AND operator is able to fill this role. You can specify a bit mask that shows which bits to switch to zero (with zeroes in the mask: 0 AND anything gives 0) and which bits to leave in their current state (with ones in the mask: anything AND 1 gives the original state).

In action this looks like this:

var argb:Number = 0xFFC97B33;
// isolate Alpha:
// leave bits in alpha channel alone, zero others
var alphaMask:Number = 0xFF000000;
var alpha:Number = argb & alphaMask;
"trace("Alpha: "+alpha.toString(16));
// isolate Red:
var redMask:Number = 0x00FF0000;
var red:Number = argb & redMask;
trace("Red: "+red.toString(16));
// isolate Green:
var greenMask:Number = 0x0000FF00;
var green:Number = argb & greenMask;
trace("Green: "+green.toString(16));
// isolate Blue:
var blueMask:Number = 0x000000FF;
var blue:Number = argb & blueMask;
trace("Blue: "+blue.toString(16));

This code traces the following:

The red, green, and blue values all make sense, but the alpha value is a little weird. This is because Flash uses signed integer values to work with bitwise operators. Signed integers count up to a maximum positive value of 0x7FFFFFF, wrap around to –0x7FFFFFFF, and then count up towards –1. A full explanation of signed versus unsigned integer values is beyond the scope of this article but suffice it to say that –0x100000 is the equivalent of 0xFF000000 for our purposes. You'll see that everything works out fine in the next step.

Everything works perfectly for the blue channel but you still need to shift the other channels to the right so that they occupy the least significant digits. You could do this using the right shift operator (>>) but it's important to remember that each hex character represents four bits, so you have to shift by four bits for every hex digit you want to shift. You also want Flash to treat the value as an unsigned value to avoid unexpected negative results as in the above example, so use the unsigned right shift operator (>>>) instead.

Let's simplify the masking code and add code to shift all the channels except blue:

var argb:Number = 0xFFC97B33;
// mask the alpha bits, then shift them to the least significant bits
// shifting 6 hex digits, so 24 binary digits
var alpha:Number = (argb & 0xFF000000) >>> 24;
trace("Alpha: "+alpha.toString(16));
// isolate Red:
var red:Number = (argb & 0x00FF0000) >>> 16;
trace("Red: "+red.toString(16));
// isolate Green:
var green:Number = (argb & 0x0000FF00) >>> 8;
trace("Green: "+green.toString(16));
// isolate Blue:
var blue:Number = argb & 0x000000FF;
trace("Blue: "+blue.toString(16));

This code traces the following:

It looks like this worked! Now you can manipulate the channels separately.


Tip: You can convert a hexadecimal or binary string to a number using parseInt and passing the radix as the second parameter:

// decimal:
trace(parseInt("16711884")); // 16711884
// hexadecimal:
trace(parseInt("FF00CC",16)); // 16711884
// binary:
trace(parseInt("111111110000000011001100",2)); // 16711884

Manipulating Color Channels

Color channel values are simply integers that range from 0 (0x00) to 255 (0xFF). As such, you can use just about any math operators you want to manipulate them; the important thing is to ensure that when you recombine them, they are still within the range from 0 to 255.

The following example simply adds random, channel-specific "noise" to the color channel values you extracted in the previous section. Although this example affects only a single color value, by looping through all of the pixels (or random pixels) in a BitmapData instance, you could generate noise over an image:

var redNoiseAmount:Number = 255;
var greenNoiseAmount:Number = 0;
var blueNoiseAmount:Number = 128;

// add a random amount to each channel, but keep the value within 0-255:
red = Math.min(255,red+random(redNoiseAmount));
green = Math.min(255,green+random(greenNoiseAmount));
blue = Math.min(255,blue+random(blueNoiseAmount));

This is sort of a useless example, considering that the BitmapData.noise method is available. You could do much more complex and interesting manipulations than this one, but I wanted to keep things very simple (the focus is on the bitwise operations). There is a slightly more useful example in the source files you downloaded previously.

All that remains now is to combine the individual color channels back into a single color value.

Combining Color Channels

Combining separate channels back into a single color value is easier than splitting them apart. All you have to do is left-shift each channel back into its proper position and then use the OR operator (|) to combine them again. Mashed into a single, efficient line of code, it looks like this:

argb = (alpha << 24) | (red << 16) | (green << 8) | blue;

Broken down, you can see the exact process:

// argb starts at 0, which we can look at as 0x00000000
argb = 0;
// take the alpha value (0xFF) and shift it 24 bits (6 hex digits) left:
alpha <<= 24;
// alpha now equals 0xFF000000
// combine it with argb using OR. This will turn on any bits that are on in alpha:
argb |= alpha;
// argb now equals 0xFF000000
// shift the red value (0xC9) 16 bits (4 hex digits) left:
red <<= 16;
// red now equals 0x00C90000
// combine with argb using OR, this will turn on any bits that are on in red, while leaving on any bits that were previously on:
argb |= red;
// argb now equals 0xFFC90000

// continue with green and blue.

Using bitwise operators, you can now separate a single RGB or ARGB color into its component channels, manipulate the color values for each channel, and then combine them back into a single color value. To see this all at work in one place, check out the source files you downloaded earlier and then take a look at some other ways you can apply bitwise operators.

Inserting Color Channels

What if you want to manipulate just one color channel? It's easy enough to extract a single color channel but it might not be so obvious how to insert a single channel into an existing color value without dissembling the whole thing. All you need to do is zero the channel's original bits by applying an inverted bit mask using AND, and then using the OR operator to set the new bits in the channel.

This example inserts a new value (0xEE) into the green channel of an existing color:

var argb:Number = 0xFFC97B33;
var newGreenValue:Number = 0xEE;
// apply an inverted mask using AND – a one in the mask will stay unchanged, a zero in the mask will result in a zero:
argb &= 0xFFFF00FF;
// argb now equals 0xFFC90033
// shift the green value into the right position (2 hex digits or 8 bits left), then use OR to insert it into the color value:
argb |= (newGreenValue << 8);
// argb now equals 0xFFC9EE33

You could even do this all in a single line:

argb = (argb & 0xFFFF00FF) | (0xFF << 8);

Simple, eh?


Tip: Another handy use for shift operators is to emulate a Math.floor operation. Shift operators discard anything after the decimal place, so shifting by zero provides a faster way to floor your numbers (both to type and execute):

trace(1224.215>>0); // 1224


Masking for BitmapData Methods

Two methods of the BitmapData object accept bit masks as parameters: Threshold and getColorBoundsRect. In both cases, the mask specifies which bits in the color value the method should use in its comparisons. For example, if you wanted to use getColorBoundsRect to find the area containing all pixels which had a specific value for only the green channel, you could do the following:

var greenValueToMatch:Number = 0x80;
// set up a mask with only the green channel bits turned on:
var colorMask:Number = 0x0000FF00;
// call the method, and don't forget to shift the green bits into the correct position:
rect = myBitmapData.getColorBoundsRect(colorMask,greenValueToMatch<<8);

The getColorBoundsRect method uses the AND operator internally to apply the mask. It essentially checks each pixel in the bitmap to see whether it matches using a formula similar to the following:

(bitMask & colorValueToMatch) == (bitMask & sourcePixelColorValue)

Threshold uses bit masks in a nearly identical way. The following code makes all pixels with a red channel value greater than 0x99 turn white (0xFFFFFFFF):

// set up a mask with only the red channel bits turned on:
var colorMask:Number = 0x00FF0000;
// call the method
myBmp.threshold(myBmp,rect,pnt,">",0x99<<16,0xFFFFFFFF,colorMask,true);

For more specific information on these methods, check out the following pages in the Flash 8 LiveDocs:

Where to Go from Here

The best way to become comfortable with binary, hexadecimal, and bitwise concepts is to play with them. Start out by taking a look through the source file available at the beginning of this article, and then experiment on your own.

There are a ton of resources available on the web, too. Enter "bitwise operators" into Google to find them!

About the author

Grant Skinner is the CEO and chief architect of gskinner.com, a creative technology company with a focus on the Flash platform. He works with leading new media agencies and progressive corporate clients to create cutting-edge applications, campaigns, and multimedia pieces. His expertise in fusing coding with interface design, usability, marketing, and business logic has garnered international acclaim and resulted in a number of prestigious industry awards. Grant maintains an active blog at gskinner.com/blog/ and an exhibit of his experimental work at incomplet.org. Read more about Grant in the Flash: Ten years, ten perspectives series.