raster.rs

3.5 kB · rust · 131 lines

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