use png::{ColorType, Decoder};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use crate::FontFormat;
use bmfont_parser::BMFont;
#[derive(Debug, Clone, PartialEq)]
pub struct CharacterData {
pub(crate) id: u32,
pub(crate) x1: f32,
pub(crate) x2: f32,
pub(crate) y1: f32,
pub(crate) y2: f32,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) x_off: i32,
pub(crate) y_off: i32,
}
#[derive(Debug, PartialEq)]
pub struct Font {
pub name: String,
pub(crate) image_buffer: Vec<u8>,
pub(crate) width: u32,
pub(crate) height: u32,
pub line_height: u32,
pub size: u32,
pub(crate) min_offset_y: i32,
pub(crate) average_xadvance: f32,
pub(crate) characters: HashMap<u16, CharacterData>,
}
impl Font {
pub fn load<T: Into<PathBuf>>(format: &FontFormat, fnt_path: T) -> Font {
let fnt_path = fnt_path.into();
if !fnt_path.exists() {
panic!("Font image or format file missing");
}
let bm_font;
match BMFont::from_path(format, fnt_path) {
Ok(bmf) => bm_font = bmf,
Err(error) => panic!("Failed to load font file: {}", error),
}
let image_path = &bm_font.pages[0].image_path;
println!("{:?}", image_path);
Font::load_with_bmfont_and_image_read(&bm_font, File::open(image_path).unwrap())
}
pub fn load_raw<T: Into<String>, R: Read>(
format: &FontFormat,
content: T,
image_read: R,
) -> Font {
let bm_font;
match BMFont::from_loaded(format, content.into(), &["image.png"]) {
Ok(bmf) => bm_font = bmf,
Err(error) => panic!("Failed to load font file: {}", error),
}
Font::load_with_bmfont_and_image_read(&bm_font, image_read)
}
fn load_with_bmfont_and_image_read<R: Read>(bm_font: &BMFont, read: R) -> Font {
let decoder = Decoder::new(read);
let (info, mut reader) = decoder.read_info().unwrap();
if info.color_type != ColorType::RGBA {
panic!("Font color type is not RGBA");
}
let mut image_buffer = vec![0; info.buffer_size()];
reader.next_frame(&mut image_buffer).unwrap();
if image_buffer.len() != (info.width * info.height * 4) as usize {
panic!("Font image is deformed");
}
let mut characters = HashMap::<u16, CharacterData>::new();
let width_float = info.width as f32;
let height_float = info.height as f32;
let mut min_off_y = 100_000;
let mut xadvance_sum = 0.0;
for (key, value) in bm_font.chars.iter() {
let x1 = value.x as f32 / width_float;
let x2 = (value.x as f32 + value.width as f32) / width_float;
let y1 = value.y as f32 / height_float;
let y2 = (value.y as f32 + value.height as f32) / height_float;
if value.yoffset < min_off_y {
min_off_y = value.yoffset;
}
xadvance_sum += value.xadvance as f32;
characters.insert(
*key as u16,
CharacterData {
id: value.id,
x1,
x2,
y1,
y2,
width: value.width,
height: value.height,
x_off: value.xoffset,
y_off: value.yoffset,
},
);
}
let avg_xadvances = xadvance_sum / characters.len() as f32;
Font {
name: (&bm_font.font_name).clone(),
image_buffer: image_buffer,
width: info.width,
height: info.height,
line_height: bm_font.line_height,
size: bm_font.size,
min_offset_y: min_off_y,
average_xadvance: avg_xadvances,
characters: characters,
}
}
pub fn get_character(&self, character: u16) -> Result<CharacterData, String> {
if let Some(character_data) = self.characters.get(&character) {
Ok(character_data.clone())
} else {
Err(format!("Character not found: '{}'", character))
}
}
}