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