use super::cache::ImageCache;
use super::{AddImage, ImageData, ImageId, ImageLocation};
use crate::font::FontLibrary;
use crate::font_introspector::scale::{
image::{Content, Image as GlyphImage},
*,
};
use crate::font_introspector::zeno::Format;
use core::borrow::Borrow;
use core::hash::{Hash, Hasher};
use rustc_hash::FxHashMap;
use zeno::{Angle, Transform};
const SOURCES: &[Source] = &[
Source::ColorOutline(0),
Source::ColorBitmap(StrikeWith::BestFit),
Source::Outline,
];
pub struct GlyphCache {
scx: ScaleContext,
fonts: FxHashMap<FontKey, FontEntry>,
img: GlyphImage,
max_height: u16,
}
impl GlyphCache {
pub fn new() -> Self {
GlyphCache {
scx: ScaleContext::new(),
fonts: FxHashMap::default(),
img: GlyphImage::new(),
max_height: 0,
}
}
#[inline]
pub fn session<'a>(
&'a mut self,
images: &'a mut ImageCache,
font: usize,
font_library: &'a FontLibrary,
coords: &[i16],
size: f32,
) -> GlyphCacheSession<'a> {
let quant_size = size as u16;
let entry = get_entry(&mut self.fonts, font, coords);
GlyphCacheSession {
font,
entry,
images,
font_library,
max_height: &self.max_height,
scaled_image: &mut self.img,
quant_size,
scale_context: &mut self.scx,
}
}
}
fn get_entry<'a>(
fonts: &'a mut FxHashMap<FontKey, FontEntry>,
id: usize,
coords: &[i16],
) -> &'a mut FontEntry {
let key = (id, Coords::Ref(coords));
if let Some(entry) = fonts.get_mut(&key) {
return unsafe { core::mem::transmute::<&mut FontEntry, &mut FontEntry>(entry) };
}
let key = FontKey {
key: (id, Coords::new(coords)),
};
fonts.entry(key).or_default()
}
pub struct GlyphCacheSession<'a> {
entry: &'a mut FontEntry,
images: &'a mut ImageCache,
scaled_image: &'a mut GlyphImage,
font: usize,
font_library: &'a FontLibrary,
scale_context: &'a mut ScaleContext,
quant_size: u16,
#[allow(unused)]
max_height: &'a u16,
}
impl GlyphCacheSession<'_> {
pub fn get_image(&mut self, image: ImageId) -> Option<ImageLocation> {
self.images.get(&image)
}
#[inline]
pub fn get(&mut self, id: u16) -> Option<GlyphEntry> {
let key = GlyphKey {
id,
size: self.quant_size,
};
if let Some(entry) = self.entry.glyphs.get(&key) {
if self.images.is_valid(entry.image) {
return Some(*entry);
}
}
self.scaled_image.data.clear();
let mut font_library_data = self.font_library.inner.lock();
let enable_hint = font_library_data.hinting;
let font_data = font_library_data.get(&self.font);
let should_embolden = font_data.should_embolden;
let should_italicize = font_data.should_italicize;
if let Some(data) = font_library_data.get_data(&self.font) {
let mut scaler = self
.scale_context
.builder(data)
.hint(enable_hint)
.size(self.quant_size.into())
.build();
if Render::new(SOURCES)
.format(Format::CustomSubpixel([0.3, 0., -0.3]))
.embolden(if should_embolden { 0.5 } else { 0.0 })
.transform(if should_italicize {
Some(Transform::skew(
Angle::from_degrees(14.0),
Angle::from_degrees(0.0),
))
} else {
None
})
.render_into(&mut scaler, id, self.scaled_image)
{
let p = self.scaled_image.placement;
let w = p.width as u16;
let h = p.height as u16;
if w == 0 || h == 0 {
let entry = GlyphEntry {
left: p.left,
top: p.top,
width: w,
height: h,
image: ImageId::empty(), is_bitmap: false,
};
self.entry.glyphs.insert(key, entry);
return Some(entry);
}
let req = AddImage {
width: w,
height: h,
has_alpha: true,
data: ImageData::Borrowed(&self.scaled_image.data),
};
let image = self.images.allocate(req)?;
let entry = GlyphEntry {
left: p.left,
top: p.top,
width: w,
height: h,
image,
is_bitmap: self.scaled_image.content == Content::Color,
};
self.entry.glyphs.insert(key, entry);
return Some(entry);
}
}
None
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct FontKey {
key: (usize, Coords<'static>),
}
impl<'a> Borrow<(usize, Coords<'a>)> for FontKey {
fn borrow(&self) -> &(usize, Coords<'a>) {
&self.key
}
}
#[derive(Default)]
struct FontEntry {
glyphs: FxHashMap<GlyphKey, GlyphEntry>,
}
#[derive(Clone, Debug)]
#[repr(u8)]
enum Coords<'a> {
None,
Inline(u8, [i16; 8]),
Heap(Vec<i16>),
Ref(&'a [i16]),
}
impl Coords<'_> {
fn new(coords: &[i16]) -> Self {
let len = coords.len();
if len == 0 {
Self::None
} else if len <= 8 {
let mut arr = [0i16; 8];
arr[..len].copy_from_slice(coords);
Self::Inline(len as u8, arr)
} else {
Self::Heap(coords.into())
}
}
}
impl Coords<'_> {
fn as_ref(&self) -> &[i16] {
match self {
Self::None => &[],
Self::Inline(len, arr) => &arr[..*len as usize],
Self::Heap(vec) => vec,
Self::Ref(slice) => slice,
}
}
}
impl PartialEq for Coords<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for Coords<'_> {}
impl Hash for Coords<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct GlyphKey {
id: u16,
size: u16,
}
#[derive(Copy, Clone, Debug)]
pub struct GlyphEntry {
pub left: i32,
pub top: i32,
pub width: u16,
pub height: u16,
pub image: ImageId,
pub is_bitmap: bool,
}