// 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<'a>, context: &mut ScaleContext, cache_key: CacheKey) -> Option { 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<'a>, context: ScaleContext, pub image_cache: HashMap>, } impl<'a> SwashCache<'a> { /// Create a new swash cache pub fn new(font_system: &'a FontSystem<'a>) -> 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 { 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 { 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( &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 + 2], image.data[i + 1], image.data[i], image.data[i + 3] ) ); i += 4; } } } Content::SubpixelMask => { log::warn!("TODO: SubpixelMask"); } } } } }