1#![deny(unused_results)]
6mod builder;
7mod pipeline;
8mod region;
9
10pub use region::Region;
11
12use pipeline::{Instance, Pipeline};
13
14pub use builder::GlyphBrushBuilder;
15pub use glyph_brush::ab_glyph;
16pub use glyph_brush::{
17 BuiltInLineBreaker, Extra, FontId, GlyphCruncher, GlyphPositioner,
18 HorizontalAlign, Layout, LineBreak, LineBreaker, OwnedSection, OwnedText,
19 Section, SectionGeometry, SectionGlyph, SectionGlyphIter, SectionText,
20 Text, VerticalAlign,
21};
22
23use ab_glyph::{Font, Rect};
24use core::hash::BuildHasher;
25use std::borrow::Cow;
26
27use glyph_brush::{BrushAction, BrushError, DefaultSectionHasher};
28use log::{log_enabled, warn};
29
30pub struct GlyphBrush<Depth, F = ab_glyph::FontArc, H = DefaultSectionHasher> {
35 pipeline: Pipeline<Depth>,
36 glyph_brush: glyph_brush::GlyphBrush<Instance, Extra, F, H>,
37}
38
39impl<Depth, F: Font, H: BuildHasher> GlyphBrush<Depth, F, H> {
40 #[inline]
46 pub fn queue<'a, S>(&mut self, section: S)
47 where
48 S: Into<Cow<'a, Section<'a>>>,
49 {
50 self.glyph_brush.queue(section)
51 }
52
53 #[inline]
63 pub fn queue_custom_layout<'a, S, G>(
64 &mut self,
65 section: S,
66 custom_layout: &G,
67 ) where
68 G: GlyphPositioner,
69 S: Into<Cow<'a, Section<'a>>>,
70 {
71 self.glyph_brush.queue_custom_layout(section, custom_layout)
72 }
73
74 #[inline]
78 pub fn queue_pre_positioned(
79 &mut self,
80 glyphs: Vec<SectionGlyph>,
81 extra: Vec<Extra>,
82 bounds: Rect,
83 ) {
84 self.glyph_brush.queue_pre_positioned(glyphs, extra, bounds)
85 }
86
87 #[inline]
93 pub fn keep_cached_custom_layout<'a, S, G>(
94 &mut self,
95 section: S,
96 custom_layout: &G,
97 ) where
98 S: Into<Cow<'a, Section<'a>>>,
99 G: GlyphPositioner,
100 {
101 self.glyph_brush
102 .keep_cached_custom_layout(section, custom_layout)
103 }
104
105 #[inline]
111 pub fn keep_cached<'a, S>(&mut self, section: S)
112 where
113 S: Into<Cow<'a, Section<'a>>>,
114 {
115 self.glyph_brush.keep_cached(section)
116 }
117
118 #[inline]
122 pub fn fonts(&self) -> &[F] {
123 self.glyph_brush.fonts()
124 }
125
126 pub fn add_font(&mut self, font: F) -> FontId {
130 self.glyph_brush.add_font(font)
131 }
132}
133
134impl<D, F, H> GlyphBrush<D, F, H>
135where
136 F: Font + Sync,
137 H: BuildHasher,
138{
139 fn process_queued(
140 &mut self,
141 device: &wgpu::Device,
142 staging_belt: &mut wgpu::util::StagingBelt,
143 encoder: &mut wgpu::CommandEncoder,
144 ) {
145 let pipeline = &mut self.pipeline;
146
147 let mut brush_action;
148
149 loop {
150 brush_action = self.glyph_brush.process_queued(
151 |rect, tex_data| {
152 let offset = [rect.min[0] as u16, rect.min[1] as u16];
153 let size = [rect.width() as u16, rect.height() as u16];
154
155 pipeline.update_cache(
156 device,
157 staging_belt,
158 encoder,
159 offset,
160 size,
161 tex_data,
162 );
163 },
164 Instance::from_vertex,
165 );
166
167 match brush_action {
168 Ok(_) => break,
169 Err(BrushError::TextureTooSmall { suggested }) => {
170 let max_image_dimension = 2048;
173
174 let (new_width, new_height) = if (suggested.0
175 > max_image_dimension
176 || suggested.1 > max_image_dimension)
177 && (self.glyph_brush.texture_dimensions().0
178 < max_image_dimension
179 || self.glyph_brush.texture_dimensions().1
180 < max_image_dimension)
181 {
182 (max_image_dimension, max_image_dimension)
183 } else {
184 suggested
185 };
186
187 if log_enabled!(log::Level::Warn) {
188 warn!(
189 "Increasing glyph texture size {old:?} -> {new:?}. \
190 Consider building with `.initial_cache_size({new:?})` to avoid \
191 resizing",
192 old = self.glyph_brush.texture_dimensions(),
193 new = (new_width, new_height),
194 );
195 }
196
197 pipeline.increase_cache_size(device, new_width, new_height);
198 self.glyph_brush.resize_texture(new_width, new_height);
199 }
200 }
201 }
202
203 match brush_action.unwrap() {
204 BrushAction::Draw(verts) => {
205 self.pipeline.upload(device, staging_belt, encoder, &verts);
206 }
207 BrushAction::ReDraw => {}
208 };
209 }
210}
211
212impl<F: Font + Sync, H: BuildHasher> GlyphBrush<(), F, H> {
213 fn new(
214 device: &wgpu::Device,
215 filter_mode: wgpu::FilterMode,
216 multisample: wgpu::MultisampleState,
217 render_format: wgpu::TextureFormat,
218 raw_builder: glyph_brush::GlyphBrushBuilder<F, H>,
219 ) -> Self {
220 let glyph_brush = raw_builder.build();
221 let (cache_width, cache_height) = glyph_brush.texture_dimensions();
222 GlyphBrush {
223 pipeline: Pipeline::<()>::new(
224 device,
225 filter_mode,
226 multisample,
227 render_format,
228 cache_width,
229 cache_height,
230 ),
231 glyph_brush,
232 }
233 }
234
235 #[inline]
246 pub fn draw_queued(
247 &mut self,
248 device: &wgpu::Device,
249 staging_belt: &mut wgpu::util::StagingBelt,
250 encoder: &mut wgpu::CommandEncoder,
251 target: &wgpu::TextureView,
252 target_width: u32,
253 target_height: u32,
254 ) -> Result<(), String> {
255 self.draw_queued_with_transform(
256 device,
257 staging_belt,
258 encoder,
259 target,
260 orthographic_projection(target_width, target_height),
261 )
262 }
263
264 #[inline]
276 pub fn draw_queued_with_transform(
277 &mut self,
278 device: &wgpu::Device,
279 staging_belt: &mut wgpu::util::StagingBelt,
280 encoder: &mut wgpu::CommandEncoder,
281 target: &wgpu::TextureView,
282 transform: [f32; 16],
283 ) -> Result<(), String> {
284 self.process_queued(device, staging_belt, encoder);
285 self.pipeline.draw(
286 device,
287 staging_belt,
288 encoder,
289 target,
290 transform,
291 None,
292 );
293
294 Ok(())
295 }
296
297 #[inline]
309 pub fn draw_queued_with_transform_and_scissoring(
310 &mut self,
311 device: &wgpu::Device,
312 staging_belt: &mut wgpu::util::StagingBelt,
313 encoder: &mut wgpu::CommandEncoder,
314 target: &wgpu::TextureView,
315 transform: [f32; 16],
316 region: Region,
317 ) -> Result<(), String> {
318 self.process_queued(device, staging_belt, encoder);
319 self.pipeline.draw(
320 device,
321 staging_belt,
322 encoder,
323 target,
324 transform,
325 Some(region),
326 );
327
328 Ok(())
329 }
330}
331
332impl<F: Font + Sync, H: BuildHasher> GlyphBrush<wgpu::DepthStencilState, F, H> {
333 fn new(
334 device: &wgpu::Device,
335 filter_mode: wgpu::FilterMode,
336 multisample: wgpu::MultisampleState,
337 render_format: wgpu::TextureFormat,
338 depth_stencil_state: wgpu::DepthStencilState,
339 raw_builder: glyph_brush::GlyphBrushBuilder<F, H>,
340 ) -> Self {
341 let glyph_brush = raw_builder.build();
342 let (cache_width, cache_height) = glyph_brush.texture_dimensions();
343 GlyphBrush {
344 pipeline: Pipeline::<wgpu::DepthStencilState>::new(
345 device,
346 filter_mode,
347 multisample,
348 render_format,
349 depth_stencil_state,
350 cache_width,
351 cache_height,
352 ),
353 glyph_brush,
354 }
355 }
356
357 #[inline]
368 pub fn draw_queued(
369 &mut self,
370 device: &wgpu::Device,
371 staging_belt: &mut wgpu::util::StagingBelt,
372 encoder: &mut wgpu::CommandEncoder,
373 target: &wgpu::TextureView,
374 depth_stencil_attachment: wgpu::RenderPassDepthStencilAttachment,
375 target_width: u32,
376 target_height: u32,
377 ) -> Result<(), String> {
378 self.draw_queued_with_transform(
379 device,
380 staging_belt,
381 encoder,
382 target,
383 depth_stencil_attachment,
384 orthographic_projection(target_width, target_height),
385 )
386 }
387
388 #[inline]
400 pub fn draw_queued_with_transform(
401 &mut self,
402 device: &wgpu::Device,
403 staging_belt: &mut wgpu::util::StagingBelt,
404 encoder: &mut wgpu::CommandEncoder,
405 target: &wgpu::TextureView,
406 depth_stencil_attachment: wgpu::RenderPassDepthStencilAttachment,
407 transform: [f32; 16],
408 ) -> Result<(), String> {
409 self.process_queued(device, staging_belt, encoder);
410 self.pipeline.draw(
411 device,
412 staging_belt,
413 encoder,
414 target,
415 depth_stencil_attachment,
416 transform,
417 None,
418 );
419
420 Ok(())
421 }
422
423 #[inline]
435 pub fn draw_queued_with_transform_and_scissoring(
436 &mut self,
437 device: &wgpu::Device,
438 staging_belt: &mut wgpu::util::StagingBelt,
439 encoder: &mut wgpu::CommandEncoder,
440 target: &wgpu::TextureView,
441 depth_stencil_attachment: wgpu::RenderPassDepthStencilAttachment,
442 transform: [f32; 16],
443 region: Region,
444 ) -> Result<(), String> {
445 self.process_queued(device, staging_belt, encoder);
446
447 self.pipeline.draw(
448 device,
449 staging_belt,
450 encoder,
451 target,
452 depth_stencil_attachment,
453 transform,
454 Some(region),
455 );
456
457 Ok(())
458 }
459}
460
461#[rustfmt::skip]
463pub fn orthographic_projection(width: u32, height: u32) -> [f32; 16] {
464 [
465 2.0 / width as f32, 0.0, 0.0, 0.0,
466 0.0, -2.0 / height as f32, 0.0, 0.0,
467 0.0, 0.0, 1.0, 0.0,
468 -1.0, 1.0, 0.0, 1.0,
469 ]
470}
471
472impl<D, F: Font, H: BuildHasher> GlyphCruncher<F> for GlyphBrush<D, F, H> {
473 #[inline]
474 fn glyphs_custom_layout<'a, 'b, S, L>(
475 &'b mut self,
476 section: S,
477 custom_layout: &L,
478 ) -> SectionGlyphIter<'b>
479 where
480 L: GlyphPositioner + std::hash::Hash,
481 S: Into<Cow<'a, Section<'a>>>,
482 {
483 self.glyph_brush
484 .glyphs_custom_layout(section, custom_layout)
485 }
486
487 #[inline]
488 fn fonts(&self) -> &[F] {
489 self.glyph_brush.fonts()
490 }
491
492 #[inline]
493 fn glyph_bounds_custom_layout<'a, S, L>(
494 &mut self,
495 section: S,
496 custom_layout: &L,
497 ) -> Option<Rect>
498 where
499 L: GlyphPositioner + std::hash::Hash,
500 S: Into<Cow<'a, Section<'a>>>,
501 {
502 self.glyph_brush
503 .glyph_bounds_custom_layout(section, custom_layout)
504 }
505}
506
507impl<F, H> std::fmt::Debug for GlyphBrush<F, H> {
508 #[inline]
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 write!(f, "GlyphBrush")
511 }
512}