use crate::font::FontFace;
use crate::{Result, TextError};
use swash::scale::{Render, ScaleContext, Source, StrikeWith};
use swash::zeno::Format;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlyphFormat {
Alpha,
Rgba,
}
#[derive(Debug, Clone)]
pub struct RasterizedGlyph {
pub bitmap: Vec<u8>,
pub width: u32,
pub height: u32,
pub bearing_x: i16,
pub bearing_y: i16,
pub advance: u16,
pub format: GlyphFormat,
}
pub struct GlyphRasterizer {
scale_context: ScaleContext,
}
impl GlyphRasterizer {
pub fn new() -> Self {
Self {
scale_context: ScaleContext::new(),
}
}
pub fn rasterize(
&mut self,
font: &FontFace,
glyph_id: u16,
font_size: f32,
) -> Result<RasterizedGlyph> {
let font_data = font.data();
let swash_font = swash::FontRef::from_index(font_data, font.face_index() as usize)
.ok_or(TextError::InvalidFontData)?;
let mut scaler = self
.scale_context
.builder(swash_font)
.size(font_size)
.build();
let metrics = swash_font.metrics(&[]);
let glyph_metrics = swash_font.glyph_metrics(&[]);
let scale = font_size / metrics.units_per_em as f32;
let advance = glyph_metrics.advance_width(glyph_id) * scale;
let mut render = Render::new(&[
Source::ColorOutline(0),
Source::ColorBitmap(StrikeWith::BestFit),
Source::Outline,
]);
render.format(Format::Alpha);
let image = render.render(&mut scaler, glyph_id);
match image {
Some(img) => {
let bearing_x = img.placement.left;
let bearing_y = img.placement.top;
let width = img.placement.width;
let height = img.placement.height;
Ok(RasterizedGlyph {
bitmap: img.data,
width,
height,
bearing_x: bearing_x as i16,
bearing_y: bearing_y as i16,
advance: advance.round() as u16,
format: GlyphFormat::Alpha,
})
}
None => {
Ok(RasterizedGlyph {
bitmap: Vec::new(),
width: 0,
height: 0,
bearing_x: 0,
bearing_y: 0,
advance: advance.round() as u16,
format: GlyphFormat::Alpha,
})
}
}
}
pub fn rasterize_color(
&mut self,
font: &FontFace,
glyph_id: u16,
font_size: f32,
) -> Result<RasterizedGlyph> {
let font_data = font.data();
let swash_font = swash::FontRef::from_index(font_data, font.face_index() as usize)
.ok_or(TextError::InvalidFontData)?;
let mut scaler = self
.scale_context
.builder(swash_font)
.size(font_size)
.build();
let metrics = swash_font.metrics(&[]);
let glyph_metrics = swash_font.glyph_metrics(&[]);
let scale = font_size / metrics.units_per_em as f32;
let advance = glyph_metrics.advance_width(glyph_id) * scale;
let mut render = Render::new(&[
Source::ColorBitmap(StrikeWith::BestFit),
Source::ColorOutline(0),
Source::Outline,
]);
render.format(Format::Subpixel);
let image = render.render(&mut scaler, glyph_id);
match image {
Some(img) => {
let bearing_x = img.placement.left;
let bearing_y = img.placement.top;
let width = img.placement.width;
let height = img.placement.height;
let expected_rgba_size = (width * height * 4) as usize;
let is_color = img.data.len() == expected_rgba_size;
if is_color {
Ok(RasterizedGlyph {
bitmap: img.data,
width,
height,
bearing_x: bearing_x as i16,
bearing_y: bearing_y as i16,
advance: advance.round() as u16,
format: GlyphFormat::Rgba,
})
} else {
let mut rgba = Vec::with_capacity(expected_rgba_size);
for alpha in &img.data {
rgba.push(255); rgba.push(255); rgba.push(255); rgba.push(*alpha); }
Ok(RasterizedGlyph {
bitmap: rgba,
width,
height,
bearing_x: bearing_x as i16,
bearing_y: bearing_y as i16,
advance: advance.round() as u16,
format: GlyphFormat::Rgba,
})
}
}
None => Ok(RasterizedGlyph {
bitmap: Vec::new(),
width: 0,
height: 0,
bearing_x: 0,
bearing_y: 0,
advance: advance.round() as u16,
format: GlyphFormat::Rgba,
}),
}
}
}
impl Default for GlyphRasterizer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rasterizer_creation() {
let _rasterizer = GlyphRasterizer::new();
}
}