Showcase and discover digital art at yex

Follow Design Stacks

Subscribe to our free newsletter to get all our latest tutorials and articles delivered directly to your inbox!

Using Bitwise Operators: Extracting Color Channels

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:

  • Alpha: –1000000
  • Red: C90000
  • Green: 7B00
  • Blue: 33

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:

  • Alpha: FF
  • Red: C9
  • Green: 7B
  • Blue: 33

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

Comments