use crate::font_introspector::Attributes;
use crate::sugarloaf::primitives::is_private_user_area;
use crate::SpanStyle;
use rustc_hash::FxHashMap;
use unicode_width::UnicodeWidthChar;
#[derive(Debug, Clone, Copy)]
pub struct AdvanceInfo {
pub advance_units: f32,
pub units_per_em: u16,
}
impl AdvanceInfo {
#[inline]
pub fn scaled(&self, font_size: f32) -> f32 {
if self.units_per_em > 0 {
self.advance_units * font_size / self.units_per_em as f32
} else {
0.0
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ResolvedGlyph {
pub font_id: usize,
pub width: f32,
pub is_pua: bool,
pub advance: Option<AdvanceInfo>,
}
pub(crate) struct FontCache {
cache: FxHashMap<(char, Attributes), ResolvedGlyph>,
}
impl FontCache {
pub fn new() -> Self {
Self {
cache: FxHashMap::default(),
}
}
#[inline]
pub fn get(&self, key: &(char, Attributes)) -> Option<&ResolvedGlyph> {
self.cache.get(key)
}
#[inline]
pub fn insert(&mut self, key: (char, Attributes), value: ResolvedGlyph) {
self.cache.insert(key, value);
}
pub fn clear(&mut self) {
self.cache.clear();
}
#[inline]
pub(crate) fn set_advance(&mut self, key: (char, Attributes), advance: AdvanceInfo) {
if let Some(entry) = self.cache.get_mut(&key) {
entry.advance = Some(advance);
}
}
}
pub(crate) fn resolve_with(
cache: &mut FontCache,
font_ctx: &crate::font::FontLibraryData,
ch: char,
attrs: Attributes,
) -> ResolvedGlyph {
if let Some(cached) = cache.get(&(ch, attrs)) {
return *cached;
}
let style = SpanStyle {
font_attrs: attrs,
..Default::default()
};
let mut width = ch.width().unwrap_or(1) as f32;
let mut font_id = 0;
if let Some((fid, is_emoji)) = font_ctx.find_best_font_match(ch, &style) {
font_id = fid;
if is_emoji {
width = 2.0;
}
}
let resolved = ResolvedGlyph {
font_id,
width,
is_pua: is_private_user_area(&ch),
advance: None,
};
cache.insert((ch, attrs), resolved);
resolved
}
pub(crate) fn compute_advance(
font_ctx: &crate::font::FontLibraryData,
font_id: usize,
ch: char,
) -> Option<AdvanceInfo> {
let (data, offset, _key) = font_ctx.get_data(&font_id)?;
let font_ref = crate::font_introspector::FontRef::from_index(&data, offset as usize)?;
let glyph_id = font_ref.charmap().map(ch as u32);
let metrics = crate::font_introspector::GlyphMetrics::from_font(&font_ref, &[]);
Some(AdvanceInfo {
advance_units: metrics.advance_width(glyph_id),
units_per_em: font_ref.metrics(&[]).units_per_em,
})
}