use crate::face::Face;
use crate::outline::{flatten_with_shear_offset, FlatBounds};
use crate::Error;
const SUPERSAMPLE: u32 = 4;
#[derive(Debug, Clone, Default)]
pub struct AlphaBitmap {
pub width: u32,
pub height: u32,
pub data: Vec<u8>,
}
impl AlphaBitmap {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
data: vec![0; (width as usize) * (height as usize)],
}
}
pub fn is_empty(&self) -> bool {
self.width == 0 || self.height == 0
}
pub fn get(&self, x: u32, y: u32) -> u8 {
if x >= self.width || y >= self.height {
return 0;
}
self.data[(y * self.width + x) as usize]
}
pub fn nonzero_pixel_count(&self) -> usize {
self.data.iter().filter(|&&b| b != 0).count()
}
}
#[derive(Debug)]
pub struct Rasterizer;
impl Rasterizer {
pub fn raster_glyph(face: &Face, glyph_id: u16, size_px: f32) -> Result<AlphaBitmap, Error> {
Self::raster_glyph_styled(face, glyph_id, size_px, 0.0)
}
pub fn raster_glyph_styled(
face: &Face,
glyph_id: u16,
size_px: f32,
shear_x_per_y: f32,
) -> Result<AlphaBitmap, Error> {
Self::raster_glyph_subpixel(face, glyph_id, size_px, shear_x_per_y, 0.0)
}
pub fn raster_glyph_subpixel(
face: &Face,
glyph_id: u16,
size_px: f32,
shear_x_per_y: f32,
x_subpixel: f32,
) -> Result<AlphaBitmap, Error> {
if size_px <= 0.0 {
return Ok(AlphaBitmap::default());
}
let upem = face.units_per_em().max(1) as f32;
let scale = size_px / upem;
let outline = face.with_font(|font| font.glyph_outline(glyph_id))??;
let flat = match flatten_with_shear_offset(&outline, scale, shear_x_per_y, x_subpixel) {
Some(f) => f,
None => return Ok(AlphaBitmap::default()),
};
Ok(rasterise_flat(&flat))
}
pub fn glyph_offset(face: &Face, glyph_id: u16, size_px: f32) -> Result<(f32, f32), Error> {
Self::glyph_offset_styled(face, glyph_id, size_px, 0.0)
}
pub fn glyph_offset_styled(
face: &Face,
glyph_id: u16,
size_px: f32,
shear_x_per_y: f32,
) -> Result<(f32, f32), Error> {
Self::glyph_offset_subpixel(face, glyph_id, size_px, shear_x_per_y, 0.0)
}
pub fn glyph_offset_subpixel(
face: &Face,
glyph_id: u16,
size_px: f32,
shear_x_per_y: f32,
x_subpixel: f32,
) -> Result<(f32, f32), Error> {
if size_px <= 0.0 {
return Ok((0.0, 0.0));
}
let upem = face.units_per_em().max(1) as f32;
let scale = size_px / upem;
let outline = face.with_font(|font| font.glyph_outline(glyph_id))??;
let flat = match flatten_with_shear_offset(&outline, scale, shear_x_per_y, x_subpixel) {
Some(f) => f,
None => return Ok((0.0, 0.0)),
};
Ok((flat.bounds.x_min, flat.bounds.y_min))
}
}
fn rasterise_flat(flat: &crate::outline::FlatOutline) -> AlphaBitmap {
let w = flat.bounds.width_px();
let h = flat.bounds.height_px();
if w == 0 || h == 0 {
return AlphaBitmap::default();
}
let ss_h = h * SUPERSAMPLE;
let mut edges: Vec<Edge> = Vec::new();
for contour in &flat.contours {
if contour.len() < 2 {
continue;
}
for i in 0..contour.len() {
let (x0, y0) = contour[i];
let (x1, y1) = contour[(i + 1) % contour.len()];
if (x0 - x1).abs() < 1e-6 && (y0 - y1).abs() < 1e-6 {
continue;
}
let yss0 = y0 * SUPERSAMPLE as f32;
let yss1 = y1 * SUPERSAMPLE as f32;
if (yss0 - yss1).abs() < 1e-6 {
continue; }
let (mx0, my0, mx1, my1) = if yss0 < yss1 {
(x0, yss0, x1, yss1)
} else {
(x1, yss1, x0, yss0)
};
let dydx_inv = (mx1 - mx0) / (my1 - my0); edges.push(Edge {
y_min: my0,
y_max: my1,
x_at_y_min: mx0,
dxdy: dydx_inv,
});
}
}
if edges.is_empty() {
return AlphaBitmap::new(w, h);
}
edges.sort_by(|a, b| {
a.y_min
.partial_cmp(&b.y_min)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut coverage: Vec<u8> = vec![0; (w as usize) * (ss_h as usize)];
let mut active: Vec<ActiveEdge> = Vec::new();
let mut next_edge = 0usize;
for ss_y in 0..ss_h {
let y = ss_y as f32 + 0.5;
while next_edge < edges.len() && edges[next_edge].y_min <= y {
let e = &edges[next_edge];
if e.y_max > y {
let x = e.x_at_y_min + (y - e.y_min) * e.dxdy;
active.push(ActiveEdge {
x,
y_max: e.y_max,
dxdy: e.dxdy,
});
}
next_edge += 1;
}
active.retain(|e| e.y_max > y);
if active.is_empty() {
continue;
}
active.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
let row = &mut coverage[(ss_y as usize) * (w as usize)..(ss_y as usize + 1) * (w as usize)];
let n = active.len();
let mut i = 0;
while i + 1 < n {
let x0 = active[i].x;
let x1 = active[i + 1].x;
let lo = x0.max(0.0).floor() as i64;
let hi = (x1.min(w as f32)).ceil() as i64;
if hi > lo {
let lo = lo.max(0) as usize;
let hi = (hi as usize).min(w as usize);
if hi > lo {
for px in &mut row[lo..hi] {
*px = 1;
}
}
}
i += 2;
}
for e in &mut active {
e.x += e.dxdy;
}
}
let mut bitmap = AlphaBitmap::new(w, h);
for y in 0..h {
for x in 0..w {
let mut sum = 0u32;
for s in 0..SUPERSAMPLE {
let row = (y * SUPERSAMPLE + s) as usize;
let idx = row * (w as usize) + (x as usize);
sum += coverage[idx] as u32;
}
let alpha = (sum * 255 + (SUPERSAMPLE / 2)) / SUPERSAMPLE;
bitmap.data[(y * w + x) as usize] = alpha.min(255) as u8;
}
}
bitmap
}
#[derive(Debug, Clone, Copy)]
struct Edge {
y_min: f32,
y_max: f32,
x_at_y_min: f32,
dxdy: f32,
}
#[derive(Debug, Clone, Copy)]
struct ActiveEdge {
x: f32,
y_max: f32,
dxdy: f32,
}
#[allow(dead_code)]
fn _bounds_type_hint(_: FlatBounds) {}