Understanding how Alpha, Red, Green, and Blue channels pack into a single 32-bit integer — and how to extract, pack, and blend them with bitwise operations.
ARGB is the standard 32-bit color format used by Android, .NET (System.Drawing), CSS rgba(), and many image processing libraries. Instead of three 8-bit channels like plain RGB, ARGB adds a fourth channel — Alpha — which controls transparency. When I first needed to composite UI elements programmatically, understanding ARGB layout was the key to getting alpha blending right without visual artifacts.
The format packs four 8-bit values into one 32-bit integer, occupying exactly 4 bytes:
I implemented alpha blending from scratch in a WebGL project and the key insight was that the alpha channel occupies the top 8 bits of a 32-bit ARGB value. Masking and shifting to extract a = (pixel >> 24) & 0xFF became second nature.
The alpha channel ranges from 0 (fully transparent, invisible) to 255 (fully opaque, solid). An alpha of 128 means the pixel is roughly 50% transparent — it will blend with whatever is behind it. An ARGB value of 0x00RRGGBB is completely transparent regardless of the RGB values, while 0xFFRRGGBB is fully opaque.
Extracting all four channels from an ARGB integer follows the same pattern as RGB extraction, just with one more shift. The alpha channel sits in the highest byte, so it needs a 24-bit right shift.
The pattern is completely consistent: each channel is extracted by shifting right by its bit offset and masking with 0xFF. The shift amounts are multiples of 8 because each channel occupies exactly one byte. This regularity is why ARGB extraction is so easy to implement and optimize.
// JavaScript: extract all four ARGB channels
function extractARGB(color) {
return {
a: (color >> 24) & 0xFF,
r: (color >> 16) & 0xFF,
g: (color >> 8) & 0xFF,
b: color & 0xFF
};
}
console.log(extractARGB(0x80FF5733));
// { a: 128, r: 255, g: 87, b: 51 }
Going from four separate channel values back to a single ARGB integer follows the same left-shift-and-OR pattern as RGB, with the alpha byte shifted 24 bits. Notice that each shift places the channel into its correct byte position without overlapping.
I always clamp or mask each channel value to 0-255 before packing. A common bug is passing a value larger than 255, which bleeds into the adjacent byte. The defensive mask val & 0xFF in the pack function prevents this silently and has saved me hours of debugging color artifacts in image processing code.
// JavaScript: pack four channels into ARGB
function packARGB(a, r, g, b) {
return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) |
((g & 0xFF) << 8) | (b & 0xFF);
}
console.log(packARGB(128, 255, 87, 51).toString(16));
// Output: "80ff5733"
Alpha blending is the process of compositing a transparent foreground pixel over a background pixel. The math is simple: for each channel, the result is a weighted average based on the foreground alpha. I wrote my first alpha blender to composite UI overlays on a video stream, and the performance gain from using bitwise fixed-point math instead of floating-point was dramatic.
The standard alpha blending formula for a single channel is:
The fixed-point version works because multiplying by alpha (0-255) and then dividing by 256 via right shift is equivalent to scaling by alpha/256, which is a close approximation of alpha/255. For most rendering, the 0.4% error per channel is invisible to the human eye.
// JavaScript: alpha blend two ARGB pixels
function alphaBlend(fg, bg) {
let a = (fg >> 24) & 0xFF;
if (a === 0) return bg; // fully transparent
if (a === 255) return fg; // fully opaque
let ia = 255 - a; // inverse alpha
let r = ((fg >> 16) & 0xFF) * a + ((bg >> 16) & 0xFF) * ia;
let g = ((fg >> 8) & 0xFF) * a + ((bg >> 8) & 0xFF) * ia;
let b = ( fg & 0xFF) * a + ( bg & 0xFF) * ia;
return packARGB(255, (r + 128) >> 8, (g + 128) >> 8, (b + 128) >> 8);
}
Not all 32-bit color formats store channels in the same order. The bitwise extraction formulas change depending on the format. This was a painful lesson I learned when a texture rendered with swapped red and blue channels because the source used BGRA while my code assumed ARGB.
For RGBA format, the extraction changes to: R = (color >> 24) & 0xFF, G = (color >> 16) & 0xFF, B = (color >> 8) & 0xFF, A = color & 0xFF. The alpha byte moves from the most significant to the least significant position.
When working with a library or API that returns raw pixel data, I always test with a known color — say, pure red (ARGB = 0xFFFF0000). If the extracted red channel reads 0 and blue reads 255, the format is likely BGRA. This one-line test has saved me hours of debugging on multiple occasions.
ARGB is just one pixel format. Explore color masking and different bit depths for a complete picture of how images work at the binary level.
ARGB is a 32-bit color format where four 8-bit channels are packed into one integer: Alpha (bits 31-24), Red (bits 23-16), Green (bits 15-8), and Blue (bits 7-0). The alpha channel determines opacity: 0 = fully transparent, 255 = fully opaque. This format is used by Android, .NET, CSS, and most modern graphics APIs.
Extract the alpha channel by shifting the 32-bit ARGB value right by 24 bits and masking with 0xFF: A = (color >> 24) & 0xFF. For example, from 0x80FF5733, shifting right by 24 gives 0x80, which is the alpha value (128, or roughly 50% opacity).
Pack four separate 8-bit values into one ARGB integer using left shifts and OR: argb = (A << 24) | (R << 16) | (G << 8) | B. Alpha goes to the highest byte, followed by red, green, and blue in the lowest byte.
ARGB stores alpha in the highest byte (bits 31-24) followed by RGB. RGBA stores RGB in the high three bytes and alpha in the lowest byte (bits 7-0). The extraction formulas differ: for RGBA, alpha = color & 0xFF, while for ARGB, alpha = (color >> 24) & 0xFF. Both use 32 bits total.
Alpha blending computes each output channel as a weighted average of the source and destination based on alpha. The formula for a single channel is: out = (src * alpha + dst * (255 - alpha)) / 255. Efficient implementations use fixed-point arithmetic and bit shifts to avoid division, e.g., out = (src * alpha + dst * (255 - alpha) + 128) >> 8.