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}