cosmic-text/src/swash.rs
Ian Douglas Scott 0d3fb1dd9d Use ouroboros in FontSystem to avoid lifetime bound
Perhaps not quite what ouroboros is expected to be used for, but it's
not too bad, and avoiding the lifetime bound can be a huge help.
2022-11-04 10:37:32 -06:00

133 lines
4.5 KiB
Rust

// SPDX-License-Identifier: MIT OR Apache-2.0
use std::collections::HashMap;
use swash::scale::{ScaleContext, image::Content};
use swash::scale::{Render, Source, StrikeWith};
use swash::zeno::{Format, Vector};
use crate::{CacheKey, Color, FontSystem};
pub use swash::scale::image::{Content as SwashContent, Image as SwashImage};
fn swash_image<'a>(font_system: &'a FontSystem, context: &mut ScaleContext, cache_key: CacheKey) -> Option<SwashImage> {
let font = match font_system.get_font(cache_key.font_id) {
Some(some) => some,
None => {
log::warn!("did not find font {:?}", cache_key.font_id);
return None;
},
};
// Build the scaler
let mut scaler = context
.builder(font.as_swash())
.size(cache_key.font_size as f32)
.hint(true)
.build();
// Compute the fractional offset-- you'll likely want to quantize this
// in a real renderer
let offset =
Vector::new(cache_key.x_bin.as_float(), cache_key.y_bin.as_float());
// Select our source order
Render::new(&[
// Color outline with the first palette
Source::ColorOutline(0),
// Color bitmap with best fit selection mode
Source::ColorBitmap(StrikeWith::BestFit),
// Standard scalable outline
Source::Outline,
])
// Select a subpixel format
.format(Format::Alpha)
// Apply the fractional offset
.offset(offset)
// Render the image
.render(&mut scaler, cache_key.glyph_id)
}
/// Cache for rasterizing with the swash scaler
pub struct SwashCache<'a> {
font_system: &'a FontSystem,
context: ScaleContext,
pub image_cache: HashMap<CacheKey, Option<SwashImage>>,
}
impl<'a> SwashCache<'a> {
/// Create a new swash cache
pub fn new(font_system: &'a FontSystem) -> Self {
Self {
font_system: font_system,
context: ScaleContext::new(),
image_cache: HashMap::new()
}
}
/// Create a swash Image from a cache key, without caching results
pub fn get_image_uncached(&mut self, cache_key: CacheKey) -> Option<SwashImage> {
swash_image(self.font_system, &mut self.context, cache_key)
}
/// Create a swash Image from a cache key, caching results
pub fn get_image(&mut self, cache_key: CacheKey) -> &Option<SwashImage> {
self.image_cache.entry(cache_key).or_insert_with(|| {
swash_image(self.font_system, &mut self.context, cache_key)
})
}
/// Enumerate pixels in an Image, use `with_image` for better performance
pub fn with_pixels<F: FnMut(i32, i32, Color)>(
&mut self,
cache_key: CacheKey,
base: Color,
mut f: F
) {
if let Some(image) = self.get_image(cache_key) {
let x = image.placement.left;
let y = -image.placement.top;
match image.content {
Content::Mask => {
let mut i = 0;
for off_y in 0..image.placement.height as i32 {
for off_x in 0..image.placement.width as i32 {
//TODO: blend base alpha?
f(
x + off_x,
y + off_y,
Color(
((image.data[i] as u32) << 24) |
base.0 & 0xFFFFFF
)
);
i += 1;
}
}
}
Content::Color => {
let mut i = 0;
for off_y in 0..image.placement.height as i32 {
for off_x in 0..image.placement.width as i32 {
//TODO: blend base alpha?
f(
x + off_x,
y + off_y,
Color::rgba(
image.data[i],
image.data[i + 1],
image.data[i + 2],
image.data[i + 3]
)
);
i += 4;
}
}
}
Content::SubpixelMask => {
log::warn!("TODO: SubpixelMask");
}
}
}
}
}