use super::cache::ImageCache;
use super::PixelFormat;
use super::{AddImage, ImageData, ImageId, ImageLocation};
use core::borrow::Borrow;
use core::hash::{Hash, Hasher};
use rustc_hash::FxHashMap;
use swash::scale::{
image::{Content, Image as GlyphImage},
*,
};
use swash::zeno::Format;
use swash::FontRef;
const SOURCES: &[Source] = &[
Source::ColorOutline(0),
Source::ColorBitmap(StrikeWith::BestFit),
Source::Outline,
];
pub struct GlyphCache {
scx: ScaleContext,
fonts: FxHashMap<FontKey, FontEntry>,
img: GlyphImage,
}
impl GlyphCache {
pub fn new() -> Self {
GlyphCache {
scx: ScaleContext::new(),
fonts: FxHashMap::default(),
img: GlyphImage::new(),
}
}
#[inline]
pub fn session<'a>(
&'a mut self,
images: &'a mut ImageCache,
font: FontRef<'a>,
coords: &[i16],
size: f32,
) -> GlyphCacheSession<'a> {
let quant_size = size as u16;
let entry = get_entry(&mut self.fonts, font.key.value(), coords);
let scaler = self
.scx
.builder(font)
.hint(true)
.size(size)
.build();
GlyphCacheSession {
entry,
images,
scaler,
scaled_image: &mut self.img,
quant_size,
}
}
pub fn prune(&mut self, images: &mut ImageCache) {
self.fonts.retain(|_, entry| {
for glyph in &entry.glyphs {
images.deallocate(glyph.1.image);
}
false
});
}
#[allow(unused)]
pub fn clear_evicted(&mut self, images: &mut ImageCache) {
self.fonts.retain(|_, entry| {
entry.glyphs.retain(|_, g| images.is_valid(g.image));
!entry.glyphs.is_empty()
});
}
}
fn get_entry<'a>(
fonts: &'a mut FxHashMap<FontKey, FontEntry>,
id: u64,
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,
scaler: Scaler<'a>,
scaled_image: &'a mut GlyphImage,
quant_size: u16,
}
impl<'a> GlyphCacheSession<'a> {
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();
if Render::new(SOURCES)
.format(Format::CustomSubpixel([0.3, 0., -0.3]))
.render_into(&mut self.scaler, id, self.scaled_image)
{
let p = self.scaled_image.placement;
let w = p.width as u16;
let h = p.height as u16;
let req = AddImage {
format: PixelFormat::Rgba8,
width: w,
height: h,
has_alpha: true,
evictable: 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,
desc: DescenderRegion::new(self.scaled_image),
};
self.entry.glyphs.insert(key, entry);
return Some(entry);
}
None
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct FontKey {
key: (u64, Coords<'static>),
}
impl<'a> Borrow<(u64, Coords<'a>)> for FontKey {
fn borrow(&self) -> &(u64, 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<'a> Coords<'a> {
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<'a> Coords<'a> {
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,
pub desc: DescenderRegion,
}
#[derive(Copy, Clone, Debug)]
pub struct DescenderRegion {
start: u16,
end: u16,
}
impl DescenderRegion {
fn new(image: &GlyphImage) -> Self {
let mut start = u16::MAX;
let mut end = 0;
let h = image.placement.height as i32;
let w = image.placement.width as usize;
let y1 = image.placement.top + 1;
if y1 >= 0 && y1 < h && w < u16::MAX as usize {
let y1 = y1 as usize;
let y2 = h as usize;
start = u16::MAX;
if image.content == Content::Mask {
for y in y1..y2 {
let mut has_ink = false;
let mut in_ink = false;
let offset = y * w;
if let Some(row) = image.data.get(offset..offset + w) {
for (i, alpha) in row.iter().enumerate() {
if *alpha != 0 {
if !has_ink {
has_ink = true;
start = start.min(i as u16);
}
in_ink = true;
} else if in_ink {
in_ink = false;
end = end.max(i as u16);
}
}
}
if in_ink {
end = w as u16;
}
}
} else {
for y in y1..y2 {
let mut has_ink = false;
let mut in_ink = false;
let offset = y * w * 4;
if let Some(row) = image.data.get(offset..offset + w * 4) {
for (i, rgba) in row.chunks_exact(4).enumerate() {
if rgba[0] != 0
|| rgba[1] != 0
|| rgba[2] != 0
|| rgba[3] != 0
{
if !has_ink {
has_ink = true;
start = start.min(i as u16);
}
in_ink = true;
} else if in_ink {
in_ink = false;
end = end.max(i as u16);
}
}
}
if in_ink {
end = w as u16;
}
}
}
}
Self { start, end }
}
#[inline]
pub fn range(&self) -> Option<(f32, f32)> {
if self.start <= self.end {
Some((self.start as f32, self.end as f32))
} else {
None
}
}
}