template<UnsignedInt dimensions>
Magnum::Text::Renderer class

Text renderer.

Lays out the text into mesh using given font. Use of ligatures, kerning etc. depends on features supported by particular font and its layouter.

Usage

Immutable text (e.g. menu items, credits) can be simply rendered using static methods, returning result either as data arrays or as fully configured mesh. The text can be then drawn as usual by configuring the shader and drawing the mesh:

/* Font instance, received from a plugin manager */
Containers::Pointer<Text::AbstractFont> font = ;

/* Open a 12 pt font */
font->openFile("font.ttf", 12.0f);

/* Populate a glyph cache */
Text::GlyphCache cache{Vector2i{128}};
font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                            "0123456789?!:;,. ");

Shaders::VectorGL2D shader;
GL::Buffer vertexBuffer, indexBuffer;
GL::Mesh mesh;

/* Render a 12 pt text, centered */
std::tie(mesh, std::ignore) = Text::Renderer2D::render(*font, cache, 12.0f,
    "Hello World!", vertexBuffer, indexBuffer, GL::BufferUsage::StaticDraw,
    Text::Alignment::LineCenter);

/* Projection matrix is matching application window size to have the size match
   12 pt in other applications, assuming a 96 DPI display and no UI scaling. */
Matrix3 projectionMatrix = Matrix3::projection(Vector2{windowSize()});

/* Draw the text on the screen */
shader
    .setTransformationProjectionMatrix(projectionMatrix)
    .setColor(0xffffff_rgbf)
    .bindVectorTexture(cache.texture())
    .draw(mesh);

See render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, Alignment) and render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, GL::Buffer&, GL::Buffer&, GL::BufferUsage, Alignment) for more information.

While this method is sufficient for one-shot rendering of static texts, for mutable texts (e.g. FPS counters, chat messages) there is another approach that doesn't recreate everything on each text change:

/* Initialize the renderer and reserve memory for enough glyphs */
Text::Renderer2D renderer{*font, cache, 12.0f, Text::Alignment::LineCenter};
renderer.reserve(32, GL::BufferUsage::DynamicDraw, GL::BufferUsage::StaticDraw);

/* Update the text occasionally */
renderer.render("Hello World Countdown: 10");

/* Draw the text on the screen */
shader.setTransformationProjectionMatrix(projectionMatrix)
    .setColor(0xffffff_rgbf)
    .bindVectorTexture(cache.texture())
    .draw(renderer.mesh());

Font size

As mentioned in AbstractFont class documentation, the size at which the font is loaded is decoupled from the size at which a concrete text is rendered. In particular, with a concrete projection matrix, the size you pass to either render() or to the Renderer() constructor will always result in the same size of the rendered text, independently of the size the font was loaded in. Size of the loaded font is the size at which the glyphs get prerendered into the glyph cache, affecting visual quality.

When rendering the text, there are two common approaches — either setting up the size to match a global user interface scale, or having the text size proportional to the window size. The first approach results in e.g. a 12 pt font matching a 12 pt font in other applications, and is what's shown in the above snippets. The most straightforward way to achieve that is to set up the projection matrix size to match actual window pixels, such as Platform::*Application::windowSize(). If using the regular GlyphCache, for best visual quality it should be created with the AbstractFont loaded at the same size as the text to be rendered, although often a double supersampling achieves a crisper result. I.e., loading the font with 24 pt, but rendering with 12 pt. See below for additional considerations for proper DPI awareness.

The second approach, with text size being relative to the window size, is for cases where the text is meant to match surrounding art, such as in a game menu. In this case the projection size is usually something arbitrary that doesn't match window pixels, and the text point size then has to be relative to that. For this use case a DistanceFieldGlyphCache is the better match, as it can provide text at different sizes with minimal quality loss. See its documentation for details about picking the right font size and other parameters for best results.

DPI awareness

To achieve crisp rendering and/or text size matching other applications on HiDPI displays, additional steps need to be taken. There are two separate concepts for DPI-aware rendering:

  • Interface size — size at which the interface elements are positioned on the screen. Often, for simplicity, the interface is using some "virtual units", so a 12 pt font is still a 12 pt font independently of how the interface is scaled compared to actual display properties (for example by setting a global 150% scale in the desktop environment, or by zooming a browser window). The size used by the Renderer should match these virtual units.
  • Framebuffer size — how many pixels is actually there. If a 192 DPI display has a 200% interface scale, a 12 pt font would be 32 pixels. But if it only has a 150% scale, all interface elements will be smaller, and a 12 pt font would be only 24 pixels. The size used by the AbstractFont and GlyphCache should be chosen with respect to the actual physical pixels.

When using for example Platform::Sdl2Application or other *Application implementations, you usually have three values at your disposal — windowSize(), framebufferSize() and dpiScaling(). Their relation is documented thoroughly in DPI awareness, for this particular case a scaled interface size, used instead of window size for the projection, would be calculated like this:

Vector2 interfaceSize = Vector2{windowSize()}/dpiScaling();

And a multiplier for the AbstractFont and GlyphCache font size like this. The Renderer keeps using the size without this multiplier.

Float sizeMultiplier =
    (Vector2{framebufferSize()}*dpiScaling()/Vector2{windowSize()}).max();

Required OpenGL functionality

Mutable text rendering requires ARB_map_buffer_range on desktop OpenGL (also part of OpenGL ES 3.0). If EXT_map_buffer_range is not available in ES 2.0, at least OES_mapbuffer must be supported for asynchronous buffer updates. There is no similar extension in WebGL, thus plain (and slow) buffer updates are used there.

Base classes

class AbstractRenderer
Base for text renderers.

Public static functions

static auto render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage usage, Alignment alignment = Alignment::LineLeft) -> std::tuple<GL::Mesh, Range2D>
Render text.

Constructors, destructors, conversion operators

Renderer(AbstractFont& font, const AbstractGlyphCache& cache, Float size, Alignment alignment = Alignment::LineLeft) explicit
Constructor.
Renderer(AbstractFont&, AbstractGlyphCache&&, Float, Alignment alignment = Alignment::LineLeft) deleted

Function documentation

template<UnsignedInt dimensions>
static std::tuple<GL::Mesh, Range2D> Magnum::Text::Renderer<dimensions>::render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage usage, Alignment alignment = Alignment::LineLeft)

Render text.

Parameters
font Font
cache Glyph cache
size Font size
text Text to render
vertexBuffer Buffer where to store vertices
indexBuffer Buffer where to store indices
usage Usage of vertex and index buffer
alignment Text alignment

Returns a mesh prepared for use with Shaders::VectorGL or Shaders::DistanceFieldVectorGL and a rectangle spanning the rendered text. Expects that font is present in cache and that cache isn't an array.

template<UnsignedInt dimensions>
Magnum::Text::Renderer<dimensions>::Renderer(AbstractFont& font, const AbstractGlyphCache& cache, Float size, Alignment alignment = Alignment::LineLeft) explicit

Constructor.

Parameters
font Font
cache Glyph cache
size Font size
alignment Text alignment

template<UnsignedInt dimensions>
Magnum::Text::Renderer<dimensions>::Renderer(AbstractFont&, AbstractGlyphCache&&, Float, Alignment alignment = Alignment::LineLeft) deleted

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.