Skip to main content
RDP supports multiple bitmap compression codecs to optimize graphics performance over varying network conditions. IronRDP implements the most widely-used codecs, prioritizing security and correctness.

Supported Codecs

IronRDP supports four primary bitmap codecs:
CodecRDP VersionDescriptionCompression
UncompressedAllRaw RGB bitmap dataNone
RLE4.0+Interleaved Run-Length EncodingLossless
RDP 6.06.0+Bitmap CompressionLossless
RemoteFX (RFX)7.0+Advanced codec using wavelet transformLossy/Lossless
From README.md:13-18.

Codec Negotiation

Codecs are negotiated during capabilities exchange via the Bitmap Capability Set.

Client Advertisement

// From ironrdp-connector
use ironrdp_pdu::rdp::capability_sets::BitmapCodecs;

let bitmap_config = BitmapConfig {
    lossy_compression: true,
    color_depth: 32,
    codecs: BitmapCodecs {
        codecs: vec![
            Codec::RemoteFx(RemoteFxContainer { /* ... */ }),
        ],
    },
};
The client specifies:
  • Preferred color depth: 15, 16, 24, or 32 bits per pixel
  • Supported codecs: List of codec GUIDs and capabilities
  • Lossy compression: Whether lossy codecs are acceptable

Server Response

Server selects codecs from client’s advertised list and includes them in the Demand Active PDU.
// Server's Bitmap Capability Set response
let server_bitmaps = BitmapCapabilitySet {
    pref_bits_per_pix: 32,
    desktop_width: 1920,
    desktop_height: 1080,
    bitmap_compression: true,
    // ...
};
See ironrdp-pdu/src/rdp/capability_sets/bitmap.rs.

Uncompressed Bitmaps

Raw bitmap data sent without compression.

Format

  • Pixel Format: RGB triplets or RGBX quadruplets
  • Byte Order: Platform-dependent (typically little-endian)
  • Scanline Order: Bottom-to-top (standard bitmap orientation)

Use Cases

  • Very simple images where compression overhead exceeds benefit
  • Regions smaller than ~100 pixels
  • Already-compressed image data (e.g., screenshots of compressed content)

Processing

No decompression needed — directly blit to framebuffer:
fn process_uncompressed_bitmap(
    data: &[u8],
    width: usize,
    height: usize,
    bpp: usize,
    framebuffer: &mut [u8],
) {
    let bytes_per_pixel = bpp / 8;
    let stride = width * bytes_per_pixel;
    
    for y in 0..height {
        let src_offset = y * stride;
        let dst_offset = (height - 1 - y) * stride;  // Flip Y
        framebuffer[dst_offset..dst_offset + stride]
            .copy_from_slice(&data[src_offset..src_offset + stride]);
    }
}

RLE (Run-Length Encoding)

Interleaved RLE provides lossless compression for bitmap data.

Algorithm

RLE encodes consecutive identical pixels using run-length codes:
  • Literal runs: Verbatim pixel data (1-255 pixels)
  • Repeat runs: Single color repeated N times
  • Special codes: Background color, foreground color, mixed runs

Code Structure

// From ironrdp-graphics/src/rle.rs
pub struct Code(u8);

impl Code {
    pub const REGULAR_BG_RUN: Self = Self(0x00);
    pub const REGULAR_FG_RUN: Self = Self(0x01);
    pub const DITHERED_RUN: Self = Self(0x02);
    pub const MEGA_MEGA_BG_RUN: Self = Self(0xF0);
    // ... more codes
}
See ironrdp-graphics/src/rle.rs for full implementation.

Decompression

use ironrdp_graphics::rle::decompress;

let decompressed = decompress(
    compressed_data,
    width,
    height,
    bytes_per_pixel,
)?;
The decompressor maintains:
  • Foreground color: Current pen color
  • Background color: Current background color
  • Output buffer: Reconstructed bitmap

Performance

RLE excels at:
  • Text and UI elements (large solid color regions)
  • Simple graphics with few color transitions
  • Low-color-depth images
RLE struggles with:
  • Photographs and gradients
  • High-frequency color changes
  • Noisy or dithered images

RDP 6.0 Bitmap Compression

RDP 6.0 introduces an improved lossless codec.

Implementation

Implemented in ironrdp-graphics/src/rdp6/:
pub mod rdp6 {
    pub mod bitmap_stream;
    pub mod rle;
}

Decoder

use ironrdp_graphics::rdp6::bitmap_stream::decoder::decode;

let decoded = decode(
    compressed_data,
    output_buffer,
    width,
    height,
)?;
See ironrdp-graphics/src/rdp6/bitmap_stream/decoder.rs.

Encoder

IronRDP also provides an encoder for server-side implementations:
use ironrdp_graphics::rdp6::bitmap_stream::encoder::encode;

let compressed = encode(
    bitmap_data,
    width,
    height,
)?;
See ironrdp-graphics/src/rdp6/bitmap_stream/encoder.rs.

Algorithm Details

