Implement basic glyph cache in iced_tiny_skia
This commit is contained in:
parent
3105ad2e00
commit
c1ff803b8f
2 changed files with 120 additions and 65 deletions
|
|
@ -27,7 +27,7 @@ path = "../graphics"
|
||||||
[dependencies.cosmic-text]
|
[dependencies.cosmic-text]
|
||||||
features = ["std", "swash"]
|
features = ["std", "swash"]
|
||||||
git = "https://github.com/hecrj/cosmic-text"
|
git = "https://github.com/hecrj/cosmic-text"
|
||||||
rev = "dc83efbf00a2efb4118403538e8a47bfd69c3e5e"
|
rev = "81080c1b9498933b43c1889601a7ea6a3d16e161"
|
||||||
|
|
||||||
[dependencies.twox-hash]
|
[dependencies.twox-hash]
|
||||||
version = "1.6"
|
version = "1.6"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use std::sync::Arc;
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
system: Option<System>,
|
system: Option<System>,
|
||||||
|
glyph_cache: GlyphCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ouroboros::self_referencing]
|
#[ouroboros::self_referencing]
|
||||||
|
|
@ -45,6 +46,7 @@ impl Pipeline {
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
),
|
),
|
||||||
|
glyph_cache: GlyphCache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,76 +118,20 @@ impl Pipeline {
|
||||||
|
|
||||||
for run in buffer.layout_runs() {
|
for run in buffer.layout_runs() {
|
||||||
for glyph in run.glyphs {
|
for glyph in run.glyphs {
|
||||||
// TODO: Outline support
|
if let Some((buffer, placement)) = self
|
||||||
if let Some(image) = swash.get_image(glyph.cache_key) {
|
.glyph_cache
|
||||||
let glyph_size = image.placement.width as usize
|
.allocate(glyph.cache_key, color, &mut swash)
|
||||||
* image.placement.height as usize;
|
{
|
||||||
|
|
||||||
if glyph_size == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Cache glyph rasterization
|
|
||||||
let mut buffer = vec![0u32; glyph_size];
|
|
||||||
|
|
||||||
match image.content {
|
|
||||||
cosmic_text::SwashContent::Mask => {
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
// TODO: Blend alpha
|
|
||||||
let [r, g, b, _a] = color.into_rgba8();
|
|
||||||
|
|
||||||
for _y in 0..image.placement.height {
|
|
||||||
for _x in 0..image.placement.width {
|
|
||||||
buffer[i] =
|
|
||||||
tiny_skia::ColorU8::from_rgba(
|
|
||||||
b,
|
|
||||||
g,
|
|
||||||
r,
|
|
||||||
image.data[i],
|
|
||||||
)
|
|
||||||
.premultiply()
|
|
||||||
.get();
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cosmic_text::SwashContent::Color => {
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
for _y in 0..image.placement.height {
|
|
||||||
for _x in 0..image.placement.width {
|
|
||||||
// TODO: Blend alpha
|
|
||||||
buffer[i >> 2] =
|
|
||||||
tiny_skia::ColorU8::from_rgba(
|
|
||||||
image.data[i + 2],
|
|
||||||
image.data[i + 1],
|
|
||||||
image.data[i],
|
|
||||||
image.data[i + 3],
|
|
||||||
)
|
|
||||||
.premultiply()
|
|
||||||
.get();
|
|
||||||
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cosmic_text::SwashContent::SubpixelMask => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pixmap = tiny_skia::PixmapRef::from_bytes(
|
let pixmap = tiny_skia::PixmapRef::from_bytes(
|
||||||
bytemuck::cast_slice(&buffer),
|
bytemuck::cast_slice(&buffer),
|
||||||
image.placement.width,
|
placement.width,
|
||||||
image.placement.height,
|
placement.height,
|
||||||
)
|
)
|
||||||
.expect("Create glyph pixel map");
|
.expect("Create glyph pixel map");
|
||||||
|
|
||||||
pixels.draw_pixmap(
|
pixels.draw_pixmap(
|
||||||
x as i32 + glyph.x_int + image.placement.left,
|
x as i32 + glyph.x_int + placement.left,
|
||||||
y as i32 - glyph.y_int - image.placement.top
|
y as i32 - glyph.y_int - placement.top
|
||||||
+ run.line_y as i32,
|
+ run.line_y as i32,
|
||||||
pixmap,
|
pixmap,
|
||||||
&tiny_skia::PixmapPaint::default(),
|
&tiny_skia::PixmapPaint::default(),
|
||||||
|
|
@ -203,6 +149,8 @@ impl Pipeline {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_render_cache_mut(|cache| cache.trim());
|
.with_render_cache_mut(|cache| cache.trim());
|
||||||
|
|
||||||
|
self.glyph_cache.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn measure(
|
pub fn measure(
|
||||||
|
|
@ -283,6 +231,113 @@ fn to_family(font: Font) -> cosmic_text::Family<'static> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
struct GlyphCache {
|
||||||
|
entries: FxHashMap<
|
||||||
|
(cosmic_text::CacheKey, [u8; 3]),
|
||||||
|
(Vec<u32>, cosmic_text::Placement),
|
||||||
|
>,
|
||||||
|
recently_used: FxHashSet<(cosmic_text::CacheKey, [u8; 3])>,
|
||||||
|
trim_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlyphCache {
|
||||||
|
fn new() -> Self {
|
||||||
|
GlyphCache::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate(
|
||||||
|
&mut self,
|
||||||
|
cache_key: cosmic_text::CacheKey,
|
||||||
|
color: Color,
|
||||||
|
swash: &mut cosmic_text::SwashCache<'_>,
|
||||||
|
) -> Option<(&[u8], cosmic_text::Placement)> {
|
||||||
|
let [r, g, b, _a] = color.into_rgba8();
|
||||||
|
let key = (cache_key, [r, g, b]);
|
||||||
|
|
||||||
|
if let hash_map::Entry::Vacant(entry) = self.entries.entry(key) {
|
||||||
|
// TODO: Outline support
|
||||||
|
let image = swash.get_image(cache_key).as_ref()?;
|
||||||
|
|
||||||
|
let glyph_size = image.placement.width as usize
|
||||||
|
* image.placement.height as usize;
|
||||||
|
|
||||||
|
if glyph_size == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Cache glyph rasterization
|
||||||
|
let mut buffer = vec![0u32; glyph_size];
|
||||||
|
|
||||||
|
match image.content {
|
||||||
|
cosmic_text::SwashContent::Mask => {
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
// TODO: Blend alpha
|
||||||
|
|
||||||
|
for _y in 0..image.placement.height {
|
||||||
|
for _x in 0..image.placement.width {
|
||||||
|
buffer[i] = tiny_skia::ColorU8::from_rgba(
|
||||||
|
b,
|
||||||
|
g,
|
||||||
|
r,
|
||||||
|
image.data[i],
|
||||||
|
)
|
||||||
|
.premultiply()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cosmic_text::SwashContent::Color => {
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
for _y in 0..image.placement.height {
|
||||||
|
for _x in 0..image.placement.width {
|
||||||
|
// TODO: Blend alpha
|
||||||
|
buffer[i >> 2] = tiny_skia::ColorU8::from_rgba(
|
||||||
|
image.data[i + 2],
|
||||||
|
image.data[i + 1],
|
||||||
|
image.data[i],
|
||||||
|
image.data[i + 3],
|
||||||
|
)
|
||||||
|
.premultiply()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cosmic_text::SwashContent::SubpixelMask => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.insert((buffer, image.placement));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.recently_used.insert(key);
|
||||||
|
|
||||||
|
self.entries.get(&key).map(|(buffer, placement)| {
|
||||||
|
(bytemuck::cast_slice(buffer.as_slice()), *placement)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trim(&mut self) {
|
||||||
|
if self.trim_count > 300 {
|
||||||
|
self.entries
|
||||||
|
.retain(|key, _| self.recently_used.contains(key));
|
||||||
|
|
||||||
|
self.recently_used.clear();
|
||||||
|
|
||||||
|
self.trim_count = 0;
|
||||||
|
} else {
|
||||||
|
self.trim_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Cache<'a> {
|
struct Cache<'a> {
|
||||||
entries: FxHashMap<KeyHash, cosmic_text::Buffer<'a>>,
|
entries: FxHashMap<KeyHash, cosmic_text::Buffer<'a>>,
|
||||||
recently_used: FxHashSet<KeyHash>,
|
recently_used: FxHashSet<KeyHash>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue