Pixel Format Bitwise

How different pixel formats encode color at the binary level. Compare RGB332, RGB565, RGB888, and ARGB8888 layouts, extraction, and conversion.

Digital pixel grid demonstrating pixel format bit encoding
Pixel Format Overview RGB332 (8-bit) RGB565 (16-bit) RGB888 (24-bit) ARGB8888 (32-bit) Format Conversion FAQ

Pixel Format Overview — Color Depth at the Bit Level

Every pixel format is defined by two things: how many bits are allocated to each color channel, and the order they appear in memory. I have worked with embedded displays, canvas APIs, and image codecs — each uses a different pixel format, and getting the bit layout wrong produces corrupted or shifted colors every single time.

Here is a quick comparison of the major pixel formats. The bit allocations show how many bits each channel receives, which directly determines the color precision of that channel.

When I was working on a retro game emulator, understanding pixel format layouts was essential. The difference between RGB565 and ARGB1555 is just a few bits per channel, but getting it wrong produces completely wrong colors.

FormatTotal BitsR BitsG BitsB BitsA BitsColors
RGB33283320256
RGB56516565065,536
RGB88824888016.7M
ARGB888832888816.7M + alpha

The key pattern is that each format uses a fixed total bit budget per pixel. Lower-bit-depth formats sacrifice color precision for memory savings. RGB332 gives you only 256 colors but uses 1 byte per pixel. RGB565 gives 65,536 colors at 2 bytes per pixel — a great trade-off for embedded systems. RGB888 and ARGB8888 deliver true color at 3-4 bytes per pixel.

RGB332 (8-Bit Color) — 3-3-2 Bit Allocation

RGB332 is the most compact RGB format at 8 bits per pixel. Red and Green each get 3 bits (8 levels each), and Blue gets 2 bits (4 levels), for a total of 8 × 8 × 4 = 256 possible colors. The blue channel gets fewer bits because the human eye is less sensitive to blue variations — a deliberate design choice I have seen made again and again in low-memory graphics systems.

RGB332 Bit Layout
Bit position: 7 6 5 | 4 3 2 | 1 0
Channel:     R      |  G    | B

Example: 0xD6 = 11010110
R = 110 = 6 (out of 7 max)  // ~85% red
G = 101 = 5 (out of 7 max)  // ~71% green
B = 10 = 2 (out of 3 max)   // ~66% blue
Extraction: R = (pixel >> 5) & 0x07, G = (pixel >> 2) & 0x07, B = pixel & 0x03

I encountered RGB332 on a small OLED display module for a weather station project. With only 256 colors, gradients were visibly banded, but the display data fit in a tiny buffer. The extraction pattern — shift right by the combined width of the lower channels, mask by 2^bits - 1 — works for any packed pixel format.

// JavaScript: extract RGB332 channels and convert to 8-bit each
function rgb332ToRgb888(pixel) {
  const r3 = (pixel >> 5) & 0x07;  // 3-bit red
  const g3 = (pixel >> 2) & 0x07;  // 3-bit green
  const b2 =  pixel       & 0x03;  // 2-bit blue
  // Scale to 0-255
  return {
    r: (r3 * 255) / 7,   // scale 0-7  0-255
    g: (g3 * 255) / 7,
    b: (b2 * 255) / 3
  };
}

RGB565 (16-Bit High Color) — The Embedded Standard

RGB565 is the most widely used 16-bit pixel format in embedded systems, LCD controllers, and older graphics hardware. It allocates 5 bits to Red, 6 bits to Green (the human eye is most sensitive to green), and 5 bits to Blue. This asymmetry gives better perceived quality than a symmetric 5-5-5 allocation while still fitting neatly into 16 bits.

RGB565 Bit Layout
Bit position: 15 14 13 12 11 | 10 9 8 7 6 5 | 4 3 2 1 0
Channel:      R (5 bits)       |  G (6 bits)        | B (5 bits)

Example pixel: 0xFC53
Binary:  11111 100101 10011
R = 11111 = 31 (max, 100% red)
G = 100101 = 37 (58% green)
B = 10011 = 19 (61% blue)

The extraction formulas for RGB565 are the pattern I use most often in embedded work. I have these memorized:

RGB565 Channel Extraction
R5 = (pixel >> 11) & 0x1F  // shift by G+B bits (6+5=11)
G6 = (pixel >>  5) & 0x3F  // shift by B bits (5)
B5 = pixel & 0x1F       // no shift

To convert to 8-bit for display:
R8 = (R5 * 255) / 31
G8 = (G6 * 255) / 63
B8 = (B5 * 255) / 31

