#[derive(Debug, Clone)]
pub struct RasterOutput {
pub width: usize,
pub height: usize,
pub coverage: Vec<u8>,
pub advance_x: f32,
pub advance_y: f32,
pub bearing_x: i32,
pub bearing_y: i32,
}
pub trait RasterBackend: Send + Sync {
fn rasterize(&self, face_data: &[u8], glyph_id: u16, px_size: f32) -> RasterOutput;
fn rasterize_lcd(
&self,
_face_data: &[u8],
_glyph_id: u16,
_px_size: f32,
) -> Option<oxitext_core::LcdBitmap> {
None
}
fn rasterize_full(
&self,
face_data: &[u8],
glyph_id: u16,
px_size: f32,
) -> crate::result::RasterResult {
let out = self.rasterize(face_data, glyph_id, px_size);
crate::result::RasterResult {
output: oxitext_core::RenderOutput::Greyscale(oxitext_core::Bitmap {
width: out.width as u32,
height: out.height as u32,
pixels: out.coverage,
}),
advance_x: out.advance_x,
advance_y: out.advance_y,
bearing_x: out.bearing_x,
bearing_y: out.bearing_y,
}
}
fn rasterize_color(
&self,
_face_data: &[u8],
_glyph_id: u16,
_px_size: f32,
) -> Option<oxitext_core::ColorBitmap> {
None
}
fn clear_cache(&self) {}
fn rasterize_for_sdf(
&self,
face_data: &[u8],
glyph_id: u16,
px_size: f32,
) -> Option<oxitext_core::Bitmap> {
let out = self.rasterize(face_data, glyph_id, px_size);
if out.width == 0 || out.height == 0 {
return None;
}
Some(oxitext_core::Bitmap {
width: out.width as u32,
height: out.height as u32,
pixels: out.coverage,
})
}
}
const DEFAULT_FONT_CACHE_CAP: usize = 64;
pub struct FontdueRaster {
cache: std::sync::Mutex<lru::LruCache<usize, fontdue::Font>>,
}
impl FontdueRaster {
pub fn new() -> Self {
let cap = std::num::NonZeroUsize::new(DEFAULT_FONT_CACHE_CAP)
.expect("DEFAULT_FONT_CACHE_CAP is non-zero");
Self {
cache: std::sync::Mutex::new(lru::LruCache::new(cap)),
}
}
}
impl Default for FontdueRaster {
fn default() -> Self {
Self::new()
}
}
impl FontdueRaster {
pub fn raster_positioned(
&self,
face_data: &[u8],
glyph_id: u16,
px_size: f32,
subpixel_x: f32,
subpixel_y: f32,
) -> Option<oxitext_core::Bitmap> {
let _ = (subpixel_x, subpixel_y);
let out = self.rasterize(face_data, glyph_id, px_size);
if out.width == 0 && out.height == 0 {
return Some(oxitext_core::Bitmap {
width: 0,
height: 0,
pixels: Vec::new(),
});
}
Some(oxitext_core::Bitmap {
width: out.width as u32,
height: out.height as u32,
pixels: out.coverage,
})
}
}
impl RasterBackend for FontdueRaster {
fn rasterize(&self, face_data: &[u8], glyph_id: u16, px_size: f32) -> RasterOutput {
if let Some(font) = crate::tl_cache::get_or_parse_fontdue(face_data) {
let (metrics, coverage) = font.rasterize_indexed(glyph_id, px_size);
return RasterOutput {
width: metrics.width,
height: metrics.height,
coverage,
advance_x: metrics.advance_width,
advance_y: metrics.advance_height,
bearing_x: metrics.xmin,
bearing_y: metrics.ymin,
};
}
let key = face_data.as_ptr() as usize;
let mut guard = match self.cache.lock() {
Ok(g) => g,
Err(_) => {
return RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x: 0.0,
advance_y: 0.0,
bearing_x: 0,
bearing_y: 0,
};
}
};
if !guard.contains(&key) {
let font = match fontdue::Font::from_bytes(face_data, fontdue::FontSettings::default())
{
Ok(f) => f,
Err(_) => {
return RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x: 0.0,
advance_y: 0.0,
bearing_x: 0,
bearing_y: 0,
};
}
};
guard.put(key, font);
}
match guard.get(&key) {
Some(font) => {
let (metrics, coverage) = font.rasterize_indexed(glyph_id, px_size);
RasterOutput {
width: metrics.width,
height: metrics.height,
coverage,
advance_x: metrics.advance_width,
advance_y: metrics.advance_height,
bearing_x: metrics.xmin,
bearing_y: metrics.ymin,
}
}
None => RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x: 0.0,
advance_y: 0.0,
bearing_x: 0,
bearing_y: 0,
},
}
}
fn clear_cache(&self) {
if let Ok(mut g) = self.cache.lock() {
g.clear();
}
}
}
#[cfg(feature = "ab-glyph-backend")]
pub struct AbGlyphRaster;
#[cfg(feature = "ab-glyph-backend")]
impl Default for AbGlyphRaster {
fn default() -> Self {
Self
}
}
#[cfg(feature = "ab-glyph-backend")]
impl RasterBackend for AbGlyphRaster {
fn rasterize_lcd(
&self,
face_data: &[u8],
glyph_id: u16,
px_size: f32,
) -> Option<oxitext_core::LcdBitmap> {
crate::lcd::rasterize_lcd(
face_data,
glyph_id,
px_size,
crate::options::LcdFilterKernel::FreeType5Tap,
false,
)
}
fn rasterize(&self, face_data: &[u8], glyph_id: u16, px_size: f32) -> RasterOutput {
use ab_glyph::{Font, FontRef, GlyphId as AbGlyphId, PxScale, ScaleFont};
let font = match FontRef::try_from_slice(face_data) {
Ok(f) => f,
Err(_) => {
return RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x: 0.0,
advance_y: 0.0,
bearing_x: 0,
bearing_y: 0,
};
}
};
let scale = PxScale::from(px_size);
let scaled = font.as_scaled(scale);
let ab_gid = AbGlyphId(glyph_id);
let advance_x = scaled.h_advance(ab_gid);
let advance_y = 0.0_f32;
let h_bearing = scaled.h_side_bearing(ab_gid);
let glyph = ab_gid.with_scale_and_position(scale, ab_glyph::point(0.0, 0.0));
let outlined = match font.outline_glyph(glyph) {
Some(og) => og,
None => {
return RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x,
advance_y,
bearing_x: h_bearing as i32,
bearing_y: 0,
};
}
};
let bounds = outlined.px_bounds();
let w = bounds.width().ceil() as usize;
let h = bounds.height().ceil() as usize;
if w == 0 || h == 0 {
return RasterOutput {
width: 0,
height: 0,
coverage: Vec::new(),
advance_x,
advance_y,
bearing_x: h_bearing as i32,
bearing_y: 0,
};
}
let mut coverage = vec![0u8; w * h];
outlined.draw(|x, y, c| {
let idx = y as usize * w + x as usize;
if idx < coverage.len() {
coverage[idx] = (c * 255.0).round() as u8;
}
});
RasterOutput {
width: w,
height: h,
coverage,
advance_x,
advance_y,
bearing_x: bounds.min.x as i32,
bearing_y: bounds.min.y as i32,
}
}
}