use std::{ops::Range, sync::Arc};
use crate::{
glyph_id::GlyphId,
shaped_glyphs::{BreakBefore, GlyphOffset, ShapedGlyph, ShapedGlyphs},
};
#[derive(Clone, Debug, Default, PartialEq)]
pub(crate) struct LaidGlyphs {
glyphs: Glyphs,
offsets: Option<Arc<[GlyphOffset]>>,
}
#[derive(Clone, Debug, PartialEq)]
enum Glyphs {
Packed(Arc<[PackedGlyph]>),
Full(Arc<[ShapedGlyph]>),
}
impl Default for Glyphs {
fn default() -> Self {
Self::Packed(Arc::default())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct PackedGlyph {
id: u16,
advance: i16,
cluster: u32,
}
impl PackedGlyph {
pub(crate) fn id(self) -> GlyphId {
GlyphId(u32::from(self.id))
}
fn pack(glyph: &ShapedGlyph) -> (Self, bool) {
let id = glyph.id().0;
let advance = glyph.advance.cast_unsigned();
let fits = (id >> 16 == 0) & (advance.wrapping_add(1 << 14) >> 15 == 0);
let packed = Self {
id: (id & 0xFFFF) as u16,
advance: (((advance & 0x7FFF) as u16) << 1).cast_signed()
| i16::from(glyph.unsafe_to_break()),
cluster: glyph.cluster,
};
(packed, fits)
}
fn unpack(self) -> ShapedGlyph {
let before = if self.advance & 1 == 0 {
BreakBefore::Safe
} else {
BreakBefore::Unsafe
};
ShapedGlyph::new(
GlyphId(u32::from(self.id)),
self.cluster,
i32::from(self.advance >> 1),
before,
)
}
}
#[derive(Clone, Copy)]
pub(crate) enum RunGlyphs<'a> {
Packed(&'a [PackedGlyph]),
Full(&'a [ShapedGlyph]),
}
impl<'a> RunGlyphs<'a> {
pub(crate) fn len(self) -> usize {
match self {
Self::Packed(glyphs) => glyphs.len(),
Self::Full(glyphs) => glyphs.len(),
}
}
pub(crate) fn get(self, index: usize) -> Option<ShapedGlyph> {
match self {
Self::Packed(glyphs) => glyphs.get(index).map(|glyph| glyph.unpack()),
Self::Full(glyphs) => glyphs.get(index).copied(),
}
}
pub(crate) fn iter(self) -> impl Iterator<Item = ShapedGlyph> + 'a {
(0..self.len()).filter_map(move |index| self.get(index))
}
fn slice(self, range: Range<usize>) -> Self {
match self {
Self::Packed(glyphs) => Self::Packed(&glyphs[range]),
Self::Full(glyphs) => Self::Full(&glyphs[range]),
}
}
}
impl LaidGlyphs {
pub(crate) fn new(shaped: &ShapedGlyphs) -> Self {
let offsets = shaped.offsets();
let mut fits = true;
let packed: Arc<[PackedGlyph]> = shaped
.iter()
.map(|glyph| {
let (packed, fit) = PackedGlyph::pack(glyph);
fits &= fit;
packed
})
.collect();
let glyphs = if fits {
Glyphs::Packed(packed)
} else {
Glyphs::Full(Arc::from(&shaped[..]))
};
Self {
glyphs,
offsets: offsets
.iter()
.any(|offset| *offset != GlyphOffset::default())
.then(|| Arc::from(offsets)),
}
}
pub(crate) fn len(&self) -> usize {
self.all().len()
}
pub(crate) fn run(&self, range: Range<usize>) -> RunGlyphs<'_> {
self.all().slice(range)
}
fn all(&self) -> RunGlyphs<'_> {
match &self.glyphs {
Glyphs::Packed(glyphs) => RunGlyphs::Packed(glyphs),
Glyphs::Full(glyphs) => RunGlyphs::Full(glyphs),
}
}
pub(crate) fn has_offsets(&self) -> bool {
self.offsets.is_some()
}
pub(crate) fn offsets(&self, range: Range<usize>) -> Option<&[GlyphOffset]> {
self.offsets.as_deref().map(|offsets| {
let len = offsets.len();
&offsets[range.start.min(len)..range.end.min(len)]
})
}
pub(crate) fn take(
&self,
range: Range<usize>,
text: &Range<usize>,
out: &mut ShapedGlyphs,
) -> usize {
let start = range.start;
let end = start
+ self
.run(range)
.iter()
.take_while(|glyph| text.contains(&glyph.text_offset()))
.count();
let offsets = self.offsets(start..end).unwrap_or_default();
match self.run(start..end) {
RunGlyphs::Packed(glyphs) => {
out.extend(glyphs.iter().map(|glyph| glyph.unpack()), offsets);
}
RunGlyphs::Full(glyphs) => out.extend_from_slice(glyphs, offsets),
}
end
}
}