Skip to main content

uorustlibs/
font.rs

1//! Methods for reading font data out of fonts.mul
2//!
3//! There are 10 fonts in a file.
4//!
5//! Fonts are represented in a continuous, unindexed file as groups
6//! `|header: u8|characters: [Character..224]`
7//!
8//! Individual Characters are defined as
9//! `|width: u8|height: u8|unknown: u8|pixels: [Color16..width*height]`
10//!
11#[cfg(feature = "image")]
12use crate::color::Color;
13use crate::color::{BLACK_16, Color16};
14use crate::error::MulReaderResult;
15use byteorder::{LittleEndian, ReadBytesExt};
16#[cfg(feature = "image")]
17use image::{Rgba, RgbaImage};
18use std::fs::File;
19use std::io::{Read, Seek};
20use std::path::Path;
21
22/// An individual glyph in a font
23#[derive(Debug, PartialEq, Eq, Clone)]
24pub struct Character {
25    pub width: u8,
26    pub height: u8,
27    pub unknown: u8,
28    pub data: Vec<Color16>,
29}
30
31#[cfg(feature = "image")]
32impl Character {
33    pub fn to_image(&self) -> RgbaImage {
34        let mut buffer = RgbaImage::new(self.width as u32, self.height as u32);
35        for y in 0..self.height {
36            for x in 0..self.width {
37                let pixel = self.data[(y as usize * self.width as usize) + x as usize];
38                println!("{}", pixel);
39                // Black is transparent here
40                if pixel != BLACK_16 {
41                    let (r, g, b, a) = pixel.to_rgba();
42                    buffer.put_pixel(x as u32, y as u32, Rgba([r, g, b, a]));
43                }
44            }
45        }
46        buffer
47    }
48}
49
50/// A font. Fonts should always have 224 characters in them.
51/// They map to ASCII, but skip the first 32 characters
52#[derive(Debug, PartialEq, Eq, Clone)]
53pub struct Font {
54    pub header: u8,
55    pub characters: Vec<Character>,
56}
57
58/// A struct to help reading fonts from a path
59#[derive(Debug)]
60pub struct FontReader<T: Read + Seek> {
61    data_reader: T,
62}
63
64impl FontReader<File> {
65    /// Create a new FontReader from a mul path
66    pub fn new(font_path: &Path) -> MulReaderResult<FontReader<File>> {
67        let data_reader = File::open(font_path)?;
68
69        Ok(FontReader { data_reader })
70    }
71}
72
73impl<T: Read + Seek> FontReader<T> {
74    /// Create a FontReader from an existing readable object
75    pub fn from_readable(data_reader: T) -> FontReader<T> {
76        FontReader { data_reader }
77    }
78
79    /// Read all 10 fonts from the file.
80    /// As fonts are variable-length (due to differing character sizes),
81    /// it's not easy to read them individually
82    pub fn read_fonts(&mut self) -> MulReaderResult<Vec<Font>> {
83        let mut out = vec![];
84        for _ in 0..10 {
85            out.push(self.read_font()?);
86        }
87        Ok(out)
88    }
89
90    fn read_font(&mut self) -> MulReaderResult<Font> {
91        let header = self.data_reader.read_u8()?;
92        let mut chars = vec![];
93        for _ in 0..224 {
94            chars.push(self.read_character()?);
95        }
96        Ok(Font {
97            header,
98            characters: chars,
99        })
100    }
101
102    fn read_character(&mut self) -> MulReaderResult<Character> {
103        let width = self.data_reader.read_u8()?;
104        let height = self.data_reader.read_u8()?;
105        let unknown = self.data_reader.read_u8()?;
106        let mut pixels = vec![];
107        for _ in 0..(width as u32 * height as u32) {
108            pixels.push(self.data_reader.read_u16::<LittleEndian>()?);
109        }
110        Ok(Character {
111            width,
112            height,
113            unknown,
114            data: pixels,
115        })
116    }
117}