floem_vger/
glyphs.rs

1use crate::atlas::{Atlas, AtlasContent};
2use cosmic_text::{SubpixelBin, SwashContent, SwashImage};
3use rect_packer::Rect;
4use std::collections::HashMap;
5
6#[derive(Copy, Clone, Debug)]
7pub struct AtlasInfo {
8    pub rect: Option<Rect>,
9    pub left: i32,
10    pub top: i32,
11    pub colored: bool,
12}
13
14pub enum PixelFormat {
15    //TODO: add Rgb(currently we assume Rgba everywhere)
16    Rgba,
17}
18
19pub struct Image {
20    pub width: u32,
21    pub height: u32,
22    pub data: Vec<u8>,
23    pub pixel_format: PixelFormat,
24}
25
26pub struct GlyphCache {
27    pub size: u32,
28    pub mask_atlas: Atlas,
29    pub color_atlas: Atlas,
30    glyph_infos: HashMap<
31        (
32            cosmic_text::fontdb::ID,
33            u16,
34            u32,
35            (SubpixelBin, SubpixelBin),
36        ),
37        AtlasInfo,
38    >,
39    svg_infos: HashMap<Vec<u8>, HashMap<(u32, u32), AtlasInfo>>,
40    img_infos: HashMap<Vec<u8>, AtlasInfo>,
41}
42
43impl GlyphCache {
44    pub fn new(device: &wgpu::Device) -> Self {
45        let size = 1024;
46        Self {
47            size,
48            mask_atlas: Atlas::new(device, AtlasContent::Mask, size, size),
49            color_atlas: Atlas::new(device, AtlasContent::Color, size, size),
50            glyph_infos: HashMap::new(),
51            img_infos: HashMap::new(),
52            svg_infos: HashMap::new(),
53        }
54    }
55
56    pub fn get_image_mask(&mut self, hash: &[u8], image_fn: impl FnOnce() -> Image) -> AtlasInfo {
57        if let Some(info) = self.img_infos.get(hash) {
58            return *info;
59        }
60
61        let image = image_fn();
62        let rect = self
63            .color_atlas
64            .add_region(&image.data, image.width, image.height);
65        let info = AtlasInfo {
66            rect,
67            left: 0,
68            top: 0,
69            colored: true,
70        };
71        self.img_infos.insert(hash.to_vec(), info);
72
73        info
74    }
75
76    pub fn get_svg_mask(
77        &mut self,
78        hash: &[u8],
79        width: u32,
80        height: u32,
81        image: impl FnOnce() -> Vec<u8>,
82    ) -> AtlasInfo {
83        if !self.svg_infos.contains_key(hash) {
84            self.svg_infos.insert(hash.to_vec(), HashMap::new());
85        }
86
87        {
88            let svg_infos = self.svg_infos.get(hash).unwrap();
89            if let Some(info) = svg_infos.get(&(width, height)) {
90                return *info;
91            }
92        }
93
94        let data = image();
95        let rect = self.color_atlas.add_region(&data, width, height);
96        let info = AtlasInfo {
97            rect,
98            left: 0,
99            top: 0,
100            colored: true,
101        };
102
103        let svg_infos = self.svg_infos.get_mut(hash).unwrap();
104        svg_infos.insert((width, height), info);
105
106        info
107    }
108
109    pub fn get_glyph_mask(
110        &mut self,
111        font_id: cosmic_text::fontdb::ID,
112        glyph_id: u16,
113        size: u32,
114        subpx: (SubpixelBin, SubpixelBin),
115        image: impl FnOnce() -> SwashImage,
116    ) -> AtlasInfo {
117        let key = (font_id, glyph_id, size, subpx);
118        if let Some(rect) = self.glyph_infos.get(&key) {
119            return *rect;
120        }
121
122        let image = image();
123        let rect = match image.content {
124            SwashContent::Mask => self.mask_atlas.add_region(
125                &image.data,
126                image.placement.width,
127                image.placement.height,
128            ),
129            SwashContent::SubpixelMask | SwashContent::Color => self.color_atlas.add_region(
130                &image.data,
131                image.placement.width,
132                image.placement.height,
133            ),
134        };
135        let info = AtlasInfo {
136            rect,
137            left: image.placement.left,
138            top: image.placement.top,
139            colored: image.content != SwashContent::Mask,
140        };
141        self.glyph_infos.insert(key, info);
142        info
143    }
144
145    pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
146        self.mask_atlas.update(device, encoder);
147        self.color_atlas.update(device, encoder);
148    }
149
150    pub fn check_usage(&mut self, device: &wgpu::Device) -> bool {
151        let max_seen = (self.mask_atlas.max_seen as f32 * 2.0)
152            .max(self.color_atlas.max_seen as f32 * 2.0) as u32;
153        if max_seen > self.size {
154            self.size = max_seen;
155            self.mask_atlas.resize(device, self.size, self.size);
156            self.color_atlas.resize(device, self.size, self.size);
157            self.clear();
158            true
159        } else if self.mask_atlas.usage() > 0.7 || self.color_atlas.usage() > 0.7 {
160            self.clear();
161            false
162        } else {
163            false
164        }
165    }
166
167    pub fn clear(&mut self) {
168        self.mask_atlas.clear();
169        self.color_atlas.clear();
170        self.glyph_infos.clear();
171        self.svg_infos.clear();
172        self.img_infos.clear();
173    }
174}