makepad_draw/text/rasterizer.rs
1use super::{
2 font::{Font, GlyphId},
3 font_atlas::{ColorAtlas, GlyphImage, GlyphImageKey, GrayscaleAtlas},
4 geom::{Point, Rect, Size},
5 image::Image,
6 sdfer,
7 sdfer::Sdfer,
8};
9
10#[derive(Debug)]
11pub struct Rasterizer {
12 sdfer: Sdfer,
13 grayscale_atlas: GrayscaleAtlas,
14 color_atlas: ColorAtlas,
15}
16
17impl Rasterizer {
18 pub fn new(settings: Settings) -> Self {
19 Self {
20 sdfer: Sdfer::new(settings.sdfer),
21 grayscale_atlas: GrayscaleAtlas::new(settings.grayscale_atlas_size),
22 color_atlas: ColorAtlas::new(settings.color_atlas_size),
23 }
24 }
25
26 pub fn sdfer(&self) -> &Sdfer {
27 &self.sdfer
28 }
29
30 pub fn grayscale_atlas(&self) -> &GrayscaleAtlas {
31 &self.grayscale_atlas
32 }
33
34 pub fn color_atlas(&self) -> &ColorAtlas {
35 &self.color_atlas
36 }
37
38 pub fn grayscale_atlas_mut(&mut self) -> &mut GrayscaleAtlas {
39 &mut self.grayscale_atlas
40 }
41
42 pub fn color_atlas_mut(&mut self) -> &mut ColorAtlas {
43 &mut self.color_atlas
44 }
45
46 pub fn rasterize_glyph(
47 &mut self,
48 font: &Font,
49 glyph_id: GlyphId,
50 dpxs_per_em: f32,
51 ) -> Option<RasterizedGlyph> {
52 if let Some(rasterized_glyph) = self.rasterize_glyph_outline(font, glyph_id, dpxs_per_em) {
53 return Some(rasterized_glyph);
54 };
55 if let Some(rasterized_glyph) =
56 self.rasterize_glyph_raster_image(font, glyph_id, dpxs_per_em)
57 {
58 return Some(rasterized_glyph);
59 }
60 None
61 }
62
63 fn rasterize_glyph_outline(
64 &mut self,
65 font: &Font,
66 glyph_id: GlyphId,
67 dpxs_per_em: f32,
68 ) -> Option<RasterizedGlyph> {
69 let dpxs_per_em = if dpxs_per_em < 32.0 { 32.0 } else { 64.0 };
70 let dpxs_per_em = dpxs_per_em * 2.0;
71 let mut outline = None;
72 let bounds_in_ems = font.glyph_outline_bounds_in_ems(glyph_id, &mut outline)?;
73 let atlas_image_size = glyph_outline_image_size(bounds_in_ems.size, dpxs_per_em);
74 let atlas_image_padding = self.sdfer.settings().padding;
75 let atlas_image_bounds =
76 match self
77 .grayscale_atlas
78 .get_or_allocate_glyph_image(GlyphImageKey {
79 font_id: font.id(),
80 glyph_id,
81 size: atlas_image_size + Size::from(self.sdfer.settings().padding) * 2,
82 })? {
83 GlyphImage::Cached(rect) => rect,
84 GlyphImage::Allocated(mut sdf) => {
85 let outline = outline.unwrap_or_else(|| font.glyph_outline(glyph_id).unwrap());
86 let mut coverage = Image::new(atlas_image_size);
87 outline.rasterize(
88 dpxs_per_em,
89 &mut coverage.subimage_mut(atlas_image_size.into()),
90 );
91 self.sdfer
92 .coverage_to_sdf(&coverage.subimage(atlas_image_size.into()), &mut sdf);
93 sdf.bounds()
94 }
95 };
96
97 return Some(RasterizedGlyph {
98 atlas_kind: AtlasKind::Grayscale,
99 atlas_size: self.grayscale_atlas().size(),
100 atlas_image_bounds,
101 atlas_image_padding,
102 origin_in_dpxs: bounds_in_ems.origin * dpxs_per_em,
103 dpxs_per_em,
104 });
105 }
106
107 fn rasterize_glyph_raster_image(
108 &mut self,
109 font: &Font,
110 glyph_id: GlyphId,
111 dpxs_per_em: f32,
112 ) -> Option<RasterizedGlyph> {
113 const PADDING: usize = 2;
114
115 let raster_image = font.glyph_raster_image(glyph_id, dpxs_per_em)?;
116 let atlas_image_bounds =
117 match self
118 .color_atlas
119 .get_or_allocate_glyph_image(GlyphImageKey {
120 font_id: font.id(),
121 glyph_id,
122 size: raster_image.decode_size() + Size::from(2 * PADDING),
123 })? {
124 GlyphImage::Cached(rect) => rect,
125 GlyphImage::Allocated(mut image) => {
126 let size = image.size();
127 image = image.subimage_mut(Rect::from(size).unpad(PADDING));
128 raster_image.decode(&mut image);
129 image.bounds()
130 }
131 };
132 return Some(RasterizedGlyph {
133 atlas_kind: AtlasKind::Color,
134 atlas_size: self.color_atlas.size(),
135 atlas_image_bounds,
136 atlas_image_padding: PADDING,
137 origin_in_dpxs: raster_image.origin_in_dpxs(),
138 dpxs_per_em: raster_image.dpxs_per_em(),
139 });
140 }
141}
142
143#[derive(Clone, Copy, Debug)]
144pub struct Settings {
145 pub sdfer: sdfer::Settings,
146 pub grayscale_atlas_size: Size<usize>,
147 pub color_atlas_size: Size<usize>,
148}
149
150#[derive(Clone, Copy, Debug)]
151pub struct RasterizedGlyph {
152 pub atlas_kind: AtlasKind,
153 pub atlas_size: Size<usize>,
154 pub atlas_image_bounds: Rect<usize>,
155 pub atlas_image_padding: usize,
156 pub origin_in_dpxs: Point<f32>,
157 pub dpxs_per_em: f32,
158}
159
160#[derive(Clone, Copy, Debug)]
161pub enum AtlasKind {
162 Grayscale,
163 Color,
164}
165
166fn glyph_outline_image_size(size_in_ems: Size<f32>, dpxs_per_em: f32) -> Size<usize> {
167 let size_in_dpxs = size_in_ems * dpxs_per_em;
168 Size::new(
169 size_in_dpxs.width.ceil() as usize,
170 size_in_dpxs.height.ceil() as usize,
171 )
172}
173
174/*
175use {
176 crate::{
177 font_atlas::CxFontAtlas,
178 font_loader::{FontId, FontLoader},
179 sdf_glyph_rasterizer::SdfGlyphRasterizer,
180 svg_glyph_rasterizer::SvgGlyphRasterizer,
181 },
182 makepad_platform::*,
183 std::{
184 collections::HashMap,
185 fs::{File, OpenOptions},
186 io::{self, Read, Write},
187 path::Path,
188 },
189};
190
191#[derive(Debug)]
192pub struct GlyphRasterizer {
193 sdf_glyph_rasterizer: SdfGlyphRasterizer,
194 svg_glyph_rasterizer: SvgGlyphRasterizer,
195 cache: Cache,
196}
197
198impl GlyphRasterizer {
199 pub fn new(cache_dir: Option<&Path>) -> Self {
200 Self {
201 sdf_glyph_rasterizer: SdfGlyphRasterizer::new(),
202 svg_glyph_rasterizer: SvgGlyphRasterizer::new(),
203 cache: Cache::new(cache_dir).expect("failed to load glyph raster cache"),
204 }
205 }
206
207 pub fn get_or_rasterize_glyph(
208 &mut self,
209 font_loader: &mut FontLoader,
210 font_atlas: &mut CxFontAtlas,
211 Command {
212 mode,
213 params:
214 params @ Params {
215 font_id,
216 atlas_page_id,
217 glyph_id,
218 },
219 ..
220 }: Command,
221 ) -> RasterizedGlyph<'_> {
222 let font = font_loader[font_id].as_mut().unwrap();
223 let atlas_page = &font.atlas_pages[atlas_page_id];
224 let font_size = atlas_page.font_size_in_device_pixels;
225 let font_path = font_loader.path(font_id).unwrap();
226 let key = CacheKey::new(&font_path, glyph_id, font_size);
227 if !self.cache.contains_key(&key) {
228 self.cache
229 .insert_with(key, |output| match mode {
230 Mode::Sdf => self.sdf_glyph_rasterizer.rasterize_sdf_glyph(
231 font_loader,
232 font_atlas,
233 params,
234 output,
235 ),
236 Mode::Svg => self.svg_glyph_rasterizer.rasterize_svg_glyph(
237 font_loader,
238 font_atlas,
239 params,
240 output,
241 ),
242 })
243 .expect("failed to update glyph raster cache")
244 }
245 self.cache.get(key).unwrap()
246 }
247}
248
249#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
250pub struct Command {
251 pub mode: Mode,
252 pub params: Params,
253}
254
255#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
256pub enum Mode {
257 Svg,
258 Sdf,
259}
260
261#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
262pub struct Params {
263 pub font_id: FontId,
264 pub atlas_page_id: usize,
265 pub glyph_id: usize,
266}
267
268#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
269pub struct RasterizedGlyph<'a> {
270 pub size: SizeUsize,
271 pub bytes: &'a [u8],
272}
273
274#[derive(Debug)]
275struct Cache {
276 data: Vec<u8>,
277 data_file: Option<File>,
278 index: HashMap<CacheKey, CacheIndexEntry>,
279 index_file: Option<File>,
280}
281
282impl Cache {
283 fn new(dir: Option<&Path>) -> io::Result<Self> {
284 let mut data_file = match dir {
285 Some(dir) => Some(
286 OpenOptions::new()
287 .create(true)
288 .read(true)
289 .write(true)
290 .open(dir.join("glyph_raster_data"))?,
291 ),
292 None => None,
293 };
294
295 let mut data = Vec::new();
296 if let Some(data_file) = &mut data_file {
297 data_file.read_to_end(&mut data)?;
298 }
299
300 let mut index_file = match dir {
301 Some(dir) => Some(
302 OpenOptions::new()
303 .create(true)
304 .read(true)
305 .write(true)
306 .open(dir.join("glyph_raster_index"))?,
307 ),
308 None => None,
309 };
310
311 let mut index = HashMap::new();
312 if let Some(index_file) = &mut index_file {
313 loop {
314 let mut buffer = [0; 32];
315 match index_file.read_exact(&mut buffer) {
316 Ok(_) => (),
317 Err(error) if error.kind() == io::ErrorKind::UnexpectedEof => break,
318 Err(error) => return Err(error),
319 }
320 index.insert(
321 CacheKey::from_bytes(buffer[0..8].try_into().unwrap()),
322 CacheIndexEntry::from_bytes(buffer[8..32].try_into().unwrap()),
323 );
324 }
325 }
326 Ok(Self {
327 data,
328 data_file,
329 index,
330 index_file,
331 })
332 }
333
334 fn contains_key(&self, key: &CacheKey) -> bool {
335 self.index.contains_key(key)
336 }
337
338 fn get(&self, key: CacheKey) -> Option<RasterizedGlyph<'_>> {
339 let CacheIndexEntry { size, offset, len } = self.index.get(&key).copied()?;
340 Some(RasterizedGlyph {
341 size,
342 bytes: &self.data[offset..][..len],
343 })
344 }
345
346 fn insert_with(
347 &mut self,
348 key: CacheKey,
349 f: impl FnOnce(&mut Vec<u8>) -> SizeUsize,
350 ) -> io::Result<()> {
351 let offset = self.data.len();
352 let size = f(&mut self.data);
353 let len = self.data.len() - offset;
354 if let Some(data_file) = &mut self.data_file {
355 data_file.write_all(&self.data[offset..][..len])?;
356 }
357 let index_entry = CacheIndexEntry { size, offset, len };
358 self.index.insert(key, index_entry);
359 if let Some(index_file) = &mut self.index_file {
360 let mut buffer = [0; 32];
361 buffer[0..8].copy_from_slice(&key.to_bytes());
362 buffer[8..32].copy_from_slice(&index_entry.to_bytes());
363 index_file.write_all(&buffer)?;
364 }
365 Ok(())
366 }
367}
368
369#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
370struct CacheKey(LiveId);
371
372impl CacheKey {
373 fn new(font_path: &str, glyph_id: usize, font_size: f64) -> Self {
374 Self(
375 LiveId::empty()
376 .bytes_append(font_path.as_bytes())
377 .bytes_append(&glyph_id.to_ne_bytes())
378 .bytes_append(&font_size.to_ne_bytes()),
379 )
380 }
381
382 fn from_bytes(bytes: [u8; 8]) -> Self {
383 Self(LiveId(u64::from_be_bytes(bytes)))
384 }
385
386 fn to_bytes(self) -> [u8; 8] {
387 self.0 .0.to_be_bytes()
388 }
389}
390
391#[derive(Clone, Copy, Debug)]
392struct CacheIndexEntry {
393 size: SizeUsize,
394 offset: usize,
395 len: usize,
396}
397
398impl CacheIndexEntry {
399 fn from_bytes(bytes: [u8; 24]) -> Self {
400 Self {
401 size: SizeUsize {
402 width: u32::from_be_bytes(bytes[0..4].try_into().unwrap())
403 .try_into()
404 .unwrap(),
405 height: u32::from_be_bytes(bytes[4..8].try_into().unwrap())
406 .try_into()
407 .unwrap(),
408 },
409 offset: u64::from_be_bytes(bytes[8..16].try_into().unwrap())
410 .try_into()
411 .unwrap(),
412 len: u64::from_be_bytes(bytes[16..24].try_into().unwrap())
413 .try_into()
414 .unwrap(),
415 }
416 }
417
418 fn to_bytes(self) -> [u8; 24] {
419 let mut bytes = [0; 24];
420 bytes[0..4].copy_from_slice(&u32::try_from(self.size.width).unwrap().to_be_bytes());
421 bytes[4..8].copy_from_slice(&u32::try_from(self.size.height).unwrap().to_be_bytes());
422 bytes[8..16].copy_from_slice(&u64::try_from(self.offset).unwrap().to_be_bytes());
423 bytes[16..24].copy_from_slice(&u64::try_from(self.len).unwrap().to_be_bytes());
424 bytes
425 }
426}
427*/