oxvif 0.9.7

Async Rust client library for the ONVIF IP camera protocol
Documentation
//! 8×8 monospace bitmap font for snapshot OSD overlay.
//!
//! IBM CGA-style printable ASCII (`0x20`..=`0x7E`). Each glyph is 8
//! rows × 8 bits, MSB = leftmost pixel. The MSB-leftmost convention
//! lets us blit a row by shifting `(byte >> (7 - col)) & 1` once per
//! pixel, which is what `draw_text` does.
//!
//! Public-domain glyph data — same shapes everyone uses (Cosmac VIP,
//! original PC BIOS, RISC OS bootroms). Embedded directly so the
//! mock has zero font-rendering dependencies.

pub const CHAR_W: usize = 8;
pub const CHAR_H: usize = 8;

const FIRST_CHAR: u8 = 0x20;
const LAST_CHAR: u8 = 0x7E;

#[rustfmt::skip]
const GLYPHS: [[u8; CHAR_H]; (LAST_CHAR - FIRST_CHAR + 1) as usize] = [
    // 0x20 ' '
    [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
    // 0x21 '!'
    [0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00],
    // 0x22 '"'
    [0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00],
    // 0x23 '#'
    [0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00],
    // 0x24 '$'
    [0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00],
    // 0x25 '%'
    [0x00,0xC6,0xCC,0x18,0x30,0x66,0xC6,0x00],
    // 0x26 '&'
    [0x38,0x6C,0x38,0x76,0xDC,0xCC,0x76,0x00],
    // 0x27 '''
    [0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00],
    // 0x28 '('
    [0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00],
    // 0x29 ')'
    [0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00],
    // 0x2A '*'
    [0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00],
    // 0x2B '+'
    [0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00],
    // 0x2C ','
    [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30],
    // 0x2D '-'
    [0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00],
    // 0x2E '.'
    [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00],
    // 0x2F '/'
    [0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00],
    // 0x30 '0'
    [0x7C,0xC6,0xCE,0xDE,0xF6,0xE6,0x7C,0x00],
    // 0x31 '1'
    [0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00],
    // 0x32 '2'
    [0x7C,0xC6,0x06,0x1C,0x30,0x66,0xFE,0x00],
    // 0x33 '3'
    [0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00],
    // 0x34 '4'
    [0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x1E,0x00],
    // 0x35 '5'
    [0xFE,0xC0,0xC0,0xFC,0x06,0xC6,0x7C,0x00],
    // 0x36 '6'
    [0x38,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00],
    // 0x37 '7'
    [0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00],
    // 0x38 '8'
    [0x7C,0xC6,0xC6,0x7C,0xC6,0xC6,0x7C,0x00],
    // 0x39 '9'
    [0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00],
    // 0x3A ':'
    [0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00],
    // 0x3B ';'
    [0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30],
    // 0x3C '<'
    [0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00],
    // 0x3D '='
    [0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00],
    // 0x3E '>'
    [0x60,0x30,0x18,0x0C,0x18,0x30,0x60,0x00],
    // 0x3F '?'
    [0x7C,0xC6,0x0C,0x18,0x18,0x00,0x18,0x00],
    // 0x40 '@'
    [0x7C,0xC6,0xDE,0xDE,0xDE,0xC0,0x78,0x00],
    // 0x41 'A'
    [0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00],
    // 0x42 'B'
    [0xFC,0x66,0x66,0x7C,0x66,0x66,0xFC,0x00],
    // 0x43 'C'
    [0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00],
    // 0x44 'D'
    [0xF8,0x6C,0x66,0x66,0x66,0x6C,0xF8,0x00],
    // 0x45 'E'
    [0xFE,0x62,0x68,0x78,0x68,0x62,0xFE,0x00],
    // 0x46 'F'
    [0xFE,0x62,0x68,0x78,0x68,0x60,0xF0,0x00],
    // 0x47 'G'
    [0x3C,0x66,0xC0,0xC0,0xCE,0x66,0x3E,0x00],
    // 0x48 'H'
    [0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00],
    // 0x49 'I'
    [0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00],
    // 0x4A 'J'
    [0x1E,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0x00],
    // 0x4B 'K'
    [0xE6,0x66,0x6C,0x78,0x6C,0x66,0xE6,0x00],
    // 0x4C 'L'
    [0xF0,0x60,0x60,0x60,0x62,0x66,0xFE,0x00],
    // 0x4D 'M'
    [0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00],
    // 0x4E 'N'
    [0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0x00],
    // 0x4F 'O'
    [0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00],
    // 0x50 'P'
    [0xFC,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00],
    // 0x51 'Q'
    [0x7C,0xC6,0xC6,0xC6,0xD6,0x7C,0x0E,0x00],
    // 0x52 'R'
    [0xFC,0x66,0x66,0x7C,0x6C,0x66,0xE6,0x00],
    // 0x53 'S'
    [0x7C,0xC6,0xE0,0x78,0x0E,0xC6,0x7C,0x00],
    // 0x54 'T'
    [0x7E,0x7E,0x5A,0x18,0x18,0x18,0x3C,0x00],
    // 0x55 'U'
    [0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00],
    // 0x56 'V'
    [0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00],
    // 0x57 'W'
    [0xC6,0xC6,0xC6,0xD6,0xFE,0xEE,0xC6,0x00],
    // 0x58 'X'
    [0xC6,0xC6,0x6C,0x38,0x6C,0xC6,0xC6,0x00],
    // 0x59 'Y'
    [0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00],
    // 0x5A 'Z'
    [0xFE,0xC6,0x8C,0x18,0x32,0x66,0xFE,0x00],
    // 0x5B '['
    [0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00],
    // 0x5C '\'
    [0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00],
    // 0x5D ']'
    [0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00],
    // 0x5E '^'
    [0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00],
    // 0x5F '_'
    [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF],
    // 0x60 '`'
    [0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00],
    // 0x61 'a'
    [0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00],
    // 0x62 'b'
    [0xE0,0x60,0x7C,0x66,0x66,0x66,0xDC,0x00],
    // 0x63 'c'
    [0x00,0x00,0x7C,0xC6,0xC0,0xC6,0x7C,0x00],
    // 0x64 'd'
    [0x1C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00],
    // 0x65 'e'
    [0x00,0x00,0x7C,0xC6,0xFE,0xC0,0x7C,0x00],
    // 0x66 'f'
    [0x3C,0x66,0x60,0xF8,0x60,0x60,0xF0,0x00],
    // 0x67 'g'
    [0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0xF8],
    // 0x68 'h'
    [0xE0,0x60,0x6C,0x76,0x66,0x66,0xE6,0x00],
    // 0x69 'i'
    [0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00],
    // 0x6A 'j'
    [0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C],
    // 0x6B 'k'
    [0xE0,0x60,0x66,0x6C,0x78,0x6C,0xE6,0x00],
    // 0x6C 'l'
    [0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00],
    // 0x6D 'm'
    [0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0x00],
    // 0x6E 'n'
    [0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x00],
    // 0x6F 'o'
    [0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0x00],
    // 0x70 'p'
    [0x00,0x00,0xDC,0x66,0x66,0x7C,0x60,0xF0],
    // 0x71 'q'
    [0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0x1E],
    // 0x72 'r'
    [0x00,0x00,0xDC,0x76,0x60,0x60,0xF0,0x00],
    // 0x73 's'
    [0x00,0x00,0x7E,0xC0,0x7C,0x06,0xFC,0x00],
    // 0x74 't'
    [0x30,0x30,0xFC,0x30,0x30,0x36,0x1C,0x00],
    // 0x75 'u'
    [0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x76,0x00],
    // 0x76 'v'
    [0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x38,0x00],
    // 0x77 'w'
    [0x00,0x00,0xC6,0xD6,0xD6,0xFE,0x6C,0x00],
    // 0x78 'x'
    [0x00,0x00,0xC6,0x6C,0x38,0x6C,0xC6,0x00],
    // 0x79 'y'
    [0x00,0x00,0xC6,0xC6,0xC6,0x7E,0x06,0xFC],
    // 0x7A 'z'
    [0x00,0x00,0xFE,0x4C,0x18,0x32,0xFE,0x00],
    // 0x7B '{'
    [0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00],
    // 0x7C '|'
    [0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00],
    // 0x7D '}'
    [0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00],
    // 0x7E '~'
    [0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00],
];

/// Glyph rows for `c`. Out-of-range chars (control codes, non-ASCII)
/// fall through to the space glyph rather than panic — they just
/// look like a gap in the rendered text.
pub fn glyph(c: char) -> [u8; CHAR_H] {
    let n = c as u32;
    if (FIRST_CHAR as u32..=LAST_CHAR as u32).contains(&n) {
        GLYPHS[(n - FIRST_CHAR as u32) as usize]
    } else {
        GLYPHS[0]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn space_glyph_is_blank() {
        assert_eq!(glyph(' '), [0u8; 8]);
    }

    #[test]
    fn unknown_char_falls_back_to_blank() {
        // Control char and non-ASCII both → space.
        assert_eq!(glyph('\x00'), [0u8; 8]);
        assert_eq!(glyph(''), [0u8; 8]);
    }

    #[test]
    fn digit_glyphs_have_pixels() {
        // Sanity: every digit 0–9 should have at least one lit pixel.
        for c in '0'..='9' {
            assert!(
                glyph(c).iter().any(|&b| b != 0),
                "digit {c} should have non-blank glyph"
            );
        }
    }

    #[test]
    fn uppercase_glyphs_have_pixels() {
        for c in 'A'..='Z' {
            assert!(glyph(c).iter().any(|&b| b != 0), "{c} should be non-blank");
        }
    }

    #[test]
    fn glyph_table_covers_full_printable_range() {
        // 0x20..=0x7E inclusive = 95 entries.
        assert_eq!(GLYPHS.len(), 95);
    }
}