Skip to content

Commit e182e68

Browse files
dzhou121Zoxc
authored andcommitted
Add support for colored fonts and SVGs
1 parent 74e6ea1 commit e182e68

File tree

7 files changed

+344
-46
lines changed

7 files changed

+344
-46
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ wgpu = "0.18.0"
2020
euclid = "0.22.7"
2121
fontdue = "0.8.0"
2222
rect_packer = "0.2.1"
23+
cosmic-text = { git = "https://github.com/lapce/cosmic-text", rev = "fdc5165f79bb24c76bfeb335dbfa133094636e25" }
24+
# cosmic-text = { path = "../cosmic-text" }
2325

2426
[dev-dependencies]
2527
png = "0.17.6"

src/atlas.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ struct ImageData {
77
data: Vec<u8>,
88
}
99

10+
pub enum AtlasContent {
11+
Mask,
12+
Color,
13+
}
14+
1015
pub struct Atlas {
1116
packer: Packer,
1217
new_data: Vec<ImageData>,
1318
pub atlas_texture: wgpu::Texture,
1419
area_used: i32,
1520
did_clear: bool,
21+
content: AtlasContent,
1622
}
1723

1824
impl Atlas {
@@ -50,15 +56,37 @@ impl Atlas {
5056
}
5157
}
5258

53-
pub fn new(device: &wgpu::Device) -> Self {
54-
let atlas_texture = device.create_texture(&Self::get_texture_desc());
59+
pub fn new(device: &wgpu::Device, content: AtlasContent) -> Self {
60+
let texture_size = wgpu::Extent3d {
61+
width: Atlas::ATLAS_SIZE,
62+
height: Atlas::ATLAS_SIZE,
63+
depth_or_array_layers: 1,
64+
};
65+
let format = match content {
66+
AtlasContent::Mask => wgpu::TextureFormat::R8Unorm,
67+
AtlasContent::Color => wgpu::TextureFormat::Rgba8Unorm,
68+
};
69+
let desc = wgpu::TextureDescriptor {
70+
size: texture_size,
71+
mip_level_count: 1,
72+
sample_count: 1,
73+
dimension: wgpu::TextureDimension::D2,
74+
format,
75+
usage: wgpu::TextureUsages::COPY_SRC
76+
| wgpu::TextureUsages::COPY_DST
77+
| wgpu::TextureUsages::TEXTURE_BINDING,
78+
label: Some("atlas_texture"),
79+
view_formats: &[format],
80+
};
81+
let atlas_texture = device.create_texture(&desc);
5582

5683
Self {
5784
packer: Packer::new(Atlas::get_packer_config()),
5885
new_data: vec![],
5986
atlas_texture,
6087
area_used: 0,
6188
did_clear: false,
89+
content,
6290
}
6391
}
6492

@@ -102,7 +130,7 @@ impl Atlas {
102130
buffer: &buffer,
103131
layout: wgpu::ImageDataLayout {
104132
offset: 0,
105-
bytes_per_row: Some(sz as u32),
133+
bytes_per_row: Some((sz * 4) as u32),
106134
rows_per_image: None,
107135
},
108136
},
@@ -121,14 +149,19 @@ impl Atlas {
121149
for data in &self.new_data {
122150
// Pad data to wgpu::COPY_BYTES_PER_ROW_ALIGNMENT
123151
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as i32;
124-
let padding = (align - data.rect.width % align) % align;
125-
let padded_width = data.rect.width + padding;
152+
let pixels = match self.content {
153+
AtlasContent::Mask => 1,
154+
AtlasContent::Color => 4,
155+
};
156+
let width = data.rect.width * pixels;
157+
let padding = (align - width % align) % align;
158+
let padded_width = width + padding;
126159
let mut padded_data = vec![];
127160
padded_data.reserve((padded_width * data.rect.height) as usize);
128161

129162
let mut i = 0;
130163
for _ in 0..data.rect.height {
131-
for _ in 0..data.rect.width {
164+
for _ in 0..width {
132165
padded_data.push(data.data[i]);
133166
i += 1;
134167
}

src/glyphs.rs

Lines changed: 105 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::atlas::Atlas;
1+
use crate::atlas::{Atlas, AtlasContent};
2+
use cosmic_text::{SubpixelBin, SwashContent, SwashImage};
23
use rect_packer::Rect;
34
use std::collections::HashMap;
45

@@ -8,10 +9,29 @@ pub struct GlyphInfo {
89
pub metrics: fontdue::Metrics,
910
}
1011

12+
#[derive(Copy, Clone, Debug)]
13+
pub struct AtlasInfo {
14+
pub rect: Option<Rect>,
15+
pub left: i32,
16+
pub top: i32,
17+
pub colored: bool,
18+
}
19+
1120
pub struct GlyphCache {
12-
pub atlas: Atlas,
21+
pub mask_atlas: Atlas,
22+
pub color_atlas: Atlas,
1323
pub font: fontdue::Font,
1424
info: HashMap<(char, u32), GlyphInfo>,
25+
atlas_infos: HashMap<
26+
(
27+
cosmic_text::fontdb::ID,
28+
u16,
29+
u32,
30+
(SubpixelBin, SubpixelBin),
31+
),
32+
AtlasInfo,
33+
>,
34+
svg_infos: HashMap<Vec<u8>, HashMap<(u32, u32), AtlasInfo>>,
1535
}
1636

1737
impl GlyphCache {
@@ -22,10 +42,82 @@ impl GlyphCache {
2242
let font = include_bytes!("fonts/Anodina-Regular.ttf") as &[u8];
2343

2444
Self {
25-
atlas: Atlas::new(device),
45+
mask_atlas: Atlas::new(device, AtlasContent::Mask),
46+
color_atlas: Atlas::new(device, AtlasContent::Color),
2647
font: fontdue::Font::from_bytes(font, settings).unwrap(),
2748
info: HashMap::new(),
49+
atlas_infos: HashMap::new(),
50+
svg_infos: HashMap::new(),
51+
}
52+
}
53+
54+
pub fn get_svg_mask(
55+
&mut self,
56+
hash: &[u8],
57+
width: u32,
58+
height: u32,
59+
image: impl FnOnce() -> Vec<u8>,
60+
) -> AtlasInfo {
61+
if !self.svg_infos.contains_key(hash) {
62+
self.svg_infos.insert(hash.to_vec(), HashMap::new());
63+
}
64+
65+
{
66+
let svg_infos = self.svg_infos.get(hash).unwrap();
67+
if let Some(info) = svg_infos.get(&(width, height)) {
68+
return info.clone();
69+
}
2870
}
71+
72+
let data = image();
73+
let rect = self.color_atlas.add_region(&data, width, height);
74+
let info = AtlasInfo {
75+
rect,
76+
left: 0,
77+
top: 0,
78+
colored: true,
79+
};
80+
81+
let svg_infos = self.svg_infos.get_mut(hash).unwrap();
82+
svg_infos.insert((width, height), info.clone());
83+
84+
info
85+
}
86+
87+
pub fn get_glyph_mask<'a>(
88+
&mut self,
89+
font_id: cosmic_text::fontdb::ID,
90+
glyph_id: u16,
91+
size: u32,
92+
subpx: (SubpixelBin, SubpixelBin),
93+
image: impl FnOnce() -> SwashImage,
94+
) -> AtlasInfo {
95+
let key = (font_id, glyph_id, size, subpx);
96+
if let Some(rect) = self.atlas_infos.get(&key) {
97+
return *rect;
98+
}
99+
100+
let image = image();
101+
let rect = match image.content {
102+
SwashContent::Mask => self.mask_atlas.add_region(
103+
&image.data,
104+
image.placement.width,
105+
image.placement.height,
106+
),
107+
SwashContent::SubpixelMask | SwashContent::Color => self.color_atlas.add_region(
108+
&image.data,
109+
image.placement.width,
110+
image.placement.height,
111+
),
112+
};
113+
let info = AtlasInfo {
114+
rect,
115+
left: image.placement.left,
116+
top: image.placement.top,
117+
colored: image.content != SwashContent::Mask,
118+
};
119+
self.atlas_infos.insert(key, info);
120+
info
29121
}
30122

31123
pub fn get_glyph(&mut self, c: char, size: f32) -> GlyphInfo {
@@ -52,7 +144,7 @@ impl GlyphCache {
52144
*/
53145

54146
let rect =
55-
self.atlas
147+
self.mask_atlas
56148
.add_region(&data, metrics.width as u32, metrics.height as u32);
57149

58150
let info = GlyphInfo { rect, metrics };
@@ -64,19 +156,20 @@ impl GlyphCache {
64156
}
65157

66158
pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
67-
self.atlas.update(device, encoder);
159+
self.mask_atlas.update(device, encoder);
160+
self.color_atlas.update(device, encoder);
68161
}
69162

70-
pub fn create_view(&self) -> wgpu::TextureView {
71-
self.atlas.create_view()
72-
}
73-
74-
pub fn usage(&self) -> f32 {
75-
self.atlas.usage()
163+
pub fn check_usage(&mut self) {
164+
if self.mask_atlas.usage() > 0.7 || self.color_atlas.usage() > 0.7 {
165+
self.clear();
166+
}
76167
}
77168

78169
pub fn clear(&mut self) {
79170
self.info.clear();
80-
self.atlas.clear();
171+
self.mask_atlas.clear();
172+
self.color_atlas.clear();
173+
self.atlas_infos.clear();
81174
}
82175
}

0 commit comments

Comments
 (0)