darkengine 0.1.0

2D game engine written in Rust
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;

/// A bitmap font, in the BMFont (.fnt) format.
pub struct BitmapFont {
	font: BMFont,
	pages: Vec<glium::texture::Texture2d>,
	wrap: TextureWrapFunction,
	min: TextureMinFilter,
	mag: TextureMagFilter
}

impl BitmapFont {
	/// Load a font from a file.
	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
		})
	}

	/// Set the wrap function.
	pub fn set_wrap_function(&mut self, wrap: TextureWrapFunction) {
		self.wrap = wrap;
	}

	/// The wrap function.
	pub fn wrap_function(&self) -> TextureWrapFunction {
		self.wrap
	}

	/// Set the minification filter.
	pub fn set_min_filter(&mut self, min: TextureMinFilter) {
		self.min = min;
	}

	/// The minification filter.
	pub fn min_filter(&self) -> TextureMinFilter {
		self.min
	}

	/// Set the magnification filter.
	pub fn set_mag_filter(&mut self, mag: TextureMagFilter) {
		self.mag = mag;
	}

	/// The magnification filter.
	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();
		}
	}
}