use std::{path::Path, io::Cursor, fs};
use super::{Display, Vertex, TextureWrapFunction, TextureMinFilter, TextureMagFilter};
use bmfont::{BMFont, OrdinateOrientation};
use glium::{self, Surface, texture::{Texture2d, RawImage2d}};
use cgmath::Matrix4;
use image;
pub struct BitmapFont {
font: BMFont,
pages: Vec<glium::texture::Texture2d>,
wrap: TextureWrapFunction,
min: TextureMinFilter,
mag: TextureMagFilter
}
impl BitmapFont {
pub fn load(path: &Path, display: &Display) -> Result<BitmapFont, ()> {
let data = match fs::read(path) {
Ok(data) => data,
Err(_) => return Err(())
};
let font = match BMFont::new(Cursor::new(data), OrdinateOrientation::TopToBottom) {
Ok(font) => font,
Err(_) => return Err(())
};
let mut pages = Vec::new();
let mut path_buf = path.to_path_buf();
path_buf.pop();
for path in font.pages() {
let mut path_buf = path_buf.clone();
path_buf.push(path);
let tex = match image::open(path_buf.as_path()) {
Ok(image) => {
let image = image.to_rgba();
let dimensions = image.dimensions();
let image = RawImage2d::from_raw_rgba(image.into_raw(), dimensions);
Texture2d::with_format(
display,
image,
glium::texture::UncompressedFloatFormat::U8U8U8U8,
glium::texture::MipmapsOption::NoMipmap
).unwrap()
},
Err(_) => {
return Err(());
}
};
pages.push(tex);
}
Ok(BitmapFont {
font,
pages,
wrap: TextureWrapFunction::Repeat,
min: TextureMinFilter::Nearest,
mag: TextureMagFilter::Nearest
})
}
pub fn set_wrap_function(&mut self, wrap: TextureWrapFunction) {
self.wrap = wrap;
}
pub fn wrap_function(&self) -> TextureWrapFunction {
self.wrap
}
pub fn set_min_filter(&mut self, min: TextureMinFilter) {
self.min = min;
}
pub fn min_filter(&self) -> TextureMinFilter {
self.min
}
pub fn set_mag_filter(&mut self, mag: TextureMagFilter) {
self.mag = mag;
}
pub fn mag_filter(&self) -> TextureMagFilter {
self.mag
}
pub(crate) fn draw(&self, text: &str, transform: Matrix4<f32>, r: f32, g: f32, b: f32, a: f32, program: &glium::Program, target: &mut glium::Frame, display: &Display) {
let mut pages = Vec::<(u32, Vec<Vertex>)>::new();
'l:
for pos in self.font.parse(text).unwrap() {
let (width, height) = self.pages[pos.page_index as usize].dimensions();
let left_x = pos.screen_rect.x as f32;
let right_x = (pos.screen_rect.max_x() + 1) as f32;
let top_y = pos.screen_rect.y as f32;
let bottom_y = (pos.screen_rect.max_y() + 1) as f32;
let left_u = pos.page_rect.x as f32 / width as f32;
let right_u = (pos.page_rect.max_x() + 1) as f32 / width as f32;
let bottom_v = pos.page_rect.y as f32 / height as f32;
let top_v = (pos.page_rect.max_y() + 1) as f32 / height as f32;
let (bottom_v, top_v) = (top_v, bottom_v);
let mut vertices = vec![
Vertex {
pos: [left_x, bottom_y],
uv: [left_u, bottom_v],
},
Vertex {
pos: [left_x, top_y],
uv: [left_u, top_v],
},
Vertex {
pos: [right_x, top_y],
uv: [right_u, top_v],
},
Vertex {
pos: [left_x, bottom_y],
uv: [left_u, bottom_v],
},
Vertex {
pos: [right_x, top_y],
uv: [right_u, top_v],
},
Vertex {
pos: [right_x, bottom_y],
uv: [right_u, bottom_v],
}
];
for (page, vec) in &mut pages {
if *page == pos.page_index {
vec.append(&mut vertices);
continue 'l;
}
}
pages.push((pos.page_index, vertices));
}
for (page, vertices) in pages {
let buffer = glium::VertexBuffer::new(display, &vertices).unwrap();
let (w, h) = self.pages[page as usize].dimensions();
target.draw(
&buffer,
&glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
program,
&uniform! {
size: [1.0f32, 1.0f32],
tsize: [w as f32, h as f32],
tex: self.pages[page as usize].sampled().wrap_function(self.wrap).minify_filter(self.min).magnify_filter(self.mag),
color: [r, g, b, a],
mvp: [
[transform.x.x, transform.x.y, transform.x.z, transform.x.w],
[transform.y.x, transform.y.y, transform.y.z, transform.y.w],
[transform.z.x, transform.z.y, transform.z.z, transform.z.w],
[transform.w.x, transform.w.y, transform.w.z, transform.w.w]
]
},
&glium::DrawParameters {
blend: glium::draw_parameters::Blend::alpha_blending(),
.. glium::DrawParameters::default()
}
).unwrap();
}
}
}