Browse Source

Text: random improvements to font and glyph cache docs.

Not touching DistanceFieldGlyphCacheGL docs yet, but the rest was so bad
that I just had to go and fix it all.
pull/674/head
Vladimír Vondruš 1 year ago
parent
commit
b26d694d13
  1. 38
      doc/snippets/Text-gl.cpp
  2. 52
      doc/snippets/Text.cpp
  3. 55
      src/Magnum/Text/AbstractFont.h
  4. 70
      src/Magnum/Text/AbstractGlyphCache.h
  5. 4
      src/Magnum/Text/DistanceFieldGlyphCacheGL.h
  6. 28
      src/Magnum/Text/GlyphCacheGL.h
  7. 12
      src/Magnum/Text/Renderer.h

38
doc/snippets/Text-gl.cpp

@ -61,17 +61,18 @@ Containers::Pointer<Text::AbstractFont> font =
if(!font->openFile("font.ttf", 12.0f))
Fatal{} << "Can't open font.ttf with StbTrueTypeFont";
Text::GlyphCacheGL cache{PixelFormat::R8Unorm, Vector2i{128}};
font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. ");
Text::GlyphCacheGL cache{PixelFormat::R8Unorm, {256, 256}};
if(!font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. "))
Fatal{} << "Glyph cache too small to fit all characters";
/* [AbstractFont-usage] */
}
{
/* [AbstractGlyphCache-filling-construct] */
Text::GlyphCacheGL cache{PixelFormat::R8Unorm, Vector2i{512}};
/* [AbstractGlyphCache-filling-construct] */
/* [AbstractGlyphCache-usage-construct] */
Text::GlyphCacheGL cache{PixelFormat::R8Unorm, {256, 256}};
/* [AbstractGlyphCache-usage-construct] */
}
{
@ -84,28 +85,13 @@ Containers::Pointer<Text::AbstractFont> font = DOXYGEN_ELLIPSIS(manager.loadAndI
font->openFile("font.ttf", 96.0f);
Text::DistanceFieldGlyphCacheGL cache{Vector2i{1024}, Vector2i{128}, 12};
font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. ");
if(!font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. "))
Fatal{} << "Glyph cache too small to fit all characters";
/* [DistanceFieldGlyphCacheGL-usage] */
}
{
/* -Wnonnull in GCC 11+ "helpfully" says "this is null" if I don't initialize
the font pointer. I don't care, I just want you to check compilation errors,
not more! */
PluginManager::Manager<Text::AbstractFont> manager;
/* [GlyphCacheGL-usage] */
Containers::Pointer<Text::AbstractFont> font = DOXYGEN_ELLIPSIS(manager.loadAndInstantiate(""));
font->openFile("font.ttf", 12.0f);
Text::GlyphCacheGL cache{PixelFormat::R8Unorm, Vector2i{128}};
font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. ");
/* [GlyphCacheGL-usage] */
}
{
/* -Wnonnull in GCC 11+ "helpfully" says "this is null" if I don't initialize
the font pointer. I don't care, I just want you to check compilation errors,

52
doc/snippets/Text.cpp

@ -43,6 +43,7 @@
#include <Corrade/Utility/Resource.h>
#include "Magnum/FileCallback.h"
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/Math/Color.h"
@ -98,6 +99,33 @@ CORRADE_PLUGIN_REGISTER(MyFontConverter, MyNamespace::MyFontConverter,
avoid -Wmisssing-prototypes */
void mainText();
void mainText() {
{
PluginManager::Manager<Text::AbstractFont> manager;
Containers::Pointer<Text::AbstractFont> font =
manager.loadAndInstantiate("StbTrueTypeFont");
struct: Text::AbstractGlyphCache {
using Text::AbstractGlyphCache::AbstractGlyphCache;
Text::GlyphCacheFeatures doFeatures() const override { return {}; }
} cache{PixelFormat::R8Unorm, Vector2i{256}};
/* [AbstractFont-glyph-cache-by-id] */
CORRADE_INTERNAL_ASSERT_OUTPUT(font->fillGlyphCache(cache, {
font->glyphForName("fi"),
font->glyphForName("f_f"),
font->glyphForName("fl"),
DOXYGEN_ELLIPSIS()
}));
/* [AbstractFont-glyph-cache-by-id] */
/* [AbstractFont-glyph-cache-all] */
Containers::Array<UnsignedInt> glyphs{NoInit, font->glyphCount()};
for(UnsignedInt i = 0; i != font->glyphCount(); ++i)
glyphs[i] = i;
CORRADE_INTERNAL_ASSERT_OUTPUT(font->fillGlyphCache(cache, glyphs));
/* [AbstractFont-glyph-cache-all] */
}
{
PluginManager::Manager<Text::AbstractFont> manager;
Containers::Pointer<Text::AbstractFont> font =
@ -179,6 +207,26 @@ font->setFileCallback([](const std::string& filename,
/* [AbstractFont-setFileCallback-template] */
}
{
/* -Wnonnull in GCC 11+ "helpfully" says "this is null" if I don't initialize
the font pointer. I don't care, I just want you to check compilation errors,
not more! */
PluginManager::Manager<Text::AbstractFont> manager;
struct: Text::AbstractGlyphCache {
using Text::AbstractGlyphCache::AbstractGlyphCache;
Text::GlyphCacheFeatures doFeatures() const override { return {}; }
} cache{PixelFormat::R8Unorm, Vector2i{256}};
/* [AbstractGlyphCache-usage-fill] */
Containers::Pointer<Text::AbstractFont> font = DOXYGEN_ELLIPSIS(manager.loadAndInstantiate(""));
if(!font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. "))
Fatal{} << "Glyph cache too small to fit all characters";
/* [AbstractGlyphCache-usage-fill] */
}
{
struct: Text::AbstractGlyphCache {
using Text::AbstractGlyphCache::AbstractGlyphCache;
@ -186,7 +234,7 @@ struct: Text::AbstractGlyphCache {
Text::GlyphCacheFeatures doFeatures() const override { return {}; }
} cache{PixelFormat::R8Unorm, Vector2i{256}};
/* [AbstractGlyphCache-filling-images] */
Containers::ArrayView<const ImageView2D> images = DOXYGEN_ELLIPSIS({});
Containers::Array<Image2D> images = DOXYGEN_ELLIPSIS({}); /* or ImageView2D, Trade::ImageData2D... */
/* [AbstractGlyphCache-filling-images] */
/* [AbstractGlyphCache-filling-font] */
@ -200,7 +248,7 @@ cache.atlas().clearFlags(
TextureTools::AtlasLandfillFlag::RotatePortrait|
TextureTools::AtlasLandfillFlag::RotateLandscape);
Containers::Optional<Range2Di> range = cache.atlas().add(
stridedArrayView(images).slice(&ImageView2D::size),
stridedArrayView(images).slice(&Image2D::size),
offsets);
CORRADE_INTERNAL_ASSERT(range);
/* [AbstractGlyphCache-filling-atlas] */

55
src/Magnum/Text/AbstractFont.h

@ -114,17 +114,21 @@ glyphs will be rasterized. Then it stays open until the font is destroyed,
@ref close() or another font is opened.
In the following example a font is loaded from the filesystem using the
@ref StbTrueTypeFont plugin, prerendering all needed glyphs, completely with
all error handling:
@ref StbTrueTypeFont plugin, prerendering desired glyphs into a
@ref GlyphCacheGL, completely with all error handling --- depending on the
font file, font plugin implementation, font size and cache size, it may happen
that the characters won't all fit, which should be checked by the application:
@snippet Text-gl.cpp AbstractFont-usage
See @ref plugins for more information about general plugin usage and the list
of @m_class{m-doc} <a href="#derived-classes">derived classes</a> for available
font plugins. See @ref GlyphCache for more information about glyph caches and
@ref BasicRenderer "Renderer*D" for information about actual text rendering.
font plugins. See @ref AbstractGlyphCache for more information about glyph
caches, @ref BasicRenderer "Renderer*D" for high-level text rendering, and
@ref AbstractShaper for low-level access to the font text shaping
functionality.
@subsection Text-AbstractFont-usage-font-size Font size
@section Text-AbstractFont-font-size Font size
Font libraries specify font size in *points*, where 1 pt = ~1.333 px at 96 DPI,
so in the above snippet a 12 pt font corresponds to 16 px on a 96 DPI display.
@ -136,18 +140,47 @@ properties are specified *in pixels* in @ref lineHeight(), @ref ascent() and
@ref descent().
The font size used when opening the font affects how large the glyphs will be
when rendered into the @ref GlyphCache. Actual text rendering with
when rendered into the glyph cache. Actual text rendering with
@ref BasicRenderer "Renderer*D" however uses its own font size, and the
rendered size is then additionally depending on the actual projection used.
This decoupling of font sizes is useful for example in case of
@ref DistanceFieldGlyphCache, where a single prerendered glyph size can be used
to render arbitrarily large font sizes without becoming blurry or jaggy. When
not using a distance field glyph cache, it's usually desirable to have the font
size and the actual rendered size match. See
@ref DistanceFieldGlyphCacheGL, where a single prerendered glyph size can be
used to render arbitrarily large font sizes without becoming blurry or jaggy.
When not using a distance field glyph cache, it's usually desirable to have the
font size and the actual rendered size match. See
@ref Text-BasicRenderer-usage-font-size "the Renderer*D documentation" for
further information about picking font sizes.
@subsection Text-AbstractFont-usage-callbacks Loading data from memory, using file callbacks
@section Text-AbstractFont-glyph-cache Glyph cache filling options
Besides filling the cache with glyphs corresponding to individual characters in
a UTF-8 string, it's possible to directly specify glyph IDs with
@ref fillGlyphCache(AbstractGlyphCache&, const Containers::StridedArrayView1D<const UnsignedInt>&).
That's useful for example with ligatures like *fl* or *ff* that can get used by
a particular font implementation instead of individual letters, and which thus
don't have a clear mapping to a Unicode codepoint. You can use some font
introspection utility to retrieve either directly the glyph IDs, or get just
glyph names and match them with IDs using @ref glyphForName() at runtime. The
following snippet prerenders a few of the named ligatures present in the
[Source Sans](https://github.com/adobe-fonts/source-sans) font that this
website uses. Again explicitly checking that the operation succeeds, this time
with a @ref CORRADE_INTERNAL_ASSERT_OUTPUT() macro:
@snippet Text.cpp AbstractFont-glyph-cache-by-id
Subsequent calls to @ref fillGlyphCache() add to already existing glyphs,
allowing for incremental filling based on which glyphs are needed by actual
text. Such as by passing the output of @ref AbstractShaper::glyphIdsInto()
through @ref AbstractGlyphCache::glyphIdsInto() and adding all glyphs which it
didn't find. Note that, however, the glyph cache packing is the most efficient
when all glyphs are added at once.
Finally, it's possible to use this overload to populate the glyph cache with
all glyphs in the font:
@snippet Text.cpp AbstractFont-glyph-cache-all
@section Text-AbstractFont-usage-callbacks Loading data from memory, using file callbacks
Besides loading data directly from the filesystem using @ref openFile() like
shown above, it's possible to use @ref openData() to import data from memory.

70
src/Magnum/Text/AbstractGlyphCache.h

@ -107,37 +107,44 @@ MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& output, GlyphCacheFeatures value);
@m_since{2019,10}
A GPU-API-agnostic base for glyph caches, supporting multiple fonts and both 2D
and 2D array textures. See the @ref GlyphCache and @ref DistanceFieldGlyphCache
subclasses for concrete OpenGL implementations. The base class provides a
common interface for adding fonts, glyph properties, uploading glyph data and
retrieving glyph properties back.
and 2D array textures. Provides a common interface for adding fonts, glyph
properties, uploading glyph data and retrieving glyph properties back, the
@ref GlyphCacheGL and @ref DistanceFieldGlyphCacheGL subclasses then provide
concrete implementations backed with an OpenGL texture.
@section Text-AbstractGlyphCache-filling Filling the glyph cache
@section Text-AbstractGlyphCache-usage Basic usage
A glyph cache is constructed through the concrete GPU-API-specific subclasses,
namely the @ref GlyphCache and @ref DistanceFieldGlyphCache classes mentioned
above. Depending on the use case and platform capabilities it's also possible
to create glyph caches with 2D texture arrays, for example when large alphabets
are used or when the cache may get filled on-demand with many additional
glyphs.
A glyph cache is created in an appropriate @ref PixelFormat and a size.
@ref PixelFormat::R8Unorm is the usual choice, @ref PixelFormat::RGBA8Unorm is
useful for emoji fonts or when arbitrary icon data are put into the cache.
@snippet Text-gl.cpp AbstractGlyphCache-filling-construct
The rest of this section describes low level usage of the glyph cache filling
APIs, which are useful mainly when implementing an @ref AbstractFont itself or
when adding arbitrary other image data to the cache. When using the glyph cache
with an existing @ref AbstractFont instance, often the high level use involves
just calling @ref AbstractFont::fillGlyphCache(), which does all of the
following on its own.
Let's say we want to fill the glyph cache with a custom set of images that
don't necessarily need to be a font per se. Assuming the input images are
stored in a simple array, and the goal is to put them all together into the
cache and reference them later simply by their array indices.
such as @ref GlyphCacheGL. Its constructor takes a @ref PixelFormat and desired
texture size. @ref PixelFormat::R8Unorm is the usual choice,
@ref PixelFormat::RGBA8Unorm is useful for emoji fonts or when arbitrary icon
data are put into the cache.
@snippet Text-gl.cpp AbstractGlyphCache-usage-construct
Afterwards, assuming there's an opened @ref AbstractFont instance and a known
set of glyphs to prerender, the high-level use involves just calling
@ref AbstractFont::fillGlyphCache(), which then does all packing and data
copying on its own. Note that depending on the font file, font plugin
implementation, font size and cache size, it may happen that the characters
won't all fit, which should be checked by the application:
@snippet Text.cpp AbstractGlyphCache-usage-fill
As long as the cache size allows, you can call
@ref AbstractFont::fillGlyphCache() multiple times with additional glyphs and
other fonts. See the @ref Text-AbstractFont-glyph-cache "AbstractFont documentation"
for more options for glyph cache filling.
@section Text-AbstractGlyphCache-filling Filling the glyph cache directly
This section describes low level usage of the glyph cache filling APIs, which
are useful mainly when implementing an @ref AbstractFont itself or when adding
arbitrary other image data to the cache. Let's say we want to fill the glyph
cache with a custom set of images that don't necessarily need to be a font per
se. Assuming the input images are stored in a simple array, and the goal is to
put them all together into the cache and reference them later simply by their
array indices.
@snippet Text.cpp AbstractGlyphCache-filling-images
@ -192,7 +199,7 @@ letters *j* or *q* that reach below the baseline).
Important is to call @ref flushImage() at the end, which makes the glyph cache
update its actual GPU-side texture based on what area of the image was updated.
In case of @ref DistanceFieldGlyphCache for example it also triggers distance
In case of @ref DistanceFieldGlyphCacheGL for example it also triggers distance
field generation for given area.
If the images put into the cache are meant to be used with general meshes, the
@ -852,8 +859,9 @@ class MAGNUM_TEXT_EXPORT AbstractGlyphCache {
* @param[in] fontGlyphIds Glyph IDs in given font
* @param[out] glyphIds Resulting cache-global glyph IDs
*
* A batch variant of @ref glyphId(), mainly meant to be used to index
* the @ref glyphOffsets() const, @ref glyphLayers() const and
* A batch variant of @ref glyphId(), mainly meant to be used with the
* output of @ref AbstractShaper::glyphIdsInto() to then index the
* @ref glyphOffsets() const, @ref glyphLayers() const and
* @ref glyphRectangles() const views.
*
* The @p fontId is expected to be less than @ref fontCount(), all

4
src/Magnum/Text/DistanceFieldGlyphCacheGL.h

@ -44,8 +44,8 @@ namespace Magnum { namespace Text {
@m_since_latest
Unlike the base @ref GlyphCacheGL, this class converts each binary image to a
distance field. It's not possible to only use this cache for monochrome glyphs
as the internal texture format is single-channel.
distance field. It's possible to only use this cache for monochrome glyphs as
the internal texture format is single-channel.
@section Text-DistanceFieldGlyphCacheGL-usage Usage

28
src/Magnum/Text/GlyphCacheGL.h

@ -40,28 +40,21 @@
namespace Magnum { namespace Text {
/**
@brief OpenGL glyph cache
@brief OpenGL implementation of a glyph cache
@m_since_latest
Contains font glyphs rendered into a texture atlas.
@section Text-GlyphCacheGL-usage Usage
Create the @ref GlyphCacheGL object with sufficient size and then call
@ref AbstractFont::createGlyphCache() to fill it with glyphs.
@snippet Text-gl.cpp GlyphCacheGL-usage
See the @ref BasicRenderer "Renderer*D" class for information about text
rendering. The @ref AbstractGlyphCache base class has more information about
general glyph cache usage.
Implementation of an @ref AbstractGlyphCache backed by a @ref GL::Texture2D.
See the @ref AbstractGlyphCache class documentation for information about
setting up an instance of this class and filling it with glyphs. See the
@ref DistanceFieldGlyphCacheGL subclass for a variant that adds distance field
processing on top.
@section Text-GlyphCacheGL-internal-format Internal texture format
The @ref GL::TextureFormat used by @ref texture() is implicitly coming from
@ref GL::textureFormat(Magnum::PixelFormat) applied to @ref format(), or if
@ref GlyphCacheFeature::ImageProcessing is supported, to @ref processedFormat()
instead.
@ref GL::textureFormat(Magnum::PixelFormat) applied to the @ref format() that
was passed at construction time, or if @ref GlyphCacheFeature::ImageProcessing
is supported, to @ref processedFormat() instead.
If @ref PixelFormat::R8Unorm is used for @ref format() or if
@ref GlyphCacheFeature::ImageProcessing is supported and
@ -195,8 +188,7 @@ class MAGNUM_TEXT_EXPORT GlyphCacheGL: public AbstractGlyphCache {
private:
MAGNUM_TEXT_LOCAL GlyphCacheFeatures doFeatures() const override;
/** @todo make those MAGNUM_TEXT_LOCAL again once MagnumFont doesn't
need to subclass anything anymore */
/* These are not MAGNUM_TEXT_LOCAL because the test makes a subclass */
void doSetImage(const Vector2i& offset, const ImageView2D& image) override;
/* Used if a subclass advertises GlyphCacheFeature::ImageProcessing /
ProcessedImageDownload in its doFeatures() */

12
src/Magnum/Text/Renderer.h

@ -524,7 +524,7 @@ that doesn't recreate everything on each text change:
@subsection Text-BasicRenderer-usage-font-size Font size
As mentioned in @ref Text-AbstractFont-usage-font-size "AbstractFont class documentation",
As mentioned in @ref Text-AbstractFont-font-size "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 @ref render() or to the @ref BasicRenderer()
@ -539,7 +539,7 @@ 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 @ref Platform::Sdl2Application::windowSize() "Platform::*Application::windowSize()".
If using the regular @ref GlyphCache, for best visual quality it should be
If using the regular @ref GlyphCacheGL, for best visual quality it should be
created with the @ref 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
@ -549,8 +549,8 @@ 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 @ref DistanceFieldGlyphCache is the better match, as it can
provide text at different sizes with minimal quality loss. See its
For this use case a @ref DistanceFieldGlyphCacheGL 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.
@ -571,7 +571,7 @@ concepts for DPI-aware rendering:
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 @ref AbstractFont and
@ref GlyphCache should be chosen with respect to the actual physical
@ref GlyphCacheGL should be chosen with respect to the actual physical
pixels.
When using for example @ref Platform::Sdl2Application or other `*Application`
@ -585,7 +585,7 @@ would be calculated like this:
@snippet Text-gl.cpp BasicRenderer-dpi-interface-size
And a multiplier for the @ref AbstractFont and @ref GlyphCache font size like
And a multiplier for the @ref AbstractFont and @ref GlyphCacheGL font size like
this. The @ref BasicRenderer "Renderer*D" keeps using the size without this
multiplier.

Loading…
Cancel
Save