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 =
44                cosmic_text::Metrics::new(key.size, key.line_height.max(f32::MIN_POSITIVE));
45            let mut buffer = cosmic_text::Buffer::new(font_system, metrics);
46
47            buffer.set_size(
48                font_system,
49                Some(key.bounds.width),
50                Some(key.bounds.height.max(key.line_height)),
51            );
52            buffer.set_text(
53                font_system,
54                key.content,
55                &text::to_attributes(key.font),
56                text::to_shaping(key.shaping, key.content),
57                None,
58            );
59
60            let bounds = text::align(&mut buffer, font_system, key.align_x);
61
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
76                        .aliases
77                        .insert(Key { bounds, ..key }.hash(FxHasher::default()), hash);
78                }
79            }
80        }
81
82        let _ = self.recently_used.insert(hash);
83
84        (hash, self.entries.get_mut(&hash).unwrap())
85    }
86
87    /// Trims the [`Cache`].
88    ///
89    /// This will clear the sections of text that have not been used since the last `trim`.
90    pub fn trim(&mut self) {
91        self.entries
92            .retain(|key, _| self.recently_used.contains(key));
93
94        self.aliases
95            .retain(|_, value| self.recently_used.contains(value));
96
97        self.recently_used.clear();
98    }
99}
100
101/// A cache key representing a section of text.
102#[derive(Debug, Clone, Copy)]
103pub struct Key<'a> {
104    /// The content of the text.
105    pub content: &'a str,
106    /// The size of the text.
107    pub size: f32,
108    /// The line height of the text.
109    pub line_height: f32,
110    /// The [`Font`] of the text.
111    pub font: Font,
112    /// The bounds of the text.
113    pub bounds: Size,
114    /// The shaping strategy of the text.
115    pub shaping: text::Shaping,
116    /// The alignment of the text.
117    pub align_x: text::Alignment,
118}
119
120impl Key<'_> {
121    fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
122        self.content.hash(&mut hasher);
123        self.size.to_bits().hash(&mut hasher);
124        self.line_height.to_bits().hash(&mut hasher);
125        self.font.hash(&mut hasher);
126        self.bounds.width.to_bits().hash(&mut hasher);
127        self.bounds.height.to_bits().hash(&mut hasher);
128        self.shaping.hash(&mut hasher);
129        self.align_x.hash(&mut hasher);
130
131        hasher.finish()
132    }
133}
134
135/// The hash of a [`Key`].
136pub type KeyHash = u64;
137
138/// A cache entry.
139#[derive(Debug)]
140pub struct Entry {
141    /// The buffer of text, ready for drawing.
142    pub buffer: cosmic_text::Buffer,
143    /// The minimum bounds of the text.
144    pub min_bounds: Size,
145}