glium_glyph/lib.rs
1#[macro_use]
2extern crate glium;
3#[macro_use]
4pub extern crate glyph_brush;
5
6mod builder;
7
8pub use builder::GlyphBrushBuilder;
9
10use std::borrow::Cow;
11use std::hash::{BuildHasher, Hash};
12use std::ops::Deref;
13
14use glium::backend::{Context, Facade};
15use glium::index::PrimitiveType;
16use glium::texture::texture2d::Texture2d;
17use glium::texture::{ClientFormat, RawImage2d};
18use glium::{Program, Surface};
19
20use glyph_brush::ab_glyph::{point, Font};
21use glyph_brush::{
22 BrushAction, BrushError, DefaultSectionHasher, FontId, GlyphCruncher, GlyphPositioner, Section,
23 SectionGlyphIter,
24};
25use glyph_brush::{Extra, Rectangle};
26
27#[derive(Copy, Clone, Debug)]
28struct GlyphVertex {
29 /// screen position
30 left_top: [f32; 3],
31 right_bottom: [f32; 2],
32 /// texture position
33 tex_left_top: [f32; 2],
34 tex_right_bottom: [f32; 2],
35 /// text color
36 color: [f32; 4],
37}
38
39implement_vertex!(
40 GlyphVertex,
41 left_top,
42 right_bottom,
43 tex_left_top,
44 tex_right_bottom,
45 color
46);
47
48#[derive(Copy, Clone, Debug)]
49struct InstanceVertex {
50 v: f32,
51}
52
53implement_vertex!(InstanceVertex, v);
54
55fn rect_to_rect(rect: Rectangle<u32>) -> glium::Rect {
56 glium::Rect {
57 left: rect.min[0],
58 bottom: rect.min[1],
59 width: rect.width(),
60 height: rect.height(),
61 }
62}
63
64fn update_texture(tex: &Texture2d, rect: Rectangle<u32>, tex_data: &[u8]) {
65 let image = RawImage2d {
66 data: std::borrow::Cow::Borrowed(tex_data),
67 format: ClientFormat::U8,
68 height: rect.height(),
69 width: rect.width(),
70 };
71 tex.write(rect_to_rect(rect), image);
72}
73
74#[inline]
75fn to_vertex(
76 glyph_brush::GlyphVertex {
77 mut tex_coords,
78 pixel_coords,
79 bounds,
80 extra,
81 }: glyph_brush::GlyphVertex,
82) -> GlyphVertex {
83 let gl_bounds = bounds;
84
85 let mut gl_rect = glyph_brush::ab_glyph::Rect {
86 min: point(pixel_coords.min.x, pixel_coords.min.y),
87 max: point(pixel_coords.max.x, pixel_coords.max.y),
88 };
89
90 // handle overlapping bounds, modify uv_rect to preserve texture aspect
91 if gl_rect.max.x > gl_bounds.max.x {
92 let old_width = gl_rect.width();
93 gl_rect.max.x = gl_bounds.max.x;
94 tex_coords.max.x = tex_coords.min.x + tex_coords.width() * gl_rect.width() / old_width;
95 }
96 if gl_rect.min.x < gl_bounds.min.x {
97 let old_width = gl_rect.width();
98 gl_rect.min.x = gl_bounds.min.x;
99 tex_coords.min.x = tex_coords.max.x - tex_coords.width() * gl_rect.width() / old_width;
100 }
101 if gl_rect.max.y > gl_bounds.max.y {
102 let old_height = gl_rect.height();
103 gl_rect.max.y = gl_bounds.max.y;
104 tex_coords.max.y = tex_coords.min.y + tex_coords.height() * gl_rect.height() / old_height;
105 }
106 if gl_rect.min.y < gl_bounds.min.y {
107 let old_height = gl_rect.height();
108 gl_rect.min.y = gl_bounds.min.y;
109 tex_coords.min.y = tex_coords.max.y - tex_coords.height() * gl_rect.height() / old_height;
110 }
111
112 GlyphVertex {
113 left_top: [gl_rect.min.x, gl_rect.max.y, extra.z],
114 right_bottom: [gl_rect.max.x, gl_rect.min.y],
115 tex_left_top: [tex_coords.min.x, tex_coords.max.y],
116 tex_right_bottom: [tex_coords.max.x, tex_coords.min.y],
117 color: extra.color,
118 }
119}
120
121/*
122/// Object allowing glyph drawing, containing cache state. Manages glyph positioning cacheing,
123/// glyph draw caching & efficient GPU texture cache updating and re-sizing on demand.
124///
125/// Build using a [`GlyphBrushBuilder`](struct.GlyphBrushBuilder.html).
126///
127/// # Example
128///
129/// ```no_run
130/// # extern crate gfx;
131/// # extern crate gfx_window_glutin;
132/// # extern crate glutin;
133/// extern crate gfx_glyph;
134/// # use gfx_glyph::{GlyphBrushBuilder};
135/// use gfx_glyph::Section;
136/// # fn main() -> Result<(), String> {
137/// # let events_loop = glutin::EventsLoop::new();
138/// # let (_window, _device, mut gfx_factory, gfx_color, gfx_depth) =
139/// # gfx_window_glutin::init::<gfx::format::Srgba8, gfx::format::Depth>(
140/// # glutin::WindowBuilder::new(),
141/// # glutin::ContextBuilder::new(),
142/// # &events_loop);
143/// # let mut gfx_encoder: gfx::Encoder<_, _> = gfx_factory.create_command_buffer().into();
144/// # let dejavu: &[u8] = include_bytes!("../../fonts/DejaVuSans.ttf");
145/// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu)
146/// # .build(gfx_factory.clone());
147/// # let some_other_section = Section { text: "another", ..Section::default() };
148///
149/// let section = Section {
150/// text: "Hello gfx_glyph",
151/// ..Section::default()
152/// };
153///
154/// glyph_brush.queue(section);
155/// glyph_brush.queue(some_other_section);
156///
157/// glyph_brush.draw_queued(&mut gfx_encoder, &gfx_color, &gfx_depth)?;
158/// # Ok(())
159/// # }
160/// ```
161///
162/// # Caching behaviour
163///
164/// Calls to [`GlyphBrush::queue`](#method.queue),
165/// [`GlyphBrush::pixel_bounds`](#method.pixel_bounds), [`GlyphBrush::glyphs`](#method.glyphs)
166/// calculate the positioned glyphs for a section.
167/// This is cached so future calls to any of the methods for the same section are much
168/// cheaper. In the case of [`GlyphBrush::queue`](#method.queue) the calculations will also be
169/// used for actual drawing.
170///
171/// The cache for a section will be **cleared** after a
172/// [`GlyphBrush::draw_queued`](#method.draw_queued) call when that section has not been used since
173/// the previous draw call.
174*/
175
176pub struct GlyphBrush<'a, F: Font, H: BuildHasher = DefaultSectionHasher> {
177 glyph_brush: glyph_brush::GlyphBrush<GlyphVertex, Extra, F, H>,
178 params: glium::DrawParameters<'a>,
179 program: Program,
180 texture: Texture2d,
181 index_buffer: glium::index::NoIndices,
182 vertex_buffer: glium::VertexBuffer<GlyphVertex>,
183 instances: glium::VertexBuffer<InstanceVertex>,
184}
185
186impl<'p, F: Font> GlyphBrush<'p, F> {
187 pub fn new<C: Facade, V: Into<Vec<F>>>(facade: &C, fonts: V) -> Self {
188 GlyphBrushBuilder::using_fonts(fonts).build(facade)
189 }
190}
191
192impl<'p, F: Font + Sync, H: BuildHasher> GlyphBrush<'p, F, H> {
193 /// Queues a section/layout to be drawn by the next call of
194 /// [`draw_queued`](struct.GlyphBrush.html#method.draw_queued). Can be called multiple times
195 /// to queue multiple sections for drawing.
196 ///
197 /// Used to provide custom `GlyphPositioner` logic, if using built-in
198 /// [`Layout`](enum.Layout.html) simply use [`queue`](struct.GlyphBrush.html#method.queue)
199 ///
200 /// Benefits from caching, see [caching behaviour](#caching-behaviour).
201 #[inline]
202 pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
203 where
204 G: GlyphPositioner,
205 S: Into<Cow<'a, Section<'a>>>,
206 {
207 self.glyph_brush.queue_custom_layout(section, custom_layout)
208 }
209
210 /// Queues a section/layout to be drawn by the next call of
211 /// [`draw_queued`](struct.GlyphBrush.html#method.draw_queued). Can be called multiple times
212 /// to queue multiple sections for drawing.
213 ///
214 /// Benefits from caching, see [caching behaviour](#caching-behaviour).
215 #[inline]
216 pub fn queue<'a, S>(&mut self, section: S)
217 where
218 S: Into<Cow<'a, Section<'a>>>,
219 {
220 self.glyph_brush.queue(section)
221 }
222
223 /*
224 /// Draws all queued sections onto a render target.
225 /// See [`queue`](struct.GlyphBrush.html#method.queue).
226 ///
227 /// Trims the cache, see [caching behaviour](#caching-behaviour).
228 ///
229 /// # Raw usage
230 /// Can also be used with gfx raw render & depth views if necessary. The `Format` must also
231 /// be provided. [See example.](struct.GlyphBrush.html#raw-usage-1)
232 */
233
234 #[inline]
235 pub fn draw_queued<C: Facade + Deref<Target = Context>, S: Surface>(
236 &mut self,
237 facade: &C,
238 surface: &mut S,
239 ) {
240 let dims = facade.get_framebuffer_dimensions();
241 let transform = [
242 [2.0 / (dims.0 as f32), 0.0, 0.0, 0.0],
243 [0.0, 2.0 / (dims.1 as f32), 0.0, 0.0],
244 [0.0, 0.0, 1.0, 0.0],
245 [-1.0, -1.0, 0.0, 1.0],
246 ];
247 self.draw_queued_with_transform(transform, facade, surface)
248 }
249
250 /*
251 /// Draws all queued sections onto a render target, applying a position transform (e.g.
252 /// a projection). The transform applies directly to the `screen_position` coordinates from
253 /// queued `Sections`, with the Y axis inverted. Callers must account for the window size in
254 /// this transform.
255 /// See [`queue`](struct.GlyphBrush.html#method.queue).
256 ///
257 /// Trims the cache, see [caching behaviour](#caching-behaviour).
258 ///
259 /// # Raw usage
260 /// Can also be used with gfx raw render & depth views if necessary. The `Format` must also
261 /// be provided.
262 ///
263 /// ```no_run
264 /// # extern crate gfx;
265 /// # extern crate gfx_window_glutin;
266 /// # extern crate glutin;
267 /// # extern crate gfx_glyph;
268 /// # use gfx_glyph::{GlyphBrushBuilder};
269 /// # use gfx_glyph::Section;
270 /// # use gfx::format;
271 /// # use gfx::format::Formatted;
272 /// # use gfx::memory::Typed;
273 /// # fn main() -> Result<(), String> {
274 /// # let events_loop = glutin::EventsLoop::new();
275 /// # let (_window, _device, mut gfx_factory, gfx_color, gfx_depth) =
276 /// # gfx_window_glutin::init::<gfx::format::Srgba8, gfx::format::Depth>(
277 /// # glutin::WindowBuilder::new(),
278 /// # glutin::ContextBuilder::new(),
279 /// # &events_loop);
280 /// # let mut gfx_encoder: gfx::Encoder<_, _> = gfx_factory.create_command_buffer().into();
281 /// # let dejavu: &[u8] = include_bytes!("../../fonts/DejaVuSans.ttf");
282 /// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu)
283 /// # .build(gfx_factory.clone());
284 /// # let raw_render_view = gfx_color.raw();
285 /// # let raw_depth_view = gfx_depth.raw();
286 /// # let transform = [[0.0; 4]; 4];
287 /// glyph_brush.draw_queued_with_transform(
288 /// transform,
289 /// &mut gfx_encoder,
290 /// &(raw_render_view, format::Srgba8::get_format()),
291 /// &(raw_depth_view, format::Depth::get_format()),
292 /// )?
293 /// # ;
294 /// # Ok(())
295 /// # }
296 /// ```
297 */
298
299 pub fn draw_queued_with_transform<C: Facade + Deref<Target = Context>, S: Surface>(
300 &mut self,
301 transform: [[f32; 4]; 4],
302 facade: &C,
303 surface: &mut S,
304 ) {
305 let mut brush_action;
306 loop {
307 // We need this scope because of lifetimes.
308 // Ultimately, we'd like to put the &self.texture
309 // into the closure, but that'd inevitably
310 // borrow the entirety of self inside the closure.
311 // This is a problem with the language and is
312 // discussed here:
313 // http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
314 {
315 let tex = &self.texture;
316 brush_action = self.glyph_brush.process_queued(
317 |rect, tex_data| {
318 update_texture(tex, rect, tex_data);
319 },
320 to_vertex,
321 );
322 }
323 match brush_action {
324 Ok(_) => break,
325 Err(BrushError::TextureTooSmall { suggested }) => {
326 let (nwidth, nheight) = suggested;
327 self.texture = Texture2d::empty(facade, nwidth, nheight).unwrap();
328 self.glyph_brush.resize_texture(nwidth, nheight);
329 }
330 }
331 }
332
333 let sampler = glium::uniforms::Sampler::new(&self.texture)
334 .wrap_function(glium::uniforms::SamplerWrapFunction::Clamp)
335 .minify_filter(glium::uniforms::MinifySamplerFilter::Linear)
336 .magnify_filter(glium::uniforms::MagnifySamplerFilter::Linear);
337
338 match brush_action.unwrap() {
339 BrushAction::Draw(verts) => {
340 self.vertex_buffer = glium::VertexBuffer::new(facade, &verts).unwrap();
341 }
342 BrushAction::ReDraw => {}
343 };
344
345 let uniforms = uniform! {
346 font_tex: sampler,
347 transform: transform,
348 };
349
350 // drawing a frame
351 surface
352 .draw(
353 (&self.instances, self.vertex_buffer.per_instance().unwrap()),
354 &self.index_buffer,
355 &self.program,
356 &uniforms,
357 &self.params,
358 )
359 .unwrap();
360 }
361
362 /// Adds an additional font to the one(s) initially added on build.
363 ///
364 /// Returns a new [`FontId`](struct.FontId.html) to reference this font.
365 pub fn add_font<I: Into<F>>(&mut self, font_data: I) -> FontId {
366 self.glyph_brush.add_font(font_data)
367 }
368}
369
370impl<'l, F: Font, H: BuildHasher> GlyphCruncher<F> for GlyphBrush<'l, F, H> {
371 fn glyph_bounds_custom_layout<'a, S, L>(
372 &mut self,
373 section: S,
374 custom_layout: &L,
375 ) -> Option<glyph_brush::ab_glyph::Rect>
376 where
377 L: GlyphPositioner + Hash,
378 S: Into<Cow<'a, Section<'a>>>,
379 {
380 self.glyph_brush
381 .glyph_bounds_custom_layout(section, custom_layout)
382 }
383
384 fn glyphs_custom_layout<'a, 'b, S, L>(
385 &'b mut self,
386 section: S,
387 custom_layout: &L,
388 ) -> SectionGlyphIter<'b>
389 where
390 L: GlyphPositioner + Hash,
391 S: Into<Cow<'a, Section<'a>>>,
392 {
393 self.glyph_brush
394 .glyphs_custom_layout(section, custom_layout)
395 }
396
397 /// Returns the available fonts.
398 ///
399 /// The `FontId` corresponds to the index of the font data.
400 #[inline]
401 fn fonts(&self) -> &[F] {
402 self.glyph_brush.fonts()
403 }
404}