1use std::mem;
2use std::sync::{Arc, Weak};
3
4use anyhow::*;
5use image::{Rgba, RgbaImage};
6use rusttype::{point, vector};
7use smallvec::SmallVec;
8
9use crate::atlas::*;
10use crate::draw::*;
11use crate::layout::Rectangle;
12use crate::text::Text;
13
14type GlyphCache = rusttype::gpu_cache::Cache<'static>;
15pub(crate) type Font = rusttype::Font<'static>;
16pub(crate) type FontId = usize;
17
18pub struct Cache {
20 #[allow(unused)]
21 size: usize,
22 glyphs: GlyphCache,
23 textures: Vec<TextureSlot>,
24 updates: Vec<Update>,
25 font_id_counter: usize,
26 image_id_counter: usize,
27}
28
29enum TextureSlot {
30 Atlas(Atlas<Weak<usize>>),
31 Big,
32}
33
34impl Cache {
35 pub fn new(size: usize) -> Cache {
38 let glyphs = GlyphCache::builder().dimensions(size as u32, size as u32).build();
39
40 let atlas = Atlas::new(size);
41
42 Cache {
43 size,
44 glyphs,
45 textures: vec![
46 TextureSlot::Big,
48 TextureSlot::Atlas(atlas),
50 ],
51 updates: vec![
52 Update::Texture {
54 id: 0,
55 size: [size as u32, size as u32],
56 data: Vec::new(),
57 atlas: true,
58 },
59 Update::Texture {
61 id: 1,
62 size: [size as u32, size as u32],
63 data: Vec::new(),
64 atlas: true,
65 },
66 ],
67 font_id_counter: 0,
68 image_id_counter: 1,
69 }
70 }
71
72 pub fn take_updates(&mut self) -> Vec<Update> {
74 mem::take(&mut self.updates)
75 }
76
77 pub(crate) fn draw_text<F: FnMut(Rectangle, Rectangle)>(
78 &mut self,
79 text: &Text,
80 rect: Rectangle,
81 mut place_glyph: F,
82 ) {
83 let start = point(rect.left, rect.top);
84
85 let mut placed_glyphs = Vec::with_capacity(text.text.len());
86 text.layout(rect, |g, x, _, y| {
87 placed_glyphs.push(g.positioned(start + vector(x, y)));
88 });
89
90 for g in placed_glyphs.iter() {
91 self.glyphs.queue_glyph(text.font.id as usize, g.clone());
92 }
93
94 let updates = &mut self.updates;
95 self.glyphs
96 .cache_queued(|rect, data| {
97 let mut new_data = Vec::with_capacity(data.len() * 4);
98 for x in data {
99 new_data.push(255);
100 new_data.push(255);
101 new_data.push(255);
102 new_data.push(*x);
103 }
104
105 let update = Update::TextureSubresource {
106 id: 0,
107 offset: [rect.min.x, rect.min.y],
108 size: [rect.width(), rect.height()],
109 data: new_data,
110 };
111
112 updates.push(update);
113 })
114 .unwrap();
115
116 for g in placed_glyphs.iter() {
117 if let Some((uv, pos)) = self.glyphs.rect_for(text.font.id as usize, g).unwrap() {
118 place_glyph(
119 Rectangle {
120 left: uv.min.x,
121 top: uv.min.y,
122 right: uv.max.x,
123 bottom: uv.max.y,
124 },
125 Rectangle {
126 left: pos.min.x as f32,
127 top: pos.min.y as f32,
128 right: pos.max.x as f32,
129 bottom: pos.max.y as f32,
130 },
131 );
132 }
133 }
134 }
135
136 pub(crate) fn load_image(&mut self, image: RgbaImage) -> ImageData {
137 let size = Rectangle {
138 left: 0.0,
139 top: 0.0,
140 right: image.width() as f32,
141 bottom: image.height() as f32,
142 };
143 let (texture, cache_id, texcoords) = self.insert_image(image);
144 ImageData {
145 texture,
146 cache_id,
147 texcoords,
148 size,
149 }
150 }
151
152 pub(crate) fn load_patch(&mut self, mut image: RgbaImage) -> Patch {
153 let black = Rgba([0u8, 0u8, 0u8, 255u8]);
155
156 let mut h_stretch = SmallVec::<[(f32, f32); 2]>::new();
157 let mut h_content = (1.0, 0.0);
158 let mut v_stretch = SmallVec::<[(f32, f32); 2]>::new();
159 let mut v_content = (1.0, 0.0);
160 let mut h_current_stretch = None;
161 let mut v_current_stretch = None;
162
163 for x in 1..image.width() - 1 {
165 let h_begin = (x - 1) as f32 / (image.width() - 2) as f32;
166 let h_end = (x) as f32 / (image.width() - 2) as f32;
167
168 if image[(x, 0)] == black {
170 h_current_stretch = Some(h_current_stretch.map_or_else(|| (h_begin, h_end), |(s, _)| (s, h_end)));
171 } else if let Some(s) = h_current_stretch.take() {
172 h_stretch.push(s);
173 }
174
175 if image[(x, image.height() - 1)] == black {
177 h_content.0 = h_begin.min(h_content.0);
178 h_content.1 = h_end.max(h_content.1);
179 }
180 }
181
182 for y in 1..image.height() - 1 {
184 let v_begin = (y - 1) as f32 / (image.height() - 2) as f32;
185 let v_end = (y) as f32 / (image.height() - 2) as f32;
186
187 if image[(0, y)] == black {
189 v_current_stretch = Some(v_current_stretch.map_or_else(|| (v_begin, v_end), |(s, _)| (s, v_end)));
190 } else if let Some(s) = v_current_stretch.take() {
191 v_stretch.push(s);
192 }
193
194 if image[(image.width() - 1, y)] == black {
196 v_content.0 = v_begin.min(v_content.0);
197 v_content.1 = v_end.max(v_content.1);
198 }
199 }
200
201 if let Some(s) = h_current_stretch.take() {
202 h_stretch.push(s);
203 }
204 if let Some(s) = v_current_stretch.take() {
205 v_stretch.push(s);
206 }
207
208 let patch_width = image.width() - 2;
210 let patch_height = image.height() - 2;
211 let image = image::imageops::crop(&mut image, 1, 1, patch_width, patch_height).to_image();
212 let size = Rectangle {
213 left: 0.0,
214 top: 0.0,
215 right: image.width() as f32,
216 bottom: image.height() as f32,
217 };
218 let (texture, cache_id, texcoords) = self.insert_image(image);
219
220 Patch {
221 image: ImageData {
222 texture,
223 cache_id,
224 texcoords,
225 size,
226 },
227 h_stretch,
228 v_stretch,
229 h_content,
230 v_content,
231 }
232 }
233
234 pub(crate) fn load_font<D: Into<Vec<u8>>>(&mut self, data: D) -> Result<crate::text::Font> {
235 let inner = Font::try_from_vec(data.into()).ok_or_else(|| anyhow!("Invalid .ttf data"))?;
236
237 let id = self.font_id_counter;
238 self.font_id_counter += 1;
239
240 Ok(crate::text::Font { inner, id, tex_slot: 0 })
241 }
242
243 fn insert_image(&mut self, image: image::RgbaImage) -> (usize, Arc<usize>, Rectangle) {
244 for slot in self.textures.iter_mut() {
245 if let TextureSlot::Atlas(atlas) = slot {
246 atlas.remove_expired();
247 }
248 }
249
250 let image_id = Arc::new(self.image_id_counter);
251 self.image_id_counter += 1;
252
253 let slot = self
254 .textures
255 .iter_mut()
256 .enumerate()
257 .filter_map(|(index, slot)| match slot {
258 TextureSlot::Atlas(atlas) => {
259 let image_size = image.width().max(image.height()) as usize;
260 atlas
261 .insert(Arc::downgrade(&image_id), image_size)
262 .ok()
263 .map(|area| (area, atlas.size() as f32, index))
264 }
265 TextureSlot::Big => None,
266 })
267 .next();
268
269 if let Some((mut area, atlas_size, tex_id)) = slot {
270 area.right = area.left + image.width() as usize;
271 area.bottom = area.top + image.height() as usize;
272
273 let update = Update::TextureSubresource {
274 id: tex_id,
275 offset: [area.left as u32, area.top as u32],
276 size: [image.width(), image.height()],
277 data: image.to_vec(),
278 };
279 self.updates.push(update);
280
281 (
282 tex_id,
283 image_id,
284 Rectangle {
285 left: area.left as f32 / atlas_size,
286 top: area.top as f32 / atlas_size,
287 right: area.right as f32 / atlas_size,
288 bottom: area.bottom as f32 / atlas_size,
289 },
290 )
291 } else {
292 let tex_id = self.textures.len();
293
294 let update = Update::Texture {
295 id: tex_id,
296 size: [image.width(), image.height()],
297 data: image.to_vec(),
298 atlas: false,
299 };
300
301 self.updates.push(update);
302 self.textures.push(TextureSlot::Big);
303
304 (tex_id, image_id, Rectangle::from_wh(1.0, 1.0))
305 }
306 }
307}