The masks 0x1F (binary 11111, decimal 31) and 0x3F (binary 111111, decimal 63) correspond to the range of each channel. The scaling formula value * 255 / maxValue maps the reduced-range channel to an 8-bit display. Note that 31 * 255 / 31 = 255 and 0 * 255 / 31 = 0, so the endpoints are preserved correctly.

// JavaScript: RGB565 ↔ RGB888 conversion
function rgb565ToRgb888(pixel) {
  const r5 = (pixel >> 11) & 0x1F;
  const g6 = (pixel >>  5) & 0x3F;
  const b5 =  pixel        & 0x1F;
  return {
    r: Math.round(r5 * 255 / 31),
    g: Math.round(g6 * 255 / 63),
    b: Math.round(b5 * 255 / 31)
  };
}

function rgb888ToRgb565(r, g, b) {
  const r5 = (r >> 3) & 0x1F;   // keep top 5 bits
  const g6 = (g >> 2) & 0x3F;   // keep top 6 bits
  const b5 = (b >> 3) & 0x1F;   // keep top 5 bits
  return (r5 << 11) | (g6 << 5) | b5;
}

RGB888 (24-Bit True Color)

RGB888 is what we mean by "true color" — 8 bits per channel, 16.7 million colors. Each pixel occupies exactly 3 bytes, with 256 levels per channel. This is the standard for modern displays, JPEG/PNG images, and most web graphics (CSS rgb()).

RGB888 Bit Layout
Byte 2 (MSB) | Byte 1 | Byte 0 (LSB)
R[7-0]       | G[7-0] | B[7-0]

Example: 0xFF5733
R = 0xFF = 11111111 = 255 (full red)
G = 0x57 = 01010111 = 87
B = 0x33 = 00110011 = 51

One practical issue with RGB888 in memory: with 3 bytes per pixel, rows do not align to 4-byte boundaries naturally. A 4-pixel-wide row is 12 bytes (aligned), but a 5-pixel-wide row is 15 bytes — requiring 1 byte of padding per row in formats like BMP. This is why many graphics APIs internally prefer 32-bit formats even when no alpha is needed — the 4-byte alignment simplifies memory access.

// JavaScript: pack/unpack RGB888 (stored as 0xRRGGBB)
function rgb888Unpack(color) {
  return {
    r: (color >> 16) & 0xFF,
    g: (color >>  8) & 0xFF,
    b:  color        & 0xFF
  };
}

function rgb888Pack(r, g, b) {
  return ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
}

ARGB8888 (32-Bit Color with Alpha)

ARGB8888 adds an 8-bit alpha channel to the three RGB channels, making 32 bits per pixel. This is the native format for Android graphics, .NET GDI+, WebGL textures, and most modern GPU framebuffers. The 4-byte alignment makes it efficient for hardware rendering.

ARGB8888 Bit Layout
Byte 3 (MSB) | Byte 2 | Byte 1 | Byte 0 (LSB)
A[7-0]       | R[7-0] | G[7-0] | B[7-0]

Example: 0x80FF5733
A = 0x80 = 10000000 = 128 (50% opacity)
R = 0xFF = 11111111 = 255
G = 0x57 = 01010111 = 87
B = 0x33 = 00110011 = 51

I consider ARGB8888 the "universal" pixel format because it is what most GPUs natively use. Even when an image is stored as 24-bit RGB, it is often expanded to ARGB8888 in the GPU framebuffer. The trade-off is memory — a 1920×1080 framebuffer at ARGB8888 consumes about 8.3 MB (1920 × 1080 × 4), compared to 6.2 MB for RGB888.

// JavaScript: ARGB8888 pack/unpack
function argbUnpack(color) {
  return {
    a: (color >>> 24) & 0xFF,  // >>> to avoid sign extension
    r: (color >> 16)  & 0xFF,
    g: (color >>  8)  & 0xFF,
    b:  color         & 0xFF
  };
}

function argbPack(a, r, g, b) {
  return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) |
         ((g & 0xFF) <<  8) |  (b & 0xFF);
}

Converting Between Pixel Formats

Format conversion between different pixel depths is a common task in graphics programming. The general principle is: when converting from lower depth to higher depth (e.g., RGB565 to RGB888), you scale each channel value; when converting from higher to lower (e.g., RGB888 to RGB565), you drop the least significant bits.

RGB565 to RGB888 (Upconversion)

Upconversion increases color precision by scaling each channel. The 5-bit red value (0-31) becomes 8-bit (0-255) using multiplication: r8 = (r5 * 255) / 31. With integer arithmetic, the efficient form is r8 = (r5 * 255 + 15) / 31.

RGB565 RGB888 Conversion Example
RGB565: R=20, G=45, B=10
R8 = (20 * 255 + 15) / 31 = 165
G8 = (45 * 255 + 31) / 63 = 183
B8 = (10 * 255 + 15) / 31 = 83
Result: RGB = (165, 183, 83) = approximately the same perceived color

RGB888 to RGB565 (Downconversion)

Downconversion discards the least significant bits: r5 = r8 >> 3 (keep top 5 bits), g6 = g8 >> 2 (keep top 6 bits), b5 = b8 >> 3 (keep top 5 bits). The lost bits cause quantized banding, particularly visible in smooth gradients.

RGB888 RGB565 Downconversion Example
RGB888: R=200, G=150, B=100
R5 = 200 >> 3 = 25  (was 200, now maps to 25*255/31 ≈ 205)
G6 = 150 >> 2 = 37  (was 150, now maps to 37*255/63 ≈ 149)
B5 = 100 >> 3 = 12  (was 100, now maps to 12*255/31 ≈ 98)
RGB565 packed: (25 << 11) | (37 << 5) | 12 = 0xCCAC

The downconversion errors are small (within 2-5 units per channel in my testing) but become noticeable as posterization in smooth gradients. For most embedded applications this is acceptable given the 50% memory savings versus full RGB888.

// JavaScript: convert an entire buffer from RGB565 to RGB888
function convertBufferRgb565ToRgb888(src565, width, height) {
  const dst888 = new Uint8Array(width * height * 3);
  for (let i = 0; i < src565.length; i++) {
    const p = src565[i];
    const r5 = (p >> 11) & 0x1F;
    const g6 = (p >>  5) & 0x3F;
    const b5 =  p        & 0x1F;
    dst888[i * 3]     = Math.round(r5 * 255 / 31);
    dst888[i * 3 + 1] = Math.round(g6 * 255 / 63);
    dst888[i * 3 + 2] = Math.round(b5 * 255 / 31);
  }
  return dst888;
}

When to Use Each Format

RGB332: Legacy systems or extremely memory-constrained devices (256 colors)
RGB565: Embedded displays, LCD controllers, framebuffers (best quality-per-bit ratio)
RGB888: Standard images, web graphics, print (full color precision)
ARGB8888: GPU rendering, compositing, alpha transparency (hardware-native format)

Explore More Color and Bitmap Topics

Pixel formats connect directly to color masking, bitmap operations, and ARGB manipulation. These guides cover the full picture.

Frequently Asked Questions About Pixel Format Bitwise

What is the difference between RGB332 and RGB565 pixel formats?

RGB332 uses 8 bits total: 3 bits for Red (0-7), 3 bits for Green (0-7), 2 bits for Blue (0-3). It supports 256 colors. RGB565 uses 16 bits: 5 bits for Red (0-31), 6 bits for Green (0-63), 5 bits for Blue (0-31). It supports 65,536 colors and is widely used in embedded displays and low-cost LCD controllers.

How do you extract channels from an RGB565 pixel?

To extract channels from RGB565: R = (pixel >> 11) & 0x1F, G = (pixel >> 5) & 0x3F, B = pixel & 0x1F. Each extracted value is 5 or 6 bits. To convert to 8-bit for display, scale: R8 = (R * 255) / 31, G8 = (G * 255) / 63, B8 = (B * 255) / 31.

What is RGB888 and how does it differ from ARGB8888?

RGB888 is standard 24-bit true color with 8 bits per channel (Red, Green, Blue), supporting 16.7 million colors. ARGB8888 is 32-bit with an additional 8-bit alpha channel, supporting the same color range plus transparency. The difference is one extra byte for alpha (bits 31-24) and 4-byte alignment which is more efficient for modern GPUs.

Why is RGB565 commonly used in embedded displays?

RGB565 is popular in embedded systems because it balances color quality and memory usage. At 16 bits per pixel, a 320x240 display uses only 150 KB of framebuffer memory (vs 300 KB for RGB888). The human eye is less sensitive to blue, so allocating only 5 bits to red and blue but 6 bits to green maximizes perceived quality.

How do you pack an RGB888 color into RGB565 format?

To pack RGB888 into RGB565, reduce each 8-bit channel to the appropriate bit depth using right shifts: R5 = (R >> 3) & 0x1F (top 5 bits), G6 = (G >> 2) & 0x3F (top 6 bits), B5 = (B >> 3) & 0x1F (top 5 bits). Then pack: rgb565 = (R5 << 11) | (G6 << 5) | B5.

Related Tools

Bitwise Calculator →

AND, OR, XOR, NOT, shifts

Bitwise Guide →

Complete operations reference

Hex to ASCII →

Decode hex strings

SHA256 Generator →

Compute hashes