RDP 6.0 codec uses:
  • Delta encoding: Encodes differences from previous scanline
  • Color caching: Reuses frequently-occurring colors
  • Advanced RLE: Extended run-length codes

Use Cases

  • General-purpose lossless compression
  • Better than classic RLE for complex images
  • Still efficient for text and UI

RemoteFX (RFX)

RemoteFX is RDP’s advanced codec using wavelet-based compression.

Architecture

RemoteFX processing pipeline:
1
Color Space Conversion
2
Convert RGB to YCbCr:
3
use ironrdp_graphics::color_conversion;

// RGB to YUV conversion
let yuv = color_conversion::rgb_to_ycbcr(rgb_data);
4
See ironrdp-graphics/src/color_conversion.rs.
5
Discrete Wavelet Transform (DWT)
6
Apply 2D wavelet transform to decompose image into frequency subbands:
7
use ironrdp_graphics::dwt;

// Encode: Forward DWT
let mut coefficients = [0i16; 64 * 64];
dwt::encode(input_data, &mut coefficients);
8
IronRDP uses integer-based DWT for performance and precision.
9
See ironrdp-graphics/src/dwt.rs.
10
Quantization
11
Reduce precision of wavelet coefficients:
12
use ironrdp_graphics::quantization;
use ironrdp_pdu::codecs::rfx::Quant;

// Apply quantization
let quant = Quant { /* LL, LH, HL, HH factors */ };
quantization::encode(&mut coefficients, &quant);
13
Quantization introduces controlled loss, trading quality for compression.
14
See ironrdp-graphics/src/quantization.rs.
15
Subband Reconstruction
16
Reorder coefficients for better compression:
17
use ironrdp_graphics::subband_reconstruction;

// Encode differential values
subband_reconstruction::encode(&mut coefficients[4032..]);
18
See ironrdp-graphics/src/subband_reconstruction.rs.
19
RLGR Entropy Encoding
20
Compress coefficients using Run-Length Golomb-Rice (RLGR):
21
use ironrdp_graphics::rlgr;
use ironrdp_pdu::codecs::rfx::EntropyAlgorithm;

let compressed_len = rlgr::encode(
    EntropyAlgorithm::Rlgr3,
    &coefficients,
    output_buffer,
)?;
22
RLGR is optimized for wavelet coefficient distributions.
23
See ironrdp-graphics/src/rlgr.rs.

Complete RFX Encoding

use ironrdp_graphics::rfx_encode_component;
use ironrdp_pdu::codecs::rfx::{Quant, EntropyAlgorithm};

let mut input = [0i16; 64 * 64];  // Y, Cb, or Cr component
let mut output = vec![0u8; 16384];

let quant = Quant {
    ll3: 6, lh3: 6, hl3: 6, hh3: 6,
    lh2: 7, hl2: 7, hh2: 7,
    lh1: 8, hl1: 8, hh1: 8,
};

let compressed_size = rfx_encode_component(
    &mut input,
    &mut output,
    &quant,
    EntropyAlgorithm::Rlgr3,
)?;

let compressed_data = &output[..compressed_size];
See ironrdp-graphics/src/lib.rs:23-37.

RFX Tile Structure

RemoteFX divides the screen into 64×64 pixel tiles. Each tile is encoded independently:
// Pseudocode for RFX stream structure
RFX Stream {
    Sync PDU
    Codec Versions PDU
    Channels PDU
    Context PDU
    
    for each frame {
        Frame Begin PDU
        
        for each region {
            Region PDU
            
            for each tile {
                Tile Data (Y, Cb, Cr components)
            }
        }
        
        Frame End PDU
    }
}
See ironrdp-pdu/src/codecs/rfx/ for PDU structures.

Quantization Factors

Quantization quality is controlled by the Quant structure:
pub struct Quant {
    pub ll3: u8,  // Lowest frequency (LL)
    pub lh3: u8, pub hl3: u8, pub hh3: u8,
    pub lh2: u8, pub hl2: u8, pub hh2: u8,
    pub lh1: u8, pub hl1: u8, pub hh1: u8,  // Highest frequency (HH)
}
Higher values = more quantization = lower quality + better compression. Typical values:
  • Lossless: All factors = 6
  • High quality: 6-8 range
  • Medium quality: 8-10 range
  • Low quality: 10-12 range

Entropy Algorithms

pub enum EntropyAlgorithm {
    Rlgr1,  // Original RLGR
    Rlgr3,  // Improved RLGR (default)
}
RLGR3 provides better compression and is recommended.

Use Cases

RemoteFX excels at:
  • High-resolution displays: 1920×1080 and above
  • Complex graphics: Photographs, video frames
  • Acceptable quality loss: Slight artifacts OK for performance
  • Hardware acceleration: GPU-accelerated encoding/decoding
RemoteFX limitations:
  • Requires RDP 7.0+ (Windows 7/Server 2008 R2 or later)
  • Higher CPU cost than RLE
  • Lossy compression (though minimal at high quality settings)

Enabling RemoteFX on Server

Windows Server configuration:
# Enable 32-bit color
Set-ItemProperty -Path 'HKLM:\Software\Policies\Microsoft\Windows NT\Terminal Services' `
    -Name 'ColorDepth' -Type DWORD -Value 5

# Enable RemoteFX
Set-ItemProperty -Path 'HKLM:\Software\Policies\Microsoft\Windows NT\Terminal Services' `
    -Name 'fEnableVirtualizedGraphics' -Type DWORD -Value 1

# Reboot required
Restart-Computer
Or via Group Policy (gpedit.msc):
  1. Computer Configuration → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Remote Session Environment
  2. Enable “RemoteFX for Windows Server 2008 R2”
  3. Enable “Enable RemoteFX encoding for RemoteFX clients”
  4. Enable “Limit maximum color depth” → Set to 32-bit
  5. Reboot
See README.md:46-65.

Additional Graphics Utilities

Image Processing

Utilities for bitmap manipulation:
use ironrdp_graphics::image_processing;

// Convert between pixel formats, flip, etc.
See ironrdp-graphics/src/image_processing.rs.

Rectangle Processing

Optimizations for rectangular bitmap operations:
use ironrdp_graphics::rectangle_processing;

// Clipping, intersection, union operations
See ironrdp-graphics/src/rectangle_processing.rs.

Pointer (Cursor) Handling

use ironrdp_graphics::pointer;

// Decode RDP pointer shapes (color, monochrome)
See ironrdp-graphics/src/pointer.rs.

Differential Encoding

use ironrdp_graphics::diff;

// Compute and apply delta updates
See ironrdp-graphics/src/diff.rs.

Bulk Compression (ZGFX)

RDP also supports bulk compression of PDU payloads using ZGFX (not a graphics codec).

ZGFX Algorithm

ZGFX is a variant of DEFLATE optimized for RDP:
use ironrdp_graphics::zgfx::ZgfxCompressor;

let mut compressor = ZgfxCompressor::new();
let compressed = compressor.compress(data)?;
See ironrdp-graphics/src/zgfx/.

Bulk vs. Bitmap Compression

  • Bulk compression: Compresses entire PDUs (any data type)
  • Bitmap compression: Codec-specific to bitmap data
They are orthogonal — bulk compression can be applied to compressed bitmaps.

Configuration

Bulk compression is negotiated separately:
let config = Config {
    compression_type: Some(CompressionType::Rdp61),  // XCRUSH (RDP 6.1)
    // or Some(CompressionType::Rdp6) for NCRUSH
    // or None for no bulk compression
    // ...
};
See Protocol Overview for bulk compression details.

Codec Selection Strategy

Choose codecs based on use case:
ScenarioRecommended CodecRationale
LAN / High BandwidthUncompressed or RDP 6.0Minimize CPU, maximize quality
WAN / Low BandwidthRemoteFX (medium quality)Best compression ratio
Text / UI heavyRLE or RDP 6.0Lossless, efficient for solid colors
Photography / VideoRemoteFX (high quality)Handles complex imagery well
Legacy clientsRLEBroadest compatibility
Server-side renderingRemoteFX + GPU accelerationOffload CPU to GPU

Performance Considerations

Optimization Tips:
  • Profile your use case: Measure actual compression ratios and CPU usage
  • Tune quantization: Balance quality vs. bandwidth for RemoteFX
  • Enable hardware acceleration: Use GPU for RFX encoding/decoding when available
  • Monitor network conditions: Dynamically adjust codec quality
  • Leverage caching: RDP supports various caching mechanisms to reduce retransmissions
  • Consider tile size: 64×64 is optimal for most cases, but adjustable

Error Handling

Codec operations return Result types:
pub type DecodeResult<T> = Result<T, DecodeError>;

impl From<rlgr::RlgrError> for DecodeError {
    // Codec-specific error conversion
}
Handle errors gracefully:
match decompress_bitmap(data, width, height) {
    Ok(bitmap) => render(bitmap),
    Err(e) => {
        warn!("Bitmap decompression failed: {}", e);
        // Fallback: request retransmission or show placeholder
    }
}

Testing Codecs

IronRDP includes extensive codec tests:
# Run graphics crate tests
cargo test -p ironrdp-graphics

# Run RFX-specific tests
cargo test -p ironrdp-graphics --lib rfx

# Fuzz codecs for robustness
cargo xtask fuzz run -v
See crates/ironrdp-graphics/ for test fixtures.

Future Codecs

RDP 10+ introduces new codecs:
  • AVC/H.264: Video codec support
  • HEVC/H.265: Next-gen video compression
  • AV1: Modern, royalty-free codec
These are planned for future IronRDP releases.

References

  • Graphics Crate: crates/ironrdp-graphics/
  • RFX PDU Structures: crates/ironrdp-pdu/src/codecs/rfx/
  • Bitmap Capability Sets: crates/ironrdp-pdu/src/rdp/capability_sets/bitmap.rs
  • MS-RDPRFX: RemoteFX Codec specification
  • MS-RDPEGDI: Graphics Device Interface extension
  • MS-RDPBCGR § 3.1.9: Bitmap compression algorithms

Build docs developers (and LLMs) love