rustrtc 0.3.139

A high-performance real-time communication library — WebRTC, RTP/SRTP, T.38 Fax, and RTP Latching
Documentation
//! T.4 1-D (MH) page encoder.
//!
//! Emits EOL-prefixed MH rows (MSB-first bits packed into bytes) with an
//! optional RTC. The code tables mirror ITU-T T.4 Table 3/T.4 Annex and are
//! the same data the reference replay decoder uses.

const WHITE_CODES: &[(u8, u16, u16)] = &[
    (8, 53, 0),
    (6, 7, 1),
    (4, 7, 2),
    (4, 8, 3),
    (4, 11, 4),
    (4, 12, 5),
    (4, 14, 6),
    (4, 15, 7),
    (5, 19, 8),
    (5, 20, 9),
    (5, 7, 10),
    (5, 8, 11),
    (6, 8, 12),
    (6, 3, 13),
    (6, 52, 14),
    (6, 53, 15),
    (6, 42, 16),
    (6, 43, 17),
    (7, 39, 18),
    (7, 12, 19),
    (7, 8, 20),
    (7, 23, 21),
    (7, 3, 22),
    (7, 4, 23),
    (7, 40, 24),
    (7, 43, 25),
    (7, 19, 26),
    (7, 36, 27),
    (7, 24, 28),
    (8, 2, 29),
    (8, 3, 30),
    (8, 26, 31),
    (8, 27, 32),
    (8, 18, 33),
    (8, 19, 34),
    (8, 20, 35),
    (8, 21, 36),
    (8, 22, 37),
    (8, 23, 38),
    (8, 40, 39),
    (8, 41, 40),
    (8, 42, 41),
    (8, 43, 42),
    (8, 44, 43),
    (8, 45, 44),
    (8, 4, 45),
    (8, 5, 46),
    (8, 10, 47),
    (8, 11, 48),
    (8, 82, 49),
    (8, 83, 50),
    (8, 84, 51),
    (8, 85, 52),
    (8, 36, 53),
    (8, 37, 54),
    (8, 88, 55),
    (8, 89, 56),
    (8, 90, 57),
    (8, 91, 58),
    (8, 74, 59),
    (8, 75, 60),
    (8, 50, 61),
    (8, 51, 62),
    (8, 52, 63),
    (5, 27, 64),
    (5, 18, 128),
    (6, 23, 192),
    (7, 55, 256),
    (8, 54, 320),
    (8, 55, 384),
    (8, 100, 448),
    (8, 101, 512),
    (8, 104, 576),
    (8, 103, 640),
    (9, 204, 704),
    (9, 205, 768),
    (9, 210, 832),
    (9, 211, 896),
    (9, 212, 960),
    (9, 213, 1024),
    (9, 214, 1088),
    (9, 215, 1152),
    (9, 216, 1216),
    (9, 217, 1280),
    (9, 218, 1344),
    (9, 219, 1408),
    (9, 152, 1472),
    (9, 153, 1536),
    (9, 154, 1600),
    (6, 24, 1664),
    (9, 155, 1728),
    (11, 8, 1792),
    (11, 12, 1856),
    (11, 13, 1920),
    (12, 18, 1984),
    (12, 19, 2048),
    (12, 20, 2112),
    (12, 21, 2176),
    (12, 22, 2240),
    (12, 23, 2304),
    (12, 28, 2368),
    (12, 29, 2432),
    (12, 30, 2496),
    (12, 31, 2560),
];

const BLACK_CODES: &[(u8, u16, u16)] = &[
    (10, 55, 0),
    (3, 2, 1),
    (2, 3, 2),
    (2, 2, 3),
    (3, 3, 4),
    (4, 3, 5),
    (4, 2, 6),
    (5, 3, 7),
    (6, 5, 8),
    (6, 4, 9),
    (7, 4, 10),
    (7, 5, 11),
    (7, 7, 12),
    (8, 4, 13),
    (8, 7, 14),
    (9, 24, 15),
    (10, 23, 16),
    (10, 24, 17),
    (10, 8, 18),
    (11, 103, 19),
    (11, 104, 20),
    (11, 108, 21),
    (11, 55, 22),
    (11, 40, 23),
    (11, 23, 24),
    (11, 24, 25),
    (12, 202, 26),
    (12, 203, 27),
    (12, 204, 28),
    (12, 205, 29),
    (12, 104, 30),
    (12, 105, 31),
    (12, 106, 32),
    (12, 107, 33),
    (12, 210, 34),
    (12, 211, 35),
    (12, 212, 36),
    (12, 213, 37),
    (12, 214, 38),
    (12, 215, 39),
    (12, 108, 40),
    (12, 109, 41),
    (12, 218, 42),
    (12, 219, 43),
    (12, 84, 44),
    (12, 85, 45),
    (12, 86, 46),
    (12, 87, 47),
    (12, 100, 48),
    (12, 101, 49),
    (12, 82, 50),
    (12, 83, 51),
    (12, 36, 52),
    (12, 55, 53),
    (12, 56, 54),
    (12, 39, 55),
    (12, 40, 56),
    (12, 88, 57),
    (12, 89, 58),
    (12, 43, 59),
    (12, 44, 60),
    (12, 90, 61),
    (12, 102, 62),
    (12, 103, 63),
    (10, 15, 64),
    (12, 200, 128),
    (12, 201, 192),
    (12, 91, 256),
    (12, 51, 320),
    (12, 52, 384),
    (12, 53, 448),
    (13, 108, 512),
    (13, 109, 576),
    (13, 74, 640),
    (13, 75, 704),
    (13, 76, 768),
    (13, 77, 832),
    (13, 114, 896),
    (13, 115, 960),
    (13, 116, 1024),
    (13, 117, 1088),
    (13, 118, 1152),
    (13, 119, 1216),
    (13, 82, 1280),
    (13, 83, 1344),
    (13, 84, 1408),
    (13, 85, 1472),
    (13, 90, 1536),
    (13, 91, 1600),
    (13, 100, 1664),
    (13, 101, 1728),
    (11, 8, 1792),
    (11, 12, 1856),
    (11, 13, 1920),
    (12, 18, 1984),
    (12, 19, 2048),
    (12, 20, 2112),
    (12, 21, 2176),
    (12, 22, 2240),
    (12, 23, 2304),
    (12, 28, 2368),
    (12, 29, 2432),
    (12, 30, 2496),
    (12, 31, 2560),
];

fn push_code(bits: &mut Vec<u8>, color: bool, run: u32) {
    let table: &[(u8, u16, u16)] = if color { WHITE_CODES } else { BLACK_CODES };
    let mut remaining = run;
    let makeup: Vec<(u8, u16, u16)> = table.iter().filter(|e| e.2 >= 64).rev().copied().collect();
    for &(len, code, r) in &makeup {
        while remaining >= u32::from(r) {
            for i in (0..len).rev() {
                bits.push(((code >> i) & 1) as u8);
            }
            remaining -= u32::from(r);
        }
    }
    let Some(&(len, code, _)) = table
        .iter()
        .find(|e| u32::from(e.2) == remaining && e.2 < 64)
    else {
        return;
    };
    for i in (0..len).rev() {
        bits.push(((code >> i) & 1) as u8);
    }
}

/// Encode a bilevel page as T.4 1-D (MH). `rows` are 0 = white, non-0 = black.
/// Emits an EOL before each row and an RTC after the last row.
pub fn encode_mh_page(rows: &[Vec<u8>], width: usize) -> Vec<u8> {
    let mut bits = Vec::new();
    for row in rows {
        debug_assert_eq!(row.len(), width);
        bits.extend([0u8; 11]);
        bits.push(1);
        bits.push(1);
        let mut color = false;
        let mut pos = 0usize;
        let changes: Vec<(usize, u8)> = {
            let mut out = Vec::new();
            let mut cur = 0u8;
            for (i, &px) in row.iter().enumerate() {
                if px != cur {
                    out.push((i, px));
                    cur = px;
                }
            }
            out
        };
        for &(px, c) in &changes {
            push_code(&mut bits, !color, (px - pos) as u32);
            pos = px;
            color = c != 0;
        }
        push_code(&mut bits, !color, (width - pos) as u32);
    }
    for _ in 0..6 {
        bits.extend([0u8; 11]);
        bits.push(1);
        bits.push(1);
    }
    while bits.len() % 8 != 0 {
        bits.push(0);
    }
    bits.chunks(8)
        .map(|c| c.iter().fold(0u8, |a, &b| (a << 1) | b))
        .collect()
}

/// Encode without the trailing RTC (some receivers choke on a bare RTC).
pub fn encode_mh_page_no_rtc(rows: &[Vec<u8>], width: usize) -> Vec<u8> {
    let full = encode_mh_page(rows, width);
    let end = full.len() - 9;
    full[..end].to_vec()
}