blob: feb124b2b05d1ef7b276a67e4064635ae7ed372e [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use std::collections::HashMap;
use super::{Encoding, StreamOffsets};
use fello::scale::Scaler;
use fello::GlyphId;
use peniko::{Fill, Style};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default, Debug)]
pub struct GlyphKey {
pub font_id: u64,
pub font_index: u32,
pub glyph_id: u32,
pub font_size: u32,
pub hint: bool,
}
#[derive(Default)]
pub struct GlyphCache {
pub encoding: Encoding,
glyphs: HashMap<GlyphKey, CachedRange>,
}
impl GlyphCache {
pub fn clear(&mut self) {
self.encoding.reset(true);
self.glyphs.clear();
}
pub fn get_or_insert(
&mut self,
key: GlyphKey,
style: &Style,
scaler: &mut Scaler,
) -> Option<CachedRange> {
let is_fill = matches!(style, Style::Fill(_));
let is_var = !scaler.normalized_coords().is_empty();
let encoding_cache = &mut self.encoding;
let mut encode_glyph = || {
let start = encoding_cache.stream_offsets();
match style {
Style::Fill(Fill::NonZero) => encoding_cache.encode_fill(false),
Style::Fill(Fill::EvenOdd) => encoding_cache.encode_fill(true),
Style::Stroke(stroke) => encoding_cache.encode_stroke(stroke.width, stroke.scale),
}
let mut path = crate::glyph::PathEncoderPen(encoding_cache.encode_path(is_fill));
scaler
.outline(GlyphId::new(key.glyph_id as u16), &mut path)
.ok()?;
if path.0.finish(false) == 0 {
return None;
}
let end = encoding_cache.stream_offsets();
Some(CachedRange { start, end })
};
// For now, only cache non-zero filled, non-variable glyphs so we don't need to keep style
// as part of the key.
let range = if matches!(style, Style::Fill(Fill::NonZero)) && !is_var {
use std::collections::hash_map::Entry;
match self.glyphs.entry(key) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => *entry.insert(encode_glyph()?),
}
} else {
encode_glyph()?
};
Some(range)
}
}
#[derive(Copy, Clone, Default, Debug)]
pub struct CachedRange {
pub start: StreamOffsets,
pub end: StreamOffsets,
}
impl CachedRange {
pub fn len(&self) -> StreamOffsets {
StreamOffsets {
path_tags: self.end.path_tags - self.start.path_tags,
path_data: self.end.path_data - self.start.path_data,
draw_tags: self.end.draw_tags - self.start.draw_tags,
draw_data: self.end.draw_data - self.start.draw_data,
transforms: self.end.transforms - self.start.transforms,
linewidths: self.end.linewidths - self.start.linewidths,
}
}
}