1use std::sync::Arc;
2use crate::prelude::*;
3use crate::traits::font_provider::FontProvider;
4use crate::traits::rasterizer::Rasterizer;
5use crate::primitives::atlas::GlyphAtlas;
6use crate::primitives::pipeline::ZentypePipeline;
7use crate::types::VerticalAlignment;
8use zenthra_core::Color;
9use wgpu::util::DeviceExt;
10
11pub struct TextRenderer {
14 font_provider: Box<dyn FontProvider>,
15 rasterizer: Box<dyn Rasterizer>,
16 atlas: GlyphAtlas,
17 pipeline: ZentypePipeline,
18
19 instance_buffer: Option<wgpu::Buffer>,
21 instances: Vec<GlyphInstance>,
22 device: Arc<wgpu::Device>,
23
24 screen_size: [f32; 2],
26}
27
28impl TextRenderer {
29 pub fn new(
31 device: Arc<wgpu::Device>,
32 queue: &wgpu::Queue,
33 config: &wgpu::SurfaceConfiguration,
34 font_provider: Box<dyn FontProvider>,
35 rasterizer: Box<dyn Rasterizer>,
36 atlas: GlyphAtlas,
37 ) -> Self {
38 let pipeline = ZentypePipeline::new(&device, &atlas, config);
39
40 let screen_size = [config.width as f32, config.height as f32];
41 pipeline.update_screen_size(queue, screen_size[0], screen_size[1]);
42
43 Self {
44 font_provider,
45 rasterizer,
46 atlas,
47 pipeline,
48 instance_buffer: None,
49 instances: Vec::new(),
50 device,
51 screen_size,
52 }
53 }
54
55 pub fn set_font_provider(&mut self, provider: Box<dyn FontProvider>) {
57 self.font_provider = provider;
58 }
59
60 pub fn set_rasterizer(&mut self, rasterizer: Box<dyn Rasterizer>) {
62 self.rasterizer = rasterizer;
63 }
64
65 pub fn draw(&mut self, queue: &wgpu::Queue, text: &str, pos: [f32; 2], options: &TextOptions) -> ShapedBuffer {
69 let layout_width = options.max_width.unwrap_or(20000.0);
72 let layout_height = options.max_height.unwrap_or(20000.0);
73
74 self.font_provider.set_layout_size(layout_width, layout_height);
75
76 let mut final_options = options.clone();
78 final_options.max_width = Some(layout_width);
80
81 let buffer = self.font_provider.shape(text, &final_options);
83
84 for glyph in buffer.glyphs() {
86 if self.atlas.get(&glyph.key).is_none() {
87 if let Some(rasterized) = self.rasterizer.rasterize(glyph) {
89 self.atlas.insert(queue, glyph.key, &rasterized);
90 }
91 }
92 }
93
94 let y_offset = self.calculate_valign_offset(&buffer, pos, &final_options);
96
97 let mut render_pos = pos;
99 render_pos[1] += y_offset;
100
101 let mut new_instances = self.pipeline.generate_instances(&buffer, &self.atlas, render_pos, &final_options);
102 self.instances.append(&mut new_instances);
103
104 buffer
105 }
106
107 pub fn draw_rect(&mut self, pos: [f32; 2], size: [f32; 2], color: Color, clip_rect: [f32; 4]) {
110 self.instances.push(GlyphInstance {
111 pos,
112 size,
113 uv_pos: [0.0, 0.0],
114 uv_size: [0.0, 0.0],
115 color: [0.0, 0.0, 0.0, 0.0],
116 bg_color: color.to_array(),
117 clip_rect,
118 });
119 }
120
121 fn calculate_valign_offset(&self, buffer: &ShapedBuffer, pos: [f32; 2], options: &TextOptions) -> f32 {
122 if let Some(valign) = options.valign {
123 let (_, content_height) = buffer.content_size();
124 let max_h = options.max_height.unwrap_or(self.screen_size[1] - pos[1]);
125 let available_height = max_h;
126
127 match valign {
128 VerticalAlignment::Top => 0.0,
129 VerticalAlignment::Center => (available_height - content_height) / 2.0,
130 VerticalAlignment::Bottom => available_height - content_height,
131 }
132 } else {
133 0.0
134 }
135 }
136
137 fn calculate_visual_shift(&self, buffer: &ShapedBuffer, options: &TextOptions) -> f32 {
138 let font_size = options.font_size;
139 let lh = options.line_height;
140 let visual_ascent = font_size * (0.8 + (lh - 1.0) / 2.0);
141 let first_line_y = buffer.lines().first().map(|l| l.y).unwrap_or(visual_ascent);
142 visual_ascent - first_line_y
143 }
144
145 pub fn hit_test(&self, buffer: &ShapedBuffer, pos: [f32; 2], options: &TextOptions, mouse_pos: [f32; 2]) -> usize {
149 let y_offset = self.calculate_valign_offset(buffer, pos, options);
150 let v_shift = self.calculate_visual_shift(buffer, options);
151
152 let x = mouse_pos[0] - pos[0];
153 let y = mouse_pos[1] - pos[1] - y_offset - v_shift;
154
155 buffer.index_at(x, y)
156 }
157
158 pub fn position_at(&self, buffer: &ShapedBuffer, pos: [f32; 2], options: &TextOptions, index: usize) -> Option<[f32; 2]> {
160 let y_offset = self.calculate_valign_offset(buffer, pos, options);
161 let v_shift = self.calculate_visual_shift(buffer, options);
162
163 buffer.position_at(index).map(|(lx, ly)| {
164 [
165 lx + pos[0],
166 ly + pos[1] + y_offset + v_shift,
167 ]
168 })
169 }
170
171 pub fn render<'a>(&'a mut self, rpass: &mut wgpu::RenderPass<'a>) {
173 if self.instances.is_empty() {
174 return;
175 }
176
177 let required_size = (self.instances.len() * std::mem::size_of::<GlyphInstance>()) as wgpu::BufferAddress;
179
180 let recreate = match &self.instance_buffer {
182 Some(buf) => buf.size() < required_size,
183 None => true,
184 };
185
186 if recreate {
187 self.instance_buffer = Some(self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
188 label: Some("Zentype Instance Buffer"),
189 contents: bytemuck::cast_slice(&self.instances),
190 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
191 }));
192 } else if let Some(_buf) = &self.instance_buffer {
193 self.instance_buffer = Some(self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
198 label: Some("Zentype Instance Buffer"),
199 contents: bytemuck::cast_slice(&self.instances),
200 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
201 }));
202 }
203
204 if let Some(buf) = &self.instance_buffer {
206 rpass.set_pipeline(self.pipeline.pipeline());
207 rpass.set_bind_group(0, self.pipeline.atlas_bind_group(), &[]);
208 rpass.set_bind_group(1, self.pipeline.uniform_bind_group(), &[]);
209 rpass.set_vertex_buffer(0, buf.slice(..));
210 rpass.draw(0..4, 0..self.instances.len() as u32);
211 }
212
213 self.instances.clear();
215 }
216
217 pub fn resize(&mut self, queue: &wgpu::Queue, width: u32, height: u32) {
219 self.screen_size = [width as f32, height as f32];
220 self.pipeline.update_screen_size(queue, self.screen_size[0], self.screen_size[1]);
221 }
222
223 pub fn font_system(&self) -> std::sync::Arc<std::sync::Mutex<cosmic_text::FontSystem>> {
225 self.font_provider.font_system()
226 }
227}