iced_graphics/text/
cache.rs

1//! Cache text.
2use crate::core::{Font, Size};
3use crate::text;
4
5use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
6use std::collections::hash_map;
7use std::hash::{Hash, Hasher};
8
9/// A store of recently used sections of text.
10#[derive(Debug, Default)]
11pub struct Cache {
12    entries: FxHashMap<KeyHash, Entry>,
13    aliases: FxHashMap<KeyHash, KeyHash>,
14    recently_used: FxHashSet<KeyHash>,
15}
16
17impl Cache {
18    /// Creates a new empty [`Cache`].
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Gets the text [`Entry`] with the given [`KeyHash`].
24    pub fn get(&self, key: &KeyHash) -> Option<&Entry> {
25        self.entries.get(key)
26    }
27
28    /// Allocates a text [`Entry`] if it is not already present in the [`Cache`].
29    pub fn allocate(
30        &mut self,
31        font_system: &mut cosmic_text::FontSystem,
32        key: Key<'_>,
33    ) -> (KeyHash, &mut Entry) {
34        let hash = key.hash(FxHasher::default());
35
36        if let Some(hash) = self.aliases.get(&hash) {
37            let _ = self.recently_used.insert(*hash);
38
39            return (*hash, self.entries.get_mut(hash).unwrap());
40        }
41
42        if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
43            let metrics = cosmic_text::Metrics::new(
44                key.size,
45                key.line_height.max(f32::MIN_POSITIVE),
46            );
47            let mut buffer = cosmic_text::Buffer::new(font_system, metrics);
48
49            buffer.set_size(
50                font_system,
51                Some(key.bounds.width),
52                Some(key.bounds.height.max(key.line_height)),
53            );
54            buffer.set_text(
55                font_system,
56                key.content,
57                &text::to_attributes(key.font),
58                text::to_shaping(key.shaping),
59            );
60
61            let bounds = text::measure(&buffer);
62            let _ = entry.insert(Entry {
63                buffer,
64                min_bounds: bounds,
65            });
66
67            for bounds in [
68                bounds,
69                Size {
70                    width: key.bounds.width,
71                    ..bounds
72                },
73            ] {
74                if key.bounds != bounds {
75                    let _ = self.aliases.insert(
76                        Key { bounds, ..key }.hash(FxHasher::default()),
77                        hash,
78                    );
79                }
80            }
81        }
82
83        let _ = self.recently_used.insert(hash);
84
85        (hash, self.entries.get_mut(&hash).unwrap())
86    }
87
88    /// Trims the [`Cache`].
89    ///
90    /// This will clear the sections of text that have not been used since the last `trim`.
91    pub fn trim(&mut self) {
92        self.entries
93            .retain(|key, _| self.recently_used.contains(key));
94
95        self.aliases
96            .retain(|_, value| self.recently_used.contains(value));
97
98        self.recently_used.clear();
99    }
100}
101
102/// A cache key representing a section of text.
103#[derive(Debug, Clone, Copy)]
104pub struct Key<'a> {
105    /// The content of the text.
106    pub content: &'a str,
107    /// The size of the text.
108    pub size: f32,
109    /// The line height of the text.
110    pub line_height: f32,
111    /// The [`Font`] of the text.
112    pub font: Font,
113    /// The bounds of the text.
114    pub bounds: Size,
115    /// The shaping strategy of the text.
116    pub shaping: text::Shaping,
117}
118
119impl Key<'_> {
120    fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
121        self.content.hash(&mut hasher);
122        self.size.to_bits().hash(&mut hasher);
123        self.line_height.to_bits().hash(&mut hasher);
124        self.font.hash(&mut hasher);
125        self.bounds.width.to_bits().hash(&mut hasher);
126        self.bounds.height.to_bits().hash(&mut hasher);
127        self.shaping.hash(&mut hasher);
128
129        hasher.finish()
130    }
131}
132
133/// The hash of a [`Key`].
134pub type KeyHash = u64;
135
136/// A cache entry.
137#[derive(Debug)]
138pub struct Entry {
139    /// The buffer of text, ready for drawing.
140    pub buffer: cosmic_text::Buffer,
141    /// The minimum bounds of the text.
142    pub min_bounds: Size,
143}