use freetypegl::{TextureFont, TextureGlyph};
use super::{Mesh, vertex::Vertex2DTex, CoordinateOrigin};
use na::*;
use math::Rect;
use util::Result;
use std::f32;
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum Antialiasing{
None=0,
Gray=1,
LCD=3
}
unsafe impl Send for Ttf{}
pub struct Ttf{
font: TextureFont,
antialiasing_type: Antialiasing,
}
pub struct Builder<'a>{
path: Option<&'a str>,
bytes: Option<&'a [u8]>,
pt_height: f32,
antialiasing: Antialiasing,
}
impl<'a> Builder<'a>{
pub fn new(path: &'a str, pt_height: f32) -> Builder<'a>{
Builder{
path: Some(path),
bytes: None,
pt_height,
antialiasing: Antialiasing::LCD,
}
}
pub fn from_bytes(bytes: &'a [u8], pt_height: f32) -> Builder<'a>{
Builder{
path: None,
bytes: Some(bytes),
pt_height,
antialiasing: Antialiasing::LCD,
}
}
pub fn antialiasing(mut self, antialiasing: Antialiasing) -> Builder<'a>{
self.antialiasing = antialiasing;
self
}
pub fn create(self) -> Result<Ttf>{
let tex_font = if let Some(path) = self.path{
TextureFont::load(path, self.pt_height, self.antialiasing as usize)
}else{
TextureFont::load_from_memory(self.bytes.unwrap().to_vec(), self.pt_height, self.antialiasing as usize)
};
Ok( Ttf{
font: tex_font,
antialiasing_type: self.antialiasing,
})
}
}
impl Ttf{
pub fn line_height(&self) -> f32{
self.font.height()
}
pub fn ascender(&self) -> f32{
self.font.ascender()
}
pub fn descender(&self) -> f32{
self.font.descender()
}
pub fn line_gap(&self) -> f32{
self.font.height() - self.font.ascender()
}
pub fn antialiasing_type(&self) -> Antialiasing{
self.antialiasing_type
}
pub fn mesh(&self, string: &str, x: f32, y: f32, coordinate_origin: CoordinateOrigin) -> Mesh<Vertex2DTex>{
let mut mesh = Mesh::default();
self.shape(string,x,y,coordinate_origin, |bl, tr, glyph|{
append_glyph(&mut mesh,bl,tr,glyph);
});
mesh
}
pub fn bounding_box(&self, string: &str, x: f32, y: f32, coordinate_origin: CoordinateOrigin) -> Rect<f32>{
if string.is_empty(){
Rect{pos: pnt2(x,y), width: 0., height: 0.}
}else{
let mut min = pnt2(f32::MAX as f32, f32::MAX as f32);
let mut max = pnt2(f32::MIN as f32, f32::MIN as f32);
self.shape(string,x,y,coordinate_origin, |bl, tr, _glyph|{
match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => {
min.x = min.x.min(bl.x);
min.y = min.y.min(bl.y);
max.x = max.x.max(tr.x);
max.y = max.y.max(tr.y);
}
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => {
min.x = min.x.min(bl.x);
min.y = min.y.min(tr.y);
max.x = max.x.max(tr.x);
max.y = max.y.max(bl.y);
}
}
});
Rect{pos: min, width: max.x-min.x, height: max.y-min.y}
}
}
pub fn box_mesh(&self, string: &str, x: f32, y: f32, w: f32, h: f32, coordinate_origin: CoordinateOrigin) -> Mesh<Vertex2DTex>{
let mut mesh = Mesh::default();
self.box_shape(string,x,y,w,h,coordinate_origin,|word,x_word,y_word|{
self.shape(word,x_word,y_word,coordinate_origin, |bl, tr, glyph|{
append_glyph(&mut mesh,bl,tr,glyph);
});
});
mesh
}
pub fn box_height(&self, string: &str, w: f32) -> f32{
self.box_shape(string,0.0,0.0,w,-1.0,CoordinateOrigin::TopLeft,|_,_,_|())
}
pub fn next_pos(&self, string: &str, x: f32, y: f32, coordinate_origin: CoordinateOrigin) -> Vec2{
self.shape(string,x,y,coordinate_origin,|_,_,_|())
}
pub fn shape<F>(&self, string: &str, x: f32, y: f32, coordinate_origin: CoordinateOrigin, mut f: F) -> Vec2
where F: FnMut(Vec2, Vec2, &TextureGlyph){
let mut xpos = x;
let mut ypos = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => y + self.line_height(),
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => y - self.line_height(),
};
let line_height = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => -self.line_height(),
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => self.line_height(),
};
for l in string.lines(){
xpos = x;
ypos += line_height;
let mut prevc = '\0';
for (i,c) in l.chars().enumerate(){
let glyph = self.font.glyph(c);
if i > 0 {
xpos+= glyph.kerning(prevc);
}
let (bl,tr) = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => {
let x0 = ( xpos + glyph.offset_x() as f32 ) as i32 as f32;
let y1 = ( ypos + glyph.offset_y() as f32 ) as i32 as f32;
let x1 = ( x0 + glyph.width() as f32 ) as i32 as f32;
let y0 = ( y1 - glyph.height() as f32 ) as i32 as f32;
(vec2(x0,y0),vec2(x1,y1))
}
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => {
let x0 = ( xpos + glyph.offset_x() as f32 ) as i32 as f32;
let y0 = ( ypos - glyph.offset_y() as f32 ) as i32 as f32;
let x1 = ( x0 + glyph.width() as f32 ) as i32 as f32;
let y1 = ( y0 + glyph.height() as f32 ) as i32 as f32;
(vec2(x0,y1),vec2(x1,y0))
}
};
f(bl,tr,&glyph);
xpos += glyph.advance_x();
prevc = c;
}
}
vec2(xpos,ypos)
}
pub fn shape_iter<'a>(&'a self, string: &'a str, x: f32, y: f32, coordinate_origin: CoordinateOrigin) -> ShapeIter<'a> {
let mut xpos = x;
let mut ypos = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => y,
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => y - self.line_height(),
};
let line_height = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => self.line_height(),
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => -self.line_height(),
};
let iter = Box::new(string.lines().flat_map(move |l|{
xpos = x;
ypos -= line_height;
let mut prevc = '\0';
l.chars().enumerate().map(move |(i,c)|{
let glyph = self.font.glyph(c);
if i > 0 {
xpos+= glyph.kerning(prevc);
}
let (bl,tr) = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => {
let x0 = ( xpos + glyph.offset_x() as f32 ) as i32 as f32;
let y1 = ( ypos + glyph.offset_y() as f32 ) as i32 as f32;
let x1 = ( x0 + glyph.width() as f32 ) as i32 as f32;
let y0 = ( y1 - glyph.height() as f32 ) as i32 as f32;
(pnt2(x0,y0), pnt2(x1,y1))
}
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => {
let x0 = ( xpos + glyph.offset_x() as f32 ) as i32 as f32;
let y0 = ( ypos - glyph.offset_y() as f32 ) as i32 as f32;
let x1 = ( x0 + glyph.width() as f32 ) as i32 as f32;
let y1 = ( y0 + glyph.height() as f32 ) as i32 as f32;
(pnt2(x0,y1), pnt2(x1,y0))
}
};
xpos += glyph.advance_x();
prevc = c;
Shape{
rect: Rect{ pos: bl, width: tr.x - bl.x, height: tr.y - bl.y},
glyph,
character: c
}
})
}));
ShapeIter{ iter }
}
pub fn box_shape<F>(&self, string: &str, x: f32, y: f32, w: f32, h: f32, coordinate_origin: CoordinateOrigin, mut f: F) -> f32
where F: FnMut(&str, f32, f32)
{
let line_height = match coordinate_origin {
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => -self.line_height(),
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => self.line_height(),
};
let mut ypos = match coordinate_origin {
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => y - self.line_height(),
CoordinateOrigin::TopLeft | CoordinateOrigin::CenterDown => y + self.line_height(),
};
let mut xpos = x;
let mut first_char = true;
let space_glyph = self.font.glyph(' ');
for paragraph in string.lines(){
for word in paragraph.split_whitespace(){
let word_width = self.next_pos(word,0.0,0.0,coordinate_origin).x;
if !first_char && xpos + word_width > x + w + 1.0{
ypos += line_height;
if h>0.0 && ypos < y - h as f32{
ypos -= self.descender();
return y - ypos;
}
xpos = x;
}
first_char = false;
f(vec!(word.to_string(), " ".to_string()).concat().as_ref(),xpos,ypos);
xpos += word_width + space_glyph.advance_x();
}
ypos += line_height;
xpos = x;
first_char = true;
if h>0.0 && ypos < y - h as f32{
break;
}
}
ypos -= self.descender();
y - ypos
}
pub fn freetypegl(&self) -> &TextureFont{
&self.font
}
}
pub struct Shape{
pub rect: Rect<f32>,
pub glyph: TextureGlyph,
pub character: char,
}
pub struct ShapeIter<'a>{
iter: Box<Iterator<Item=Shape> + 'a>
}
impl<'a> Iterator for ShapeIter<'a>{
type Item = Shape;
fn next(&mut self) -> Option<Shape>{
self.iter.next()
}
}
pub fn append_glyph(mesh: &mut Mesh<Vertex2DTex>, bl: Vec2, tr: Vec2, glyph: &TextureGlyph){
mesh.push(Vertex2DTex{ position: bl, texcoord: vec2(glyph.s0(), glyph.t1()) });
mesh.push(Vertex2DTex{ position: vec2(tr.x, bl.y), texcoord: vec2(glyph.s1(), glyph.t1()) });
mesh.push(Vertex2DTex{ position: tr, texcoord: vec2(glyph.s1(), glyph.t0()) });
mesh.push(Vertex2DTex{ position: bl, texcoord: vec2(glyph.s0(), glyph.t1()) });
mesh.push(Vertex2DTex{ position: tr, texcoord: vec2(glyph.s1(), glyph.t0()) });
mesh.push(Vertex2DTex{ position: vec2(bl.x,tr.y), texcoord: vec2(glyph.s0(), glyph.t0()) });
}