1use super::atlas::{AllocId, AtlasAllocator};
2use crate::{
3 fonts::{FontId, GlyphId},
4 geometry::{rect::RectI, vector::Vector2I},
5 platform::{FontSystem, RasterizationOptions},
6 scene::ImageGlyph,
7 ImageData,
8};
9use anyhow::anyhow;
10use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
11use ordered_float::OrderedFloat;
12use std::{collections::HashMap, mem, sync::Arc};
13
14#[derive(Hash, Eq, PartialEq)]
15struct GlyphDescriptor {
16 font_id: FontId,
17 font_size: OrderedFloat<f32>,
18 glyph_id: GlyphId,
19}
20
21pub struct ImageCache {
22 prev_frame: HashMap<usize, (AllocId, RectI)>,
23 curr_frame: HashMap<usize, (AllocId, RectI)>,
24 image_glyphs: HashMap<GlyphDescriptor, Option<(AllocId, RectI, Vector2I)>>,
25 atlases: AtlasAllocator,
26 scale_factor: f32,
27 fonts: Arc<dyn FontSystem>,
28}
29
30impl ImageCache {
31 pub fn new(
32 device: metal::Device,
33 size: Vector2I,
34 scale_factor: f32,
35 fonts: Arc<dyn FontSystem>,
36 ) -> Self {
37 let descriptor = TextureDescriptor::new();
38 descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
39 descriptor.set_width(size.x() as u64);
40 descriptor.set_height(size.y() as u64);
41 Self {
42 prev_frame: Default::default(),
43 curr_frame: Default::default(),
44 image_glyphs: Default::default(),
45 atlases: AtlasAllocator::new(device, descriptor),
46 scale_factor,
47 fonts,
48 }
49 }
50
51 pub fn set_scale_factor(&mut self, scale_factor: f32) {
52 if scale_factor != self.scale_factor {
53 self.scale_factor = scale_factor;
54 for (_, glyph) in self.image_glyphs.drain() {
55 if let Some((alloc_id, _, _)) = glyph {
56 self.atlases.deallocate(alloc_id);
57 }
58 }
59 }
60 }
61
62 pub fn render(&mut self, image: &ImageData) -> (AllocId, RectI) {
63 let (alloc_id, atlas_bounds) = self
64 .prev_frame
65 .remove(&image.id)
66 .or_else(|| self.curr_frame.get(&image.id).copied())
67 .or_else(|| self.atlases.upload(image.size(), image.as_bytes()))
68 .ok_or_else(|| anyhow!("could not upload image of size {:?}", image.size()))
69 .unwrap();
70 self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
71 (alloc_id, atlas_bounds)
72 }
73
74 pub fn render_glyph(&mut self, image_glyph: &ImageGlyph) -> Option<(AllocId, RectI, Vector2I)> {
75 *self
76 .image_glyphs
77 .entry(GlyphDescriptor {
78 font_id: image_glyph.font_id,
79 font_size: OrderedFloat(image_glyph.font_size),
80 glyph_id: image_glyph.id,
81 })
82 .or_insert_with(|| {
83 let (glyph_bounds, bytes) = self.fonts.rasterize_glyph(
84 image_glyph.font_id,
85 image_glyph.font_size,
86 image_glyph.id,
87 Default::default(),
88 self.scale_factor,
89 RasterizationOptions::Bgra,
90 )?;
91 let (alloc_id, atlas_bounds) = self
92 .atlases
93 .upload(glyph_bounds.size(), &bytes)
94 .ok_or_else(|| {
95 anyhow!(
96 "could not upload image glyph of size {:?}",
97 glyph_bounds.size()
98 )
99 })
100 .unwrap();
101 Some((alloc_id, atlas_bounds, glyph_bounds.origin()))
102 })
103 }
104
105 pub fn finish_frame(&mut self) {
106 mem::swap(&mut self.prev_frame, &mut self.curr_frame);
107 for (_, (id, _)) in self.curr_frame.drain() {
108 self.atlases.deallocate(id);
109 }
110 }
111
112 pub fn atlas_texture(&self, atlas_id: usize) -> Option<&TextureRef> {
113 self.atlases.texture(atlas_id)
114 }
115}