use crate::{
geom::{Rectangle, Vector, Transform},
input::{ButtonState, MouseButton},
graphics::{Background, Font, FontStyle, GpuTriangle, Image, Mesh, Vertex, View},
lifecycle::Window,
};
use immi::{Draw, DrawContext, GlyphInfos, Matrix};
use rusttype::{Point, Scale};
pub fn create_immi_ctx<'a>(state: ImmiStatus, render: &'a mut ImmiRender<'a>) -> DrawContext<'a, ImmiRender<'a>> {
immi::draw().draw(state.window_size.x, state.window_size.y, render, state.mouse_pos, state.pressed, state.released)
}
pub struct ImmiStatus {
window_size: Vector,
mouse_pos: Option<[f32; 2]>,
pressed: bool,
released: bool,
}
impl ImmiStatus {
pub fn new(window: &Window) -> ImmiStatus {
let window_size = window.screen_size();
let mouse_pos = window.mouse().pos();
let mouse_x_normalized = (mouse_pos.x / window_size.x) * 2f32 - 1f32;
let mouse_y_normalized = (mouse_pos.y / window_size.y) * -2f32 + 1f32;
let state = window.mouse()[MouseButton::Left];
ImmiStatus {
window_size,
mouse_pos: Some([mouse_x_normalized, mouse_y_normalized]),
pressed: state == ButtonState::Pressed,
released: state == ButtonState::Released,
}
}
}
pub struct ImmiRender<'a> {
window: &'a mut Mesh,
view: View,
font: &'a Font
}
impl<'a> ImmiRender<'a> {
#[deprecated(since = "0.3.3", note = "please use new_with_view instead")]
pub fn new(target: &'a mut Mesh, font: &'a Font) -> ImmiRender<'a> {
ImmiRender::new_with_view(target, View::new(Rectangle::new((-1, -1), (2, 2))), font)
}
pub fn new_with_view(target: &'a mut Mesh, view: View, font: &'a Font) -> ImmiRender<'a> {
ImmiRender {
window: target,
view,
font
}
}
pub fn new_with_window(window: &'a mut Window, font: &'a Font) -> ImmiRender<'a> {
let view = window.view();
ImmiRender::new_with_view(window.mesh(), view, font)
}
}
fn matrix_to_trans(matrix: &Matrix) -> Transform {
let array = matrix.0;
Transform::from_array([
[array[0][0], array[1][0], array[2][0]],
[array[0][1], array[1][1], array[2][1]],
[0.0, 0.0, 1.0],
])
}
impl<'a> Draw for ImmiRender<'a> {
type ImageResource = Image;
type TextStyle = FontStyle;
fn draw_triangle(&mut self, texture: &Image, matrix: &Matrix, uv_coords: [[f32; 2]; 3]) {
let transform = self.view.opengl.inverse() * matrix_to_trans(matrix);
let offset = self.window.vertices.len() as u32;
self.window.vertices.extend([(-1, 1), (-1, -1), (1, 1)]
.iter()
.map(|(x, y)| transform * Vector::new(*x, *y))
.zip(uv_coords.iter().map(|[x, y]| Vector::new(*x, 1.0 - *y)))
.map(|(pos, tex_coord)| Vertex::new(pos, Some(tex_coord), Background::Img(texture))));
self.window.triangles.push(GpuTriangle::new(offset, [0, 1, 2], 0.0, Background::Img(texture)));
}
fn get_image_width_per_height(&mut self, image: &Image) -> f32 {
image.area().width() / image.area().height()
}
fn draw_glyph(&mut self, text_style: &FontStyle, glyph: char, matrix: &Matrix) {
let rendered = self.font.render(glyph.encode_utf8(&mut [0; 4]), text_style)
.expect("The character must render correctly");
self.draw_triangle(&rendered, matrix, [
[0.0, 0.0],
[0.0, 1.0],
[1.0, 1.0]
]);
self.draw_triangle(&rendered, matrix, [
[0.0, 1.0],
[1.0, 1.0],
[1.0, 0.0]
]);
}
fn line_height(&self, _text_style: &FontStyle) -> f32 { 1.2 }
fn glyph_infos(&self, text_style: &FontStyle, glyph: char) -> GlyphInfos {
let mut buffer = [0; 4];
let string = glyph.encode_utf8(&mut buffer);
let scale = Scale::uniform(text_style.size);
let start = Point { x: 0.0, y: 0.0 };
let layout = self.font.data.layout(string, scale, start)
.next()
.expect("One character string in, one layout object out");
let bounds = layout.pixel_bounding_box()
.expect("Pixel bounding box must exit");
let x_offset = bounds.min.y as f32;
let y_offset = bounds.min.y as f32;
let width = bounds.max.x as f32 - x_offset;
let height = bounds.max.y as f32 - y_offset;
GlyphInfos {
x_offset,
y_offset,
width,
height,
x_advance: x_offset + width
}
}
fn kerning(&self, text_style: &FontStyle, first_char: char, second_char: char) -> f32 {
let scale = Scale::uniform(text_style.size);
self.font.data.pair_kerning(scale, first_char, second_char)
}
}