use crate::layout::GlyphRasterConfig;
use crate::math::Geometry;
use crate::platform::{as_i32, ceil, fract, is_negative};
use crate::raster::Raster;
use crate::raw::RawFont;
use crate::FontResult;
use alloc::vec::*;
use core::mem;
use core::num::NonZeroU32;
use core::ops::Deref;
use hashbrown::HashMap;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct AABB {
pub xmin: f32,
pub xmax: f32,
pub ymin: f32,
pub ymax: f32,
}
impl AABB {
pub fn new(xmin: f32, xmax: f32, ymin: f32, ymax: f32) -> AABB {
AABB {
xmin,
xmax,
ymin,
ymax,
}
}
#[inline(always)]
pub fn scale(&self, scale: f32) -> AABB {
AABB {
xmin: self.xmin * scale,
xmax: self.xmax * scale,
ymin: self.ymin * scale,
ymax: self.ymax * scale,
}
}
}
pub const ZERO_METRICS: Metrics = Metrics {
width: 0,
height: 0,
advance_width: 0.0,
advance_height: 0.0,
bounds: AABB {
xmin: 0.0,
xmax: 0.0,
ymin: 0.0,
ymax: 0.0,
},
};
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Metrics {
pub width: usize,
pub height: usize,
pub advance_width: f32,
pub advance_height: f32,
pub bounds: AABB,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct LineMetrics {
pub ascent: f32,
pub descent: f32,
pub line_gap: f32,
pub new_line_size: f32,
}
impl LineMetrics {
pub fn new(ascent: i16, descent: i16, line_gap: i16) -> LineMetrics {
LineMetrics {
ascent: ascent as f32,
descent: descent as f32,
line_gap: line_gap as f32,
new_line_size: (ascent - descent + line_gap) as f32,
}
}
#[inline(always)]
pub fn scale(&self, scale: f32) -> LineMetrics {
LineMetrics {
ascent: self.ascent * scale,
descent: self.descent * scale,
line_gap: self.line_gap * scale,
new_line_size: self.new_line_size * scale,
}
}
}
struct Glyph {
geometry: Geometry,
width: f32,
height: f32,
advance_width: f32,
advance_height: f32,
bounds: AABB,
}
impl Glyph {
#[inline(always)]
fn metrics(&self, scale: f32) -> Metrics {
let bounds = self.bounds.scale(scale);
let mut offset_x = fract(bounds.xmin);
if is_negative(offset_x) {
offset_x += 1.0;
}
let height = scale * self.height;
let mut offset_y = ceil(height) - height - fract(bounds.ymin);
if is_negative(offset_y) {
offset_y += 1.0;
}
Metrics {
width: as_i32(ceil(scale * self.width + offset_x)) as usize,
height: as_i32(ceil(height + offset_y)) as usize,
advance_width: scale * self.advance_width,
advance_height: scale * self.advance_height,
bounds,
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct FontSettings {}
impl Default for FontSettings {
fn default() -> FontSettings {
FontSettings {}
}
}
pub struct Font {
units_per_em: f32,
glyphs: Vec<Glyph>,
char_to_glyph: HashMap<u32, NonZeroU32>,
horizontal_line_metrics: Option<LineMetrics>,
vertical_line_metrics: Option<LineMetrics>,
}
impl Font {
pub fn from_bytes<Data: Deref<Target = [u8]>>(data: Data, _: FontSettings) -> FontResult<Font> {
let mut raw = RawFont::new(data)?;
let mut glyphs = Vec::with_capacity(raw.glyf.glyphs.len());
for glyph in &mut raw.glyf.glyphs {
let geometry = Geometry::new(&glyph.points);
let advance_width = if let Some(hmtx) = &raw.hmtx {
let hmetric = hmtx.hmetrics[glyph.metrics];
hmetric.advance_width as f32
} else {
0.0
};
let advance_height = if let Some(vmtx) = &raw.vmtx {
let vmetric = vmtx.vmetrics[glyph.metrics];
vmetric.advance_height as f32
} else {
0.0
};
let bounds = geometry.effective_bounds.unwrap_or(AABB::new(0.0, 0.0, 0.0, 0.0));
glyphs.push(Glyph {
geometry,
width: bounds.xmax - bounds.xmin,
height: bounds.ymax - bounds.ymin,
advance_width,
advance_height,
bounds,
});
}
let horizontal_line_metrics = if let Some(hhea) = &raw.hhea {
Some(LineMetrics::new(hhea.ascent, hhea.descent, hhea.line_gap))
} else {
None
};
let vertical_line_metrics = if let Some(vhea) = &raw.vhea {
Some(LineMetrics::new(vhea.ascent, vhea.descent, vhea.line_gap))
} else {
None
};
Ok(Font {
glyphs,
char_to_glyph: raw.cmap.map.clone(),
units_per_em: raw.head.units_per_em as f32,
horizontal_line_metrics,
vertical_line_metrics,
})
}
pub fn horizontal_line_metrics(&self, px: f32) -> Option<LineMetrics> {
if let Some(metrics) = self.horizontal_line_metrics {
Some(metrics.scale(Self::scale_factor(px, self.units_per_em)))
} else {
None
}
}
pub fn vertical_line_metrics(&self, px: f32) -> Option<LineMetrics> {
if let Some(metrics) = self.vertical_line_metrics {
Some(metrics.scale(Self::scale_factor(px, self.units_per_em)))
} else {
None
}
}
#[inline(always)]
fn scale_factor(px: f32, units_per_em: f32) -> f32 {
px / units_per_em
}
#[inline]
pub fn metrics(&self, character: char, px: f32) -> Metrics {
self.metrics_indexed(self.lookup_glyph_index(character), px)
}
pub fn metrics_indexed(&self, index: usize, px: f32) -> Metrics {
let glyph = &self.glyphs[index];
let scale = Font::scale_factor(px, self.units_per_em);
glyph.metrics(scale)
}
#[inline]
pub fn rasterize_config(&self, config: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
self.rasterize_indexed(self.lookup_glyph_index(config.c), config.px)
}
#[inline]
pub fn rasterize(&self, character: char, px: f32) -> (Metrics, Vec<u8>) {
self.rasterize_indexed(self.lookup_glyph_index(character), px)
}
pub fn rasterize_indexed(&self, index: usize, px: f32) -> (Metrics, Vec<u8>) {
let glyph = &self.glyphs[index];
let scale = Font::scale_factor(px, self.units_per_em);
let bounds = glyph.bounds.scale(scale);
let mut offset_x = fract(bounds.xmin);
if is_negative(offset_x) {
offset_x += 1.0;
}
let height = scale * glyph.height;
let mut offset_y = ceil(height) - height - fract(bounds.ymin);
if is_negative(offset_y) {
offset_y += 1.0;
}
let metrics = Metrics {
width: as_i32(ceil(scale * glyph.width + offset_x)) as usize,
height: as_i32(ceil(height + offset_y)) as usize,
advance_width: scale * glyph.advance_width,
advance_height: scale * glyph.advance_height,
bounds,
};
let mut canvas = Raster::new(metrics.width, metrics.height);
canvas.draw(&glyph.geometry, scale, offset_x, offset_y);
(metrics, canvas.get_bitmap())
}
#[inline]
pub fn lookup_glyph_index(&self, character: char) -> usize {
unsafe {
mem::transmute::<Option<NonZeroU32>, u32>(self.char_to_glyph.get(&(character as u32)).copied())
as usize
}
}
}