#[macro_use]
extern crate glium;
#[macro_use]
pub extern crate glyph_brush;
mod builder;
pub use builder::GlyphBrushBuilder;
use std::borrow::Cow;
use std::ops::Deref;
use std::hash::{BuildHasher, Hash};
use glium::backend::{Facade, Context};
use glium::{Surface, Program, Frame};
use glium::index::PrimitiveType;
use glium::texture::{RawImage2d, ClientFormat};
use glium::texture::texture2d::Texture2d;
use glyph_brush::rusttype::{Rect, SharedBytes};
use glyph_brush::{
rusttype::{point, Font}, BrushAction, BrushError, DefaultSectionHasher,
FontId, VariedSection, GlyphPositioner, GlyphCruncher, PositionedGlyphIter,
};
const IDENTITY_MATRIX4: [[f32; 4]; 4] = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
];
#[derive(Copy, Clone, Debug)]
struct GlyphVertex {
left_top: [f32; 3],
right_bottom: [f32; 2],
tex_left_top: [f32; 2],
tex_right_bottom: [f32; 2],
color: [f32; 4],
}
implement_vertex!(GlyphVertex, left_top, right_bottom, tex_left_top,
tex_right_bottom, color);
#[derive(Copy, Clone, Debug)]
struct InstanceVertex {
v: f32,
}
implement_vertex!(InstanceVertex, v);
fn rect_to_rect(rect: Rect<u32>) -> glium::Rect {
glium::Rect {
left: rect.min.x,
bottom: rect.min.y,
width: rect.width(),
height: rect.height(),
}
}
fn update_texture(tex: &Texture2d, rect: Rect<u32>, tex_data: &[u8]) {
let image = RawImage2d {
data: std::borrow::Cow::Borrowed(tex_data),
format: ClientFormat::U8,
height: rect.height(),
width: rect.width(),
};
tex.write(rect_to_rect(rect), image);
}
#[inline]
fn to_vertex(
screen_dimensions: (u32, u32),
glyph_brush::GlyphVertex {
mut tex_coords,
pixel_coords,
bounds,
color,
z,
}: glyph_brush::GlyphVertex,
) -> GlyphVertex {
let screen_w = screen_dimensions.0 as f32;
let screen_h = screen_dimensions.1 as f32;
let gl_bounds = Rect {
min: point(
2.0 * (bounds.min.x / screen_w - 0.5),
2.0 * (0.5 - bounds.min.y / screen_h),
),
max: point(
2.0 * (bounds.max.x / screen_w - 0.5),
2.0 * (0.5 - bounds.max.y / screen_h),
),
};
let mut gl_rect = Rect {
min: point(
2.0 * (pixel_coords.min.x as f32 / screen_w - 0.5),
2.0 * (0.5 - pixel_coords.min.y as f32 / screen_h),
),
max: point(
2.0 * (pixel_coords.max.x as f32 / screen_w - 0.5),
2.0 * (0.5 - pixel_coords.max.y as f32 / screen_h),
),
};
if gl_rect.max.x > gl_bounds.max.x {
let old_width = gl_rect.width();
gl_rect.max.x = gl_bounds.max.x;
tex_coords.max.x = tex_coords.min.x + tex_coords.width() * gl_rect.width() / old_width;
}
if gl_rect.min.x < gl_bounds.min.x {
let old_width = gl_rect.width();
gl_rect.min.x = gl_bounds.min.x;
tex_coords.min.x = tex_coords.max.x - tex_coords.width() * gl_rect.width() / old_width;
}
if gl_rect.max.y < gl_bounds.max.y {
let old_height = gl_rect.height();
gl_rect.max.y = gl_bounds.max.y;
tex_coords.max.y = tex_coords.min.y + tex_coords.height() * gl_rect.height() / old_height;
}
if gl_rect.min.y > gl_bounds.min.y {
let old_height = gl_rect.height();
gl_rect.min.y = gl_bounds.min.y;
tex_coords.min.y = tex_coords.max.y - tex_coords.height() * gl_rect.height() / old_height;
}
GlyphVertex {
left_top: [gl_rect.min.x, gl_rect.max.y, z],
right_bottom: [gl_rect.max.x, gl_rect.min.y],
tex_left_top: [tex_coords.min.x, tex_coords.max.y],
tex_right_bottom: [tex_coords.max.x, tex_coords.min.y],
color,
}
}
pub struct GlyphBrush<'font, 'a, H :BuildHasher = DefaultSectionHasher> {
glyph_brush :glyph_brush::GlyphBrush<'font, GlyphVertex, H>,
params :glium::DrawParameters<'a>,
program :Program,
texture :Texture2d,
index_buffer :glium::index::NoIndices,
vertex_buffer :glium::VertexBuffer<GlyphVertex>,
instances :glium::VertexBuffer<InstanceVertex>,
}
impl<'font, 'p> GlyphBrush<'font, 'p> {
pub fn new<'a :'font, F :Facade, V :Into<Vec<Font<'a>>>>(facade :&F, fonts :V) -> Self {
GlyphBrushBuilder::using_fonts(fonts).build(facade)
}
}
impl<'font, 'p, H :BuildHasher> GlyphBrush<'font, 'p, H> {
#[inline]
pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
where
G: GlyphPositioner,
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush.queue_custom_layout(section, custom_layout)
}
#[inline]
pub fn queue<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush.queue(section)
}
#[inline]
pub fn draw_queued<F :Facade + Deref<Target = Context>>(&mut self, facade :&F, frame :&mut Frame) {
self.draw_queued_with_transform(IDENTITY_MATRIX4, facade, frame)
}
pub fn draw_queued_with_transform<F :Facade + Deref<Target = Context>>(&mut self, transform :[[f32; 4]; 4], facade :&F, frame :&mut Frame) {
let screen_dims = facade.get_framebuffer_dimensions();
let mut brush_action;
loop {
{
let tex = &self.texture;
brush_action = self.glyph_brush.process_queued(
|rect, tex_data| {
update_texture(tex, rect, tex_data);
},
|v| {
to_vertex(screen_dims, v)
},
);
}
match brush_action {
Ok(_) => break,
Err(BrushError::TextureTooSmall { suggested }) => {
let (nwidth, nheight) = suggested;
self.texture = Texture2d::empty(facade, nwidth, nheight).unwrap();
self.glyph_brush.resize_texture(nwidth, nheight);
},
}
}
let sampler = glium::uniforms::Sampler::new(&self.texture)
.wrap_function(glium::uniforms::SamplerWrapFunction::Clamp)
.minify_filter(glium::uniforms::MinifySamplerFilter::Linear)
.magnify_filter(glium::uniforms::MagnifySamplerFilter::Linear);
match brush_action.unwrap() {
BrushAction::Draw(verts) => {
self.vertex_buffer = glium::VertexBuffer::new(facade, &verts).unwrap();
},
BrushAction::ReDraw => {},
};
let uniforms = uniform! {
font_tex: sampler,
transform: transform,
};
frame.draw((&self.instances, self.vertex_buffer.per_instance().unwrap()),
&self.index_buffer, &self.program, &uniforms, &self.params).unwrap();
}
pub fn add_font_bytes<'a: 'font, B: Into<SharedBytes<'a>>>(&mut self, font_data: B) -> FontId {
self.glyph_brush.add_font_bytes(font_data)
}
pub fn add_font<'a: 'font>(&mut self, font_data: Font<'a>) -> FontId {
self.glyph_brush.add_font(font_data)
}
}
impl<'font, 'l, H :BuildHasher> GlyphCruncher<'font> for GlyphBrush<'font, 'l, H> {
fn pixel_bounds_custom_layout<'a, S, L>(&mut self, section: S, custom_layout: &L)
-> Option<Rect<i32>>
where
L: GlyphPositioner + Hash,
S: Into<Cow<'a, VariedSection<'a>>>
{
self.glyph_brush.pixel_bounds_custom_layout(section, custom_layout)
}
fn glyphs_custom_layout<'a, 'b, S, L>(&'b mut self, section: S, custom_layout: &L)
-> PositionedGlyphIter<'b, 'font>
where
L: GlyphPositioner + Hash,
S: Into<Cow<'a, VariedSection<'a>>>
{
self.glyph_brush.glyphs_custom_layout(section, custom_layout)
}
#[inline]
fn fonts(&self) -> &[Font<'font>] {
self.glyph_brush.fonts()
}
}