RGB888 is a way to store the color of each pixel in three bytes: one byte each for the red, green, and blue channels. This provides 16.7 million shades without compression.
Where it is used
The standard is applied in LCD displays of smartphones, monitors, graphics cards, and in formats like BMP, TGA, and Frame Buffer. It also underpins TFT panels, HDMI (8bpc), and computer vision systems where color accuracy without information loss is critical.
Typical problems
The main problem is high memory consumption: a Full HD frame requires 6 MB, while 4K requires about 25 MB. Sequential transmission (pixel clock) requires a 24-bit bus or multiplexing. Artifacts also arise when converting to RGB565 or indexed palettes due to truncation of lower bits.
How it works
Each 8-bit channel defines intensity from 0 to 255, and a pixel is stored in memory as three consecutive bytes (R:G:B or B:G:R in little-endian). The video controller reads three bytes in one cycle and feeds them to a DAC or LVDS transmitter. Unlike RGB565 (5/6/5 bits, 65k colors, saving 33% memory), RGB888 provides smooth gradients without banding. Compared to RGB332 (3/3/2 bits, 256 colors), it requires no color palette. The difference from YUV444 is that it maintains a luminance-chrominance space — RGB888 does not separate luma and chroma, so it compresses less efficiently but is ideal for text and interfaces. Unlike 30-bit RGB101010 (professional graphics), RGB888 does not suffer from excessive bit depth and remains the standard for most consumer devices.
RGB888 functionality
- Definition and bit depth. RGB888 is a color model where each of the three channels (Red, Green, Blue) is encoded with 8 bits. This provides 256 gradations per channel, totaling 16,777,216 unique color shades. This format is called full-color or Truecolor.
- Pixel data structure. A single pixel in RGB888 format occupies exactly 24 bits, or 3 bytes. The byte order in memory can be either forward (R, G, B) or reversed (B, G, R), depending on the CPU architecture and peripheral protocol.
- Bit mask for channels. Bit masks are used to extract each channel: red channel is
0xFF0000, green is0x00FF00, blue is0x0000FF. Shifting right by 16, 8, or 0 bits scales the values to the range 0–255. - Conversion to 16-bit formats. When converting RGB888 to RGB565, the higher bits of each channel are truncated: red loses 3 lower bits, green loses 2, blue loses 3. This is done with shifts:
(R>>3) << 11 | (G>>2) << 5 | (B>>3). - Packing into a 32-bit word. For alignment to a 4-byte boundary, RGB888 is often expanded to RGBX8888 by adding a dummy byte (alpha channel or ignored byte). Packing into uint32_t looks like
(R << 16) | (G << 8) | B. - Extracting components from a word. From a 32-bit word pixel storing RGB888, the red channel is extracted as
(pixel >> 16) & 0xFF. Green is(pixel >> 8) & 0xFF, blue ispixel & 0xFF. These operations are cheap and widely used in embedded systems. - Linear brightness scaling. For software brightness adjustment in RGB888, each channel is multiplied by a coefficient k in the range 0.0–1.0. The result is saturated by taking the minimum with 255. A direct implementation is:
R = min((int)(R * k), 255). - Per-channel gamma correction. Nonlinear correction is applied individually to each byte of RGB888. A precomputed table of 256 values allows fast replacement of each channel:
R = gamma_table[R]. This is critical for rendering on sRGB displays. - Alpha blending (blending). When overlaying two layers in RGB888, an alpha channel A (0–255) is used. The formula for each channel is:
out = (fg * A + bg * (255 - A)) / 255. Division is implemented via a shift by replacing 255 with 256 and rounding. - Byte-wise buffer filling. Hardware acceleration often requires byte-wise writing to the RGB888 framebuffer. The buffer pointer is cast to uint8_t*, and then R, G, B are written sequentially for each pixel. This is the simplest driver for TFT displays.
- Byte order specifics in LVGL and emWin. Graphics libraries for embedded systems working with RGB888 support byte order swapping via macros like
LV_COLOR_16_SWAP. An incorrect order leads to inversion of red and blue channels, visually noticeable as a cyan-magenta tint. - Use in DMA2D (Chrom-ART). DMA2D controllers in STM32 microcontrollers work well with RGB888 as a source format. Pixel format configuration registers, such as
DMA2D_PF_RGB888, specify the line length in bytes — strictly width * 3. No alignment is required. - PF (Hardware virtualization of Input-Output devices)
- Memory saving with clustering. If full color is not needed, RGB888 is replaced by an indexed format with a palette. However, directly storing three bytes per pixel simplifies code and eliminates color lookup tables. The cost is high RAM consumption: for VGA (640×480), this is 921 KB.
- VGA (Emulation of legacy VGA register interface)
- Real-time gradient creation. A linear gradient between two RGB888 colors is computed by interpolating each channel using the formula
C = C1 + (C2 - C1) * t, where t ranges from 0 to 1. Smoothness is higher than in 16-bit formats due to the absence of posterization. - Conversion from YCbCr to RGB888. In video decoders, a signal from YCbCr format (e.g., 4:2:2) is recalculated to RGB888 via matrix multiplication with saturation. Standard ITU-R BT.601 coefficients yield lossless conversion to 24-bit space but require fixed-point arithmetic.
- Hardware support in MIPI DSI. The MIPI DSI interface, when sending command
0x3C(Write Memory Continue), transmits RGB888 pixels as three unpacked bytes. The display controller interprets the stream itself if configured for pixel format0x06— 24 bits per pixel. - Line alignment in the framebuffer. To optimize memory access, the line length in RGB888 may be padded to a multiple of 4 bytes. For example, at a width of 1024 pixels, the natural line length is 3072 bytes, which is already a multiple of 4. But at a width of 1023 bytes, one padding byte is added.
- Encoding using a union in C. For convenient byte manipulation of RGB888 in C, a union is used:
union { uint32_t word; struct { uint8_t b,g,r; }; } pixel;
This allows changing channels directly, avoiding bitwise operations, but requires caution with byte order. - Pipeline processing with SIMD. Vector instructions (NEON, SSE) process RGB888 as 16 packed pixels per cycle. For example, the
vld3_u8instruction in ARM NEON unpacks three channels into separate registers, allowing parallel gamma or brightness adjustments. - Precision color correction with 3×3 matrices. For color calibration, the RGB888 space is transformed by multiplying the (R,G,B) vector by a 3×3 fixed-point matrix (e.g., 1/256). The result is clipped to the 0–255 range. This is the basis of hardware color correctors in display drivers.
- Limitations and overflow. When performing arithmetic on RGB888, it is important to saturate results after multiplications and additions. Without saturation, bright tones cause spurious wraparound modulo 256, turning white into dark gray. Use a clamp(value, 0, 255) function or the built-in
__USATin ARM.
Comparison with similar features
- RGB888 vs RGB565. RGB888 allocates 8 bits per channel (24 bits total), providing 16.7 million colors, whereas RGB565 uses 5 bits for red, 6 for green, and 5 for blue (16 bits). This yields only 65,536 colors, saving memory but with noticeable color transitions and fewer shades of green.
- RGB888 vs RGB332. RGB332 uses 3 bits for red, 3 for green, and 2 for blue (8 bits), supporting only 256 colors. Unlike full-color RGB888, RGB332 is only suitable for simple graphics or indicators where compactness matters. The loss of color accuracy is catastrophic for photorealism.
- RGB888 vs ARGB8888. ARGB8888 adds an alpha transparency channel to the 24 RGB bits, occupying 32 bits per pixel. RGB888 does not support transparency, limiting its use in interfaces and games. ARGB8888 requires more memory and bandwidth but is critical for layers, overlays, and anti-aliasing.
- RGB888 vs YUV444. YUV444 encodes luminance (Y) and chrominance (UV) at full resolution, often used in video. RGB888 directly represents physical colors for displays. YUV444 is better for compression and compatibility with human vision, but RGB888 is simpler for rendering without color space conversion.
- RGB888 vs Indexed Color (palettes). Indexed stores an 8-bit index into a palette of 256 colors chosen from RGB888. This saves memory but fixes the colors. RGB888 allows any pixel to have any color independently. Indexed is good for old hardware or stylized graphics; RGB888 is mandatory for photographs and gradients.
OS and driver support
RGB888 is implemented via framebuffer drivers (Linux DRM/KMS, Windows DirectX/Display Miniport) and user-space libraries (SDL, Wayland, X11), where each channel is encoded with 8 bits, providing 16.7 million colors without a palette. The OS must support 24-bit packed pixel or 32-bit with alignment (XRGB8888), and the driver must correctly map video memory to linear or interlaced format.
Security
RGB888 vulnerabilities are associated with buffer overflows due to improper handling of row stride. Therefore, drivers apply frame size validation via IOCTL, and in user space, boundary checking is used when converting from compressed formats (JPEG, PNG). Additionally, access to the framebuffer is isolated through SELinux or Integrity Service Access Control (ISAC) to prevent screenshot trojans.
Logging
Driver logs record RGB888 errors: DPI interface synchronization violations, incorrect byte order (BGR888 vs RGB888), video memory write timeouts via ftrace or ETW. Application-level logging (via syslog, journald, WinDbg) records video mode switches, surface allocations (e.g., allocated 1920x1080x3 bytes), and warnings about unaligned pixel accesses.
Limitations
RGB888 requires 3 bytes per pixel, increasing memory bandwidth by 1.5x compared to RGB565 and limiting the maximum resolution at a fixed interface bitrate (e.g., MIPI DSI — maximum 1080p@60 Hz without compression). It also does not support hardware alpha channel blending without an additional layer (RGBA8888), and the absence of a palette complicates hardware acceleration for indexed sprites.
History and development
The format originated in the 1980s with VGA adapters (24-bit TrueColor) and became the standard for X11 and Windows 95/98. Development moved toward a compromise with RGB565 for embedded systems, then to extensions such as RGB888 with alpha channel (ARGB8888) and DXT/BC compression for GPUs. In modern interfaces (DisplayPort 2.0, HDMI 2.1), RGB888 is being replaced by 10-bit color (RGB1010102) due to HDR requirements.