raster.rs

3.5 kB · rust · 132 lines

1use super::shape::descends;2use super::{glyph, trim};34pub(crate) struct Block {5    /// The character the block draws.6    pub char: char,7    /// The glyph rows, top to bottom.8    pub rows: Vec<String>,9    /// The column the block starts at.10    pub col: usize,11    /// The rows the block sits below the baseline.12    pub offset: usize,13}1415impl Block {16    /// Reports the block width in columns.17    pub fn width(&self) -> usize {18        self.rows[0].len()19    }20}2122pub(crate) struct Layout {23    /// The height in rows.24    pub height: usize,25    /// The width in columns.26    pub width: usize,27    /// The placed blocks, in reading order.28    pub blocks: Vec<Block>,29}3031pub(crate) fn layout(text: &str) -> Layout {32    if text.is_empty() {33        return Layout {34            height: 0,35            width: 0,36            blocks: Vec::new(),37        };38    }39    let height = if text.chars().any(descends) { 7 } else { 5 };40    let shapes: Vec<(char, Vec<String>)> = text41        .chars()42        .map(|c| match glyph(c) {43            Some(g) if c != ' ' => (c, trim(&g.rows)),44            _ => (c, vec!["000".to_string(); 5]),45        })46        .collect();47    let width = shapes.iter().map(|s| s.1[0].len()).sum::<usize>() + shapes.len() - 1;48    let mut blocks = Vec::new();49    let mut col = 0;50    for (c, rows) in shapes {51        let offset = (height - rows.len()) / 2;52        let step = rows[0].len() + 1;53        blocks.push(Block {54            char: c,55            rows,56            col,57            offset,58        });59        col += step;60    }61    Layout {62        height,63        width,64        blocks,65    }66}6768/// Returns the text as a 0/1 grid, its trimmed glyphs one blank column apart.69///70/// ```71/// assert_eq!(mrlyrs::font::raster("42").len(), 5);72/// assert_eq!(mrlyrs::font::raster("(1)").len(), 7);73/// ```74pub fn raster(text: &str) -> Vec<Vec<u8>> {75    let laid = layout(text);76    if laid.blocks.is_empty() {77        return Vec::new();78    }79    let mut grid = vec![vec![0u8; laid.width]; laid.height];80    for block in &laid.blocks {81        for (r, row) in block.rows.iter().enumerate() {82            for (c, ch) in row.chars().enumerate() {83                if ch == '1' {84                    grid[block.offset + r][block.col + c] = 1;85                }86            }87        }88    }89    grid90}9192#[cfg(test)]93mod tests {94    use super::*;9596    #[test]97    fn digits_string_is_five_tall_with_gapped_width() {98        let rows = raster("42");99        assert_eq!(rows.len(), 5);100        let w4 = trim(&glyph('4').unwrap().rows)[0].len();101        let w2 = trim(&glyph('2').unwrap().rows)[0].len();102        assert_eq!(rows[0].len(), w4 + w2 + 1);103    }104105    #[test]106    fn descender_makes_the_grid_seven_tall() {107        assert_eq!(raster("(1)").len(), 7);108    }109110    #[test]111    fn five_row_glyph_straddles_in_a_seven_row_grid() {112        let rows = raster("(1)");113        let w_paren = trim(&glyph('(').unwrap().rows)[0].len();114        let w_one = trim(&glyph('1').unwrap().rows)[0].len();115        let start = w_paren + 1;116        assert!(rows[0][start..start + w_one].iter().all(|&c| c == 0));117        assert!(rows[6][start..start + w_one].iter().all(|&c| c == 0));118    }119120    #[test]121    fn empty_text_is_empty() {122        assert_eq!(raster(""), Vec::<Vec<u8>>::new());123    }124125    #[test]126    fn unknown_chars_become_three_blank_columns() {127        let rows = raster("\u{00a7}");128        assert_eq!(rows.len(), 5);129        assert_eq!(rows[0].len(), 3);130        assert!(rows.iter().all(|row| row.iter().all(|&v| v == 0)));131    }132}