#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "bench")]
extern crate test;
#[cfg(test)]
extern crate unicode_normalization;
extern crate arrayvec;
extern crate stb_truetype;
extern crate linked_hash_map;
mod geometry;
mod rasterizer;
mod support;
pub mod gpu_cache;
use std::sync::Arc;
pub use geometry::{Rect, Point, point, Vector, vector, Line, Curve};
use stb_truetype as tt;
#[derive(Clone, Debug)]
pub struct FontCollection<'a>(SharedBytes<'a>);
#[derive(Clone)]
pub struct Font<'a> {
info: tt::FontInfo<SharedBytes<'a>>
}
#[derive(Clone, Debug)]
pub enum SharedBytes<'a> {
ByRef(&'a [u8]),
ByArc(Arc<Box<[u8]>>)
}
impl<'a> ::std::ops::Deref for SharedBytes<'a> {
type Target = [u8];
fn deref(&self) -> &[u8] {
match *self {
SharedBytes::ByRef(bytes) => bytes,
SharedBytes::ByArc(ref bytes) => &***bytes
}
}
}
impl<'a> From<&'a [u8]> for SharedBytes<'a> {
fn from(bytes: &'a [u8]) -> SharedBytes<'a> {
SharedBytes::ByRef(bytes)
}
}
impl From<Arc<Box<[u8]>>> for SharedBytes<'static> {
fn from(bytes: Arc<Box<[u8]>>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes)
}
}
impl From<Box<[u8]>> for SharedBytes<'static> {
fn from(bytes: Box<[u8]>) -> SharedBytes<'static> {
SharedBytes::ByArc(Arc::new(bytes))
}
}
impl From<Vec<u8>> for SharedBytes<'static> {
fn from(bytes: Vec<u8>) -> SharedBytes<'static> {
SharedBytes::ByArc(Arc::new(bytes.into_boxed_slice()))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Codepoint(pub u32);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CodepointOrGlyphId {
Codepoint(Codepoint),
GlyphId(GlyphId)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GlyphId(pub u32);
#[derive(Clone)]
pub struct Glyph<'a> {
inner: GlyphInner<'a>
}
#[derive(Clone)]
enum GlyphInner<'a> {
Proxy(&'a Font<'a>, u32),
Shared(Arc<SharedGlyphData>)
}
#[derive(Debug)]
struct SharedGlyphData {
id: u32,
extents: Option<Rect<i32>>,
scale_for_1_pixel: f32,
unit_h_metrics: HMetrics,
shape: Option<Vec<tt::Vertex>>
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct HMetrics {
pub advance_width: f32,
pub left_side_bearing: f32
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct VMetrics {
pub ascent: f32,
pub descent: f32,
pub line_gap: f32
}
#[derive(Clone)]
pub struct ScaledGlyph<'a> {
g: Glyph<'a>,
api_scale: Scale,
scale: Vector<f32>
}
#[derive(Clone)]
pub struct PositionedGlyph<'a> {
sg: ScaledGlyph<'a>,
position: Point<f32>,
bb: Option<Rect<i32>>
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Scale {
pub x: f32,
pub y: f32
}
impl Scale {
pub fn uniform(s: f32) -> Scale {
Scale { x: s, y: s }
}
}
impl From<char> for Codepoint {
fn from(c: char) -> Codepoint {
Codepoint(c as u32)
}
}
impl From<Codepoint> for CodepointOrGlyphId {
fn from(c: Codepoint) -> CodepointOrGlyphId {
CodepointOrGlyphId::Codepoint(c)
}
}
impl From<GlyphId> for CodepointOrGlyphId {
fn from(g: GlyphId) -> CodepointOrGlyphId {
CodepointOrGlyphId::GlyphId(g)
}
}
impl From<char> for CodepointOrGlyphId {
fn from(c: char) -> CodepointOrGlyphId {
Codepoint(c as u32).into()
}
}
impl<'a> FontCollection<'a> {
pub fn from_bytes<B: Into<SharedBytes<'a>>>(bytes: B) -> FontCollection<'a> {
FontCollection(bytes.into())
}
pub fn into_font(self) -> Option<Font<'a>> {
if tt::is_font(&self.0) && tt::get_font_offset_for_index(&self.0, 1).is_none() {
tt::FontInfo::new(self.0, 0).map(
|info| Font {
info: info
})
} else {
None
}
}
pub fn font_at(&self, i: usize) -> Option<Font<'a>> {
tt::get_font_offset_for_index(&self.0, i as i32)
.and_then(|o| tt::FontInfo::new(self.0.clone(), o as usize))
.map(|info| Font { info: info })
}
pub fn into_fonts(self) -> IntoFontsIter<'a> {
IntoFontsIter {
collection: self,
next_index: 0,
}
}
}
pub struct IntoFontsIter<'a> {
next_index: usize,
collection: FontCollection<'a>,
}
impl<'a> Iterator for IntoFontsIter<'a> {
type Item = Font<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.collection.font_at(self.next_index).map(|font| {
self.next_index += 1;
font
})
}
}
impl<'a> Font<'a> {
pub fn v_metrics(&self, scale: Scale) -> VMetrics {
let vm = self.info.get_v_metrics();
let scale = self.info.scale_for_pixel_height(scale.y);
VMetrics {
ascent: vm.ascent as f32 * scale,
descent: vm.descent as f32 * scale,
line_gap: vm.line_gap as f32 * scale
}
}
pub fn glyph_count(&self) -> usize {
self.info.get_num_glyphs() as usize
}
pub fn glyph<C: Into<CodepointOrGlyphId>>(&self, id: C) -> Option<Glyph> {
let gid = match id.into() {
CodepointOrGlyphId::Codepoint(Codepoint(c)) => self.info.find_glyph_index(c),
CodepointOrGlyphId::GlyphId(GlyphId(gid)) => gid
};
Some(Glyph::new(GlyphInner::Proxy(self, gid)))
}
pub fn glyphs_for<I: Iterator>(&self, itr: I) -> GlyphIter<I> where I::Item: Into<CodepointOrGlyphId> {
GlyphIter {
font: self,
itr: itr
}
}
pub fn layout<'b, 'c>(&'b self, s: &'c str, scale: Scale, start: Point<f32>) -> LayoutIter<'b, 'c> {
LayoutIter {
font: self,
chars: s.chars(),
caret: 0.0,
scale: scale,
start: start,
last_glyph: None
}
}
pub fn pair_kerning<A, B>(&self, scale: Scale, first: A, second: B) -> f32
where A: Into<CodepointOrGlyphId>, B: Into<CodepointOrGlyphId>
{
let (first, second) = (self.glyph(first).unwrap(), self.glyph(second).unwrap());
let factor = self.info.scale_for_pixel_height(scale.y) * (scale.x / scale.y);
let kern = self.info.get_glyph_kern_advance(first.id().0, second.id().0);
factor * kern as f32
}
}
#[derive(Clone)]
pub struct GlyphIter<'a, I: Iterator> where I::Item: Into<CodepointOrGlyphId> {
font: &'a Font<'a>,
itr: I
}
impl<'a, I: Iterator> Iterator for GlyphIter<'a, I> where I::Item: Into<CodepointOrGlyphId> {
type Item = Glyph<'a>;
fn next(&mut self) -> Option<Glyph<'a>> {
self.itr.next().map(|c| self.font.glyph(c).unwrap())
}
}
#[derive(Clone)]
pub struct LayoutIter<'a, 'b> {
font: &'a Font<'a>,
chars: ::std::str::Chars<'b>,
caret: f32,
scale: Scale,
start: Point<f32>,
last_glyph: Option<GlyphId>
}
impl<'a, 'b> Iterator for LayoutIter<'a, 'b> {
type Item = PositionedGlyph<'a>;
fn next(&mut self) -> Option<PositionedGlyph<'a>> {
self.chars.next().map(|c| {
let g = self.font.glyph(c).unwrap().scaled(self.scale);
if let Some(last) = self.last_glyph {
self.caret += self.font.pair_kerning(self.scale, last, g.id());
}
let g = g.positioned(point(self.start.x + self.caret, self.start.y));
self.caret += g.sg.h_metrics().advance_width;
self.last_glyph = Some(g.id());
g
})
}
}
impl<'a> Glyph<'a> {
fn new(inner: GlyphInner) -> Glyph {
Glyph {
inner: inner
}
}
pub fn font(&self) -> Option<&Font<'a>> {
match self.inner {
GlyphInner::Proxy(f, _) => Some(f),
GlyphInner::Shared(_) => None
}
}
pub fn id(&self) -> GlyphId {
match self.inner {
GlyphInner::Proxy(_, id) => GlyphId(id),
GlyphInner::Shared(ref data) => GlyphId(data.id),
}
}
pub fn scaled(self, scale: Scale) -> ScaledGlyph<'a> {
let (scale_x, scale_y) = match self.inner {
GlyphInner::Proxy(font, _) => {
let scale_y = font.info.scale_for_pixel_height(scale.y);
let scale_x = scale_y * scale.x / scale.y;
(scale_x, scale_y)
}
GlyphInner::Shared(ref data) => {
let scale_y = data.scale_for_1_pixel * scale.y;
let scale_x = scale_y * scale.x / scale.y;
(scale_x, scale_y)
}
};
ScaledGlyph {
g: self,
api_scale: scale,
scale: vector(scale_x, scale_y)
}
}
pub fn standalone(&self) -> Glyph<'static> {
match self.inner {
GlyphInner::Proxy(font, id) => Glyph::new(GlyphInner::Shared(Arc::new(SharedGlyphData {
id: id,
scale_for_1_pixel: font.info.scale_for_pixel_height(1.0),
unit_h_metrics: {
let hm = font.info.get_glyph_h_metrics(id);
HMetrics {
advance_width: hm.advance_width as f32,
left_side_bearing: hm.left_side_bearing as f32
}
},
extents: font.info.get_glyph_box(id).map(|bb| Rect {
min: point(bb.x0 as i32, -(bb.y1 as i32)),
max: point(bb.x1 as i32, -(bb.y0 as i32))
}),
shape: font.info.get_glyph_shape(id)
}))),
GlyphInner::Shared(ref data) => Glyph::new(GlyphInner::Shared(data.clone()))
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Segment {
Line(Line),
Curve(Curve)
}
#[derive(Clone, Debug)]
pub struct Contour {
pub segments: Vec<Segment>
}
impl<'a> ScaledGlyph<'a> {
pub fn id(&self) -> GlyphId {
self.g.id()
}
pub fn font(&self) -> Option<&Font<'a>> {
self.g.font()
}
pub fn into_unscaled(self) -> Glyph<'a> {
self.g
}
pub fn unscaled(&self) -> &Glyph<'a> {
&self.g
}
pub fn positioned(self, p: Point<f32>) -> PositionedGlyph<'a> {
let bb = match self.g.inner {
GlyphInner::Proxy(font, id) => {
font.info.get_glyph_bitmap_box_subpixel(id,
self.scale.x, self.scale.y,
p.x, p.y)
.map(|bb| Rect {
min: point(bb.x0, bb.y0),
max: point(bb.x1, bb.y1)
})
}
GlyphInner::Shared(ref data) => {
data.extents.map(|bb| Rect {
min: point((bb.min.x as f32 * self.scale.x + p.x).floor() as i32,
(bb.min.y as f32 * self.scale.y + p.y).floor() as i32),
max: point((bb.max.x as f32 * self.scale.x + p.x).ceil() as i32,
(bb.max.y as f32 * self.scale.y + p.y).ceil() as i32)
})
}
};
PositionedGlyph {
sg: self,
position: p,
bb: bb
}
}
pub fn scale(&self) -> Scale {
self.api_scale
}
pub fn h_metrics(&self) -> HMetrics {
match self.g.inner {
GlyphInner::Proxy(font, id) => {
let hm = font.info.get_glyph_h_metrics(id);
HMetrics {
advance_width: hm.advance_width as f32 * self.scale.x,
left_side_bearing: hm.left_side_bearing as f32 * self.scale.x
}
}
GlyphInner::Shared(ref data) => {
HMetrics {
advance_width: data.unit_h_metrics.advance_width * self.scale.x,
left_side_bearing: data.unit_h_metrics.left_side_bearing * self.scale.y
}
}
}
}
fn shape_with_offset(&self, offset: Point<f32>) -> Option<Vec<Contour>> {
use stb_truetype::VertexType;
use std::mem::replace;
match self.g.inner {
GlyphInner::Proxy(font, id) => font.info.get_glyph_shape(id),
GlyphInner::Shared(ref data) => data.shape.clone()
}.map(|shape| {
let mut result = Vec::new();
let mut current = Vec::new();
let mut last = point(0.0, 0.0);
for v in shape {
let end = point(v.x as f32 * self.scale.x + offset.x,
v.y as f32 * self.scale.y + offset.y);
match v.vertex_type() {
VertexType::MoveTo if result.len() != 0 => {
result.push(Contour {
segments: replace(&mut current, Vec::new())
})
}
VertexType::LineTo => {
current.push(Segment::Line(Line {
p: [last, end]
}))
}
VertexType::CurveTo => {
let control = point(v.cx as f32 * self.scale.x + offset.x,
v.cy as f32 * self.scale.y + offset.y);
current.push(Segment::Curve(Curve {
p: [last, control, end]
}))
}
_ => ()
}
last = end;
}
if current.len() > 0 {
result.push(Contour {
segments: replace(&mut current, Vec::new())
});
}
result
})
}
pub fn shape(&self) -> Option<Vec<Contour>> {
self.shape_with_offset(point(0.0, 0.0))
}
pub fn exact_bounding_box(&self) -> Option<Rect<f32>> {
match self.g.inner {
GlyphInner::Proxy(font, id) => font.info.get_glyph_box(id).map(|bb| {
Rect {
min: point(bb.x0 as f32 * self.scale.x, -bb.y1 as f32 * self.scale.y),
max: point(bb.x1 as f32 * self.scale.x, -bb.y0 as f32 * self.scale.y)
}
}),
GlyphInner::Shared(ref data) => data.extents.map(|bb| Rect {
min: point(bb.min.x as f32 * self.scale.x, bb.min.y as f32 * self.scale.y),
max: point(bb.max.x as f32 * self.scale.x, bb.max.y as f32 * self.scale.y)
})
}
}
pub fn standalone(&self) -> ScaledGlyph<'static> {
ScaledGlyph {
g: self.g.standalone(),
api_scale: self.api_scale,
scale: self.scale
}
}
}
impl<'a> PositionedGlyph<'a> {
pub fn id(&self) -> GlyphId {
self.sg.id()
}
pub fn font(&self) -> Option<&Font<'a>> {
self.sg.font()
}
pub fn unpositioned(&self) -> &ScaledGlyph<'a> {
&self.sg
}
pub fn into_unpositioned(self) -> ScaledGlyph<'a> {
self.sg
}
pub fn pixel_bounding_box(&self) -> Option<Rect<i32>> {
self.bb
}
pub fn shape(&self) -> Option<Vec<Contour>> {
self.sg.shape_with_offset(self.position)
}
pub fn scale(&self) -> Scale {
self.sg.api_scale
}
pub fn position(&self) -> Point<f32> {
self.position
}
pub fn draw<O: FnMut(u32, u32, f32)>(&self, o: O) {
use geometry::{Line, Curve};
use stb_truetype::VertexType;
let shape = match self.sg.g.inner {
GlyphInner::Proxy(font, id) => font.info.get_glyph_shape(id).unwrap_or_else(|| Vec::new()),
GlyphInner::Shared(ref data) => data.shape.clone().unwrap_or_else(|| Vec::new())
};
let bb = if let Some(bb) = self.bb.as_ref() {
bb
} else {
return
};
let offset = vector(bb.min.x as f32, bb.min.y as f32);
let mut lines = Vec::new();
let mut curves = Vec::new();
let mut last = point(0.0, 0.0);
for v in shape {
let end = point(v.x as f32 * self.sg.scale.x + self.position.x,
-v.y as f32 * self.sg.scale.y + self.position.y)
- offset;
match v.vertex_type() {
VertexType::LineTo => {
lines.push(Line {
p: [last, end]
})
}
VertexType::CurveTo => {
let control = point(v.cx as f32 * self.sg.scale.x + self.position.x,
-v.cy as f32 * self.sg.scale.y + self.position.y)
- offset;
curves.push(Curve {
p: [last, control, end]
})
}
VertexType::MoveTo => {}
}
last = end;
}
rasterizer::rasterize(&lines, &curves,
(bb.max.x - bb.min.x) as u32,
(bb.max.y - bb.min.y) as u32,
o);
}
pub fn standalone(&self) -> PositionedGlyph<'static> {
PositionedGlyph {
sg: self.sg.standalone(),
bb: self.bb,
position: self.position
}
}
}