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}