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*/