mod offsets;
mod spacing;
use std::mem;
use std::mem::MaybeUninit;
use std::ops::{DerefMut, IndexMut};
use std::sync::Mutex;
use std::thread;
use swash::GlyphMetrics;
use swash::scale::{Render, Scaler, Source};
use swash::zeno::Vector;
use crate::mplus::bitmap::color;
use crate::mplus::bitmap::units::Halfwidth;
use crate::mplus::bitmap::{Glyph, Image, ImageList};
pub use offsets::GlyphOffsets;
pub use spacing::GlyphSpacing;
impl GlyphOffsets {
pub fn scale(
&self,
scalers: &mut [Scaler],
positions: u8,
bit_depth: u8,
glyph_metrics: &GlyphMetrics,
glyph_spacing: &GlyphSpacing,
) -> Glyph {
debug_assert_eq!(scalers.len(), positions as usize);
let advance_width = glyph_metrics.advance_width(self.id);
let advance_height = glyph_metrics.advance_height(self.id);
let new_advance_width = glyph_spacing.halfwidths(advance_width);
let centering_offset = (new_advance_width - advance_width) / 2.0;
let x_offset = glyph_spacing.compensate(self.x_offset);
let y_offset = self.y_offset;
let is_repeating = glyph_spacing.is_code || advance_width == advance_height || self.id == 0;
let mut images = if let Halfwidth::Floor(_) | Halfwidth::Ceil = glyph_spacing.halfwidth {
if !self.is_overlay || self.x_offset < 0.0 {
let length = if is_repeating { 1 } else { positions };
let images = Mutex::new(Vec::from_iter((0..length).map(|_| MaybeUninit::uninit())));
thread::scope(|scope| {
let images = &images;
(0..length).zip(scalers).for_each(|(index, scaler)| {
let x_offset = x_offset.fract() + f32::from(index) / f32::from(length);
let y_offset = y_offset.fract();
scope.spawn(move || {
let image = Render::new(&[Source::Outline])
.offset(Vector::new(x_offset, y_offset))
.render(scaler, self.id)
.expect("expected glyph outline");
let left = image.placement.left;
let left = left.saturating_add_unsigned(centering_offset as u32);
let top = image.placement.top;
let width = image.placement.width;
let image = Image {
left,
top,
width,
data: match image.data.as_slice() {
[] => image.data,
data => color::quantize(data, width, bit_depth),
},
};
images
.lock()
.expect("expected no-poison lock on images")
.deref_mut()
.index_mut(index as usize)
.write(image);
});
});
});
let images = images
.into_inner()
.expect("expected no-poison lock on images");
unsafe { mem::transmute::<Vec<MaybeUninit<Image>>, Vec<Image>>(images) }
} else {
Vec::new()
}
} else {
Vec::new()
};
if images.iter().all(|image| image.data.is_empty()) {
images.clear();
}
Glyph {
x_offset: x_offset - x_offset.fract(),
y_offset: y_offset - y_offset.fract(),
positions,
bit_depth,
id: self.id,
advance_width: new_advance_width,
images: ImageList(images),
}
}
}