1use crate::scene::*;
2use glyphon::{
3 Attrs, Buffer, Cache, Color as GlyphonColor, Family, FontSystem, Metrics, Resolution, Shaping,
4 SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, Weight,
5};
6
7pub struct TextEngine {
8 pub font_system: FontSystem,
9 pub cache: SwashCache,
10 pub atlas: TextAtlas,
11 pub text_renderer: TextRenderer,
12 pub buffers: Vec<(NodeId, Buffer)>,
13 pub viewport: Viewport,
14}
15
16impl TextEngine {
17 pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat) -> Self {
18 let font_system = FontSystem::new();
19 let cache = SwashCache::new();
20 let glyph_cache = Cache::new(device);
21 let mut atlas = TextAtlas::new(device, queue, &glyph_cache, format);
22 let text_renderer =
23 TextRenderer::new(&mut atlas, device, wgpu::MultisampleState::default(), None);
24 let viewport = Viewport::new(device, &glyph_cache);
25
26 Self {
27 font_system,
28 cache,
29 atlas,
30 text_renderer,
31 buffers: Vec::new(),
32 viewport,
33 }
34 }
35
36 pub fn prepare(
37 &mut self,
38 scene: &SceneGraph,
39 device: &wgpu::Device,
40 queue: &wgpu::Queue,
41 viewport_width: u32,
42 viewport_height: u32,
43 ) {
44 self.buffers.clear();
45
46 if let Some(root) = scene.root {
47 self.collect_text_nodes(scene, root);
48 }
49
50 self.viewport.update(
51 queue,
52 Resolution {
53 width: viewport_width,
54 height: viewport_height,
55 },
56 );
57
58 let text_areas: Vec<TextArea> = self
59 .buffers
60 .iter()
61 .map(|(node_id, buffer)| {
62 let node = scene.get(*node_id);
63 let color = &node.style.color;
64 let r = (color.r * 255.0) as u8;
65 let g = (color.g * 255.0) as u8;
66 let b = (color.b * 255.0) as u8;
67 let a = (color.a * 255.0) as u8;
68
69 TextArea {
70 buffer,
71 left: node.layout.x,
72 top: node.layout.y,
73 scale: 1.0,
74 bounds: TextBounds {
75 left: 0,
76 top: 0,
77 right: viewport_width as i32,
78 bottom: viewport_height as i32,
79 },
80 default_color: GlyphonColor::rgba(r, g, b, a),
81 custom_glyphs: &[],
82 }
83 })
84 .collect();
85
86 self.text_renderer
87 .prepare(
88 device,
89 queue,
90 &mut self.font_system,
91 &mut self.atlas,
92 &self.viewport,
93 text_areas,
94 &mut self.cache,
95 )
96 .ok();
97 }
98
99 pub fn render<'a>(&'a self, pass: &mut wgpu::RenderPass<'a>) {
100 self.text_renderer
101 .render(&self.atlas, &self.viewport, pass)
102 .ok();
103 }
104
105 fn collect_text_nodes(&mut self, scene: &SceneGraph, node_id: NodeId) {
106 let node = scene.get(node_id);
107
108 if node.style.display == Display::None {
109 return;
110 }
111
112 if node.kind == ElementKind::Text {
113 if let Some(text) = &node.text_content {
114 let parent_style = node
115 .parent
116 .map(|p| &scene.get(p).style)
117 .unwrap_or(&node.style);
118
119 let font_size = parent_style.font_size;
120 let line_height = font_size * parent_style.line_height;
121
122 let mut buffer =
123 Buffer::new(&mut self.font_system, Metrics::new(font_size, line_height));
124
125 let weight = if parent_style.font_weight >= 700 {
126 Weight::BOLD
127 } else if parent_style.font_weight >= 500 {
128 Weight::MEDIUM
129 } else {
130 Weight::NORMAL
131 };
132
133 let family = if parent_style.font_family.is_empty() {
134 Family::SansSerif
135 } else {
136 Family::Name(&parent_style.font_family)
137 };
138
139 let attrs = Attrs::new().family(family).weight(weight);
140
141 buffer.set_size(&mut self.font_system, None, None);
144 buffer.set_text(&mut self.font_system, text, &attrs, Shaping::Advanced, None);
145 buffer.shape_until_scroll(&mut self.font_system, false);
146
147 self.buffers.push((node_id, buffer));
148 }
149 }
150
151 let children: Vec<NodeId> = node.children.clone();
152 for child_id in children {
153 self.collect_text_nodes(scene, child_id);
154 }
155 }
156}