makepad_draw/text/
font_atlas.rs1use {
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}