makepad_draw/text/
font_atlas.rs

1use {
2    super::{
3        font::{FontId, GlyphId},
4        geom::{Point, Rect, Size},
5        image::{Image, Bgra, Subimage, SubimageMut, R},
6        num::Zero,
7    },
8    std::{collections::HashMap, fs::File, io::BufWriter, path::Path, slice},
9};
10
11#[derive(Clone, Debug)]
12pub struct FontAtlas<T> {
13    needs_reset: bool,
14    image: Image<T>,
15    dirty_rect: Rect<usize>,
16    current_point: Point<usize>,
17    current_row_height: usize,
18    cached_glyph_image_rects: HashMap<GlyphImageKey, Rect<usize>>,
19}
20
21impl<T> FontAtlas<T> {
22    pub fn new(size: Size<usize>) -> Self
23    where
24        T: Clone + Default,
25    {
26        Self {
27            needs_reset: false,
28            image: Image::new(size),
29            dirty_rect: Rect::ZERO,
30            current_point: Point::ZERO,
31            current_row_height: 0,
32            cached_glyph_image_rects: HashMap::new(),
33        }
34    }
35
36    pub fn needs_reset(&self) -> bool {
37        self.needs_reset
38    }
39
40    pub fn size(&self) -> Size<usize> {
41        self.image.size()
42    }
43
44    pub fn dirty_rect(&self) -> Rect<usize> {
45        self.dirty_rect
46    }
47
48    pub fn image(&self) -> &Image<T> {
49        &self.image
50    }
51
52    pub unsafe fn replace_pixels(&mut self, pixels: Vec<T>) -> Vec<T> {
53        self.image.replace_pixels(pixels)
54    }
55
56    pub fn take_dirty_image(&mut self) -> Subimage<'_, T> {
57        let dirty_rect = self.dirty_rect;
58        self.dirty_rect = Rect::ZERO;
59        self.image.subimage(dirty_rect)
60    }
61
62    pub fn get_or_allocate_glyph_image(&mut self, key: GlyphImageKey) -> Option<GlyphImage<'_, T>> {
63        if let Some(rect) = self.cached_glyph_image_rects.get(&key) {
64            return Some(GlyphImage::Cached(*rect));
65        }
66        let rect = self.allocate_glyph_image(key.size)?;
67        self.cached_glyph_image_rects.insert(key.clone(), rect);
68        Some(GlyphImage::Allocated(self.image.subimage_mut(rect)))
69    }
70
71    fn allocate_glyph_image(&mut self, size: Size<usize>) -> Option<Rect<usize>> {
72        if self.current_point.x + size.width > self.size().width {
73            self.current_point.x = 0;
74            self.current_point.y += self.current_row_height;
75            self.current_row_height = 0;
76        }
77        if self.current_point.y + size.height > self.size().height {
78            self.needs_reset = true;
79            return None;
80        }
81        let origin = self.current_point;
82        self.current_point.x += size.width;
83        self.current_row_height = self.current_row_height.max(size.height);
84        let rect = Rect::new(origin, size);
85        self.dirty_rect = self.dirty_rect.union(rect);
86        Some(rect)
87    }
88
89    pub fn reset_if_needed(&mut self) -> bool {
90        if !self.needs_reset() {
91            return false;
92        }
93        self.needs_reset = false;
94        self.dirty_rect = Rect::ZERO;
95        self.current_point = Point::ZERO;
96        self.current_row_height = 0;
97        self.cached_glyph_image_rects.clear();
98        true
99    }
100}
101
102pub type GrayscaleAtlas = FontAtlas<R>;
103
104impl GrayscaleAtlas {
105    pub fn save_to_png(&self, path: impl AsRef<Path>) {
106        let file = File::create(path).unwrap();
107        let writer = BufWriter::new(file);
108        let size = self.size();
109        let mut encoder = png::Encoder::new(writer, size.width as u32, size.height as u32);
110        encoder.set_color(png::ColorType::Grayscale);
111        encoder.set_depth(png::BitDepth::Eight);
112        let mut writer = encoder.write_header().unwrap();
113        let pixels = self.image.as_pixels();
114        let data = unsafe { slice::from_raw_parts(pixels.as_ptr() as *const u8, pixels.len()) };
115        writer.write_image_data(&data).unwrap();
116    }
117}
118
119pub type ColorAtlas = FontAtlas<Bgra>;
120
121impl ColorAtlas {
122    pub fn save_to_png(&self, path: impl AsRef<Path>) {
123        let file = File::create(path).unwrap();
124        let writer = BufWriter::new(file);
125        let size = self.size();
126        let mut encoder = png::Encoder::new(writer, size.width as u32, size.height as u32);
127        encoder.set_color(png::ColorType::Rgba);
128        encoder.set_depth(png::BitDepth::Eight);
129        let mut writer = encoder.write_header().unwrap();
130        let pixels = self.image.as_pixels();
131        let data = unsafe { slice::from_raw_parts(pixels.as_ptr() as *const u8, pixels.len() * 4) };
132        writer.write_image_data(&data).unwrap();
133    }
134}
135
136#[derive(Clone, Debug, Eq, Hash, PartialEq)]
137pub struct GlyphImageKey {
138    pub font_id: FontId,
139    pub glyph_id: GlyphId,
140    pub size: Size<usize>,
141}
142
143#[derive(Debug)]
144pub enum GlyphImage<'a, T> {
145    Cached(Rect<usize>),
146    Allocated(SubimageMut<'a, T>),
147}