Browse Source

Text: port Renderer away from deprecated APIs.

The Renderer now does what was before redundantly copypasted into
basically each and every font plugin.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
15e101dee7
  1. 6
      src/Magnum/Text/CMakeLists.txt
  2. 92
      src/Magnum/Text/Renderer.cpp
  3. 12
      src/Magnum/Text/Renderer.h
  4. 5
      src/Magnum/Text/Test/CMakeLists.txt
  5. 44
      src/Magnum/Text/Test/RendererGLTest.cpp

6
src/Magnum/Text/CMakeLists.txt

@ -58,10 +58,10 @@ set(MagnumText_PRIVATE_HEADERS
if(MAGNUM_TARGET_GL) if(MAGNUM_TARGET_GL)
list(APPEND MagnumText_SRCS list(APPEND MagnumText_SRCS
GlyphCache.cpp GlyphCache.cpp)
Renderer.cpp)
list(APPEND MagnumText_GracefulAssert_SRCS list(APPEND MagnumText_GracefulAssert_SRCS
DistanceFieldGlyphCache.cpp) DistanceFieldGlyphCache.cpp
Renderer.cpp)
list(APPEND MagnumText_HEADERS list(APPEND MagnumText_HEADERS
DistanceFieldGlyphCache.h DistanceFieldGlyphCache.h
GlyphCache.h GlyphCache.h

92
src/Magnum/Text/Renderer.cpp

@ -27,7 +27,9 @@
#include <Corrade/Containers/Array.h> #include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayViewStl.h> /** @todo remove once Renderer is STL-free */ #include <Corrade/Containers/ArrayViewStl.h> /** @todo remove once Renderer is STL-free */
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once Renderer is STL-free */ #include <Corrade/Containers/StringStl.h> /** @todo remove once Renderer is STL-free */
#include <Corrade/Containers/Triple.h>
#include "Magnum/Mesh.h" #include "Magnum/Mesh.h"
#include "Magnum/GL/Context.h" #include "Magnum/GL/Context.h"
@ -37,6 +39,7 @@
#include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/GenericGL.h"
#include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/AbstractGlyphCache.h"
#include "Magnum/Text/AbstractShaper.h"
namespace Magnum { namespace Text { namespace Magnum { namespace Text {
@ -67,17 +70,31 @@ struct Vertex {
}; };
std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& font, const AbstractGlyphCache& cache, const Float size, const std::string& text, const Alignment alignment) { std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& font, const AbstractGlyphCache& cache, const Float size, const std::string& text, const Alignment alignment) {
/* This was originally added as a runtime error into plugin implementations
during the transition period for the new AbstractGlyphCache API, now
it's an assert in the transition period for the Renderer API. Shouldn't
get triggered by existing code that uses 2D caches. */
CORRADE_ASSERT(cache.size().z() == 1,
"Text::Renderer: array glyph caches are not supported", {});
/* Find this font in the cache. This is an assert again, as not having a
font in the cache is a user error. */
Containers::Optional<UnsignedInt> fontId = cache.findFont(&font);
CORRADE_ASSERT(fontId,
"Text::Renderer: font not found among" << cache.fontCount() << "fonts in passed glyph cache", {});
/* Output data, reserve memory as when the text would be ASCII-only. In /* Output data, reserve memory as when the text would be ASCII-only. In
reality the actual vertex count will be smaller, but allocating more at reality the actual vertex count will be smaller, but allocating more at
once is better than reallocating many times later. */ once is better than reallocating many times later. */
std::vector<Vertex> vertices; std::vector<Vertex> vertices;
vertices.reserve(text.size()*4); vertices.reserve(text.size()*4);
/* Total rendered bounds, initial line position, line increment, last+1 /* Scaling factor, line advance, total rendered bounds, initial line
vertex on previous line */ position, last+1 vertex on previous line */
const Float scale = size/font.size();
const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*scale);
Range2D rectangle; Range2D rectangle;
Vector2 linePosition; Vector2 linePosition;
const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*size/font.size());
std::size_t lastLineLastVertex = 0; std::size_t lastLineLastVertex = 0;
/* Temp buffer so we don't allocate for each new line */ /* Temp buffer so we don't allocate for each new line */
@ -87,6 +104,16 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
*/ */
std::string line; std::string line;
line.reserve(text.size()); line.reserve(text.size());
struct Glyph {
UnsignedInt id;
Vector2 offset;
Vector2 advance;
};
Containers::Array<Glyph> glyphs{NoInit, text.size()};
/* Create a shaper */
/** @todo even with reusing a shaper this is all horrific, rework!! */
Containers::Pointer<AbstractShaper> shaper = font.createShaper();
/* Render each line separately and align it horizontally */ /* Render each line separately and align it horizontally */
std::size_t pos, prevPos = 0; std::size_t pos, prevPos = 0;
@ -97,39 +124,62 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
/* Copy the line into the temp buffer */ /* Copy the line into the temp buffer */
line.assign(text, prevPos, pos-prevPos); line.assign(text, prevPos, pos-prevPos);
/* Layout the line */ /* Shape the line, get the results */
Containers::Pointer<AbstractLayouter> layouter = font.layout(cache, size, line); shaper->shape(line);
const Containers::StridedArrayView1D<Glyph> lineGlyphs = glyphs.prefix(shaper->glyphCount());
shaper->glyphsInto(lineGlyphs.slice(&Glyph::id),
lineGlyphs.slice(&Glyph::offset),
lineGlyphs.slice(&Glyph::advance));
/* Verify that we don't reallocate anything. The only problem might /* Verify that we don't reallocate anything. The only problem might
arise when the layouter decides to compose one character from more arise when the layouter decides to compose one character from more
than one glyph (i.e. accents). Will remove the assert when this than one glyph (i.e. accents). Will remove the asserts when this
issue arises. */ issue arises. */
CORRADE_INTERNAL_ASSERT(vertices.size() + layouter->glyphCount()*4 <= vertices.capacity()); CORRADE_INTERNAL_ASSERT(vertices.size() + shaper->glyphCount()*4 <= vertices.capacity());
/* Bounds of rendered line */ /* Bounds of rendered line */
Range2D lineRectangle; Range2D lineRectangle;
/* Render all glyphs */ /* Create quads for all glyphs */
Vector2 cursorPosition(linePosition); Vector2 cursorPosition(linePosition);
for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { for(UnsignedInt i = 0; i != lineGlyphs.size(); ++i) {
const Containers::Pair<Range2D, Range2D> quadPositionTextureCoordinates = layouter->renderGlyph(i, cursorPosition, lineRectangle); /* Offset of the glyph rectangle relative to the cursor, layer,
texture coordinates. We checked that the glyph cache is 2D above
so the layer can be ignored. */
const Containers::Triple<Vector2i, Int, Range2Di> cacheGlyph = cache.glyph(*fontId, glyphs[i].id);
CORRADE_INTERNAL_ASSERT(cacheGlyph.second() == 0);
/* Quad rectangle, created from cache and shaper offset and the
texture rectangle, scaled to requested text size and translated
to current cursor */
const Range2D quadPosition = Range2D::fromSize(
Vector2{cacheGlyph.first()} + glyphs[i].offset,
Vector2{cacheGlyph.third().size()})
.scaled(Vector2{scale})
.translated(cursorPosition);
/* Normalized texture coordinates */
const Range2D quadTextureCoordinates = Range2D{cacheGlyph.third()}
.scaled(1.0f/Vector2{cache.size().xy()});
/* 0---2 /* 0---2
| | | |
| | | |
| | | |
1---3 */ 1---3 */
vertices.insert(vertices.end(), { vertices.insert(vertices.end(), {
{quadPositionTextureCoordinates.first().topLeft(), {quadPosition.topLeft(), quadTextureCoordinates.topLeft()},
quadPositionTextureCoordinates.second().topLeft()}, {quadPosition.bottomLeft(), quadTextureCoordinates.bottomLeft()},
{quadPositionTextureCoordinates.first().bottomLeft(), {quadPosition.topRight(), quadTextureCoordinates.topRight()},
quadPositionTextureCoordinates.second().bottomLeft()}, {quadPosition.bottomRight(), quadTextureCoordinates.bottomRight()}
{quadPositionTextureCoordinates.first().topRight(),
quadPositionTextureCoordinates.second().topRight()},
{quadPositionTextureCoordinates.first().bottomRight(),
quadPositionTextureCoordinates.second().bottomRight()}
}); });
/* Extend the line rectangle with current quad bounds. If the
original is zero size, it gets replaced. */
lineRectangle = Math::join(lineRectangle, quadPosition);
/* Advance cursor position to next character, again scaled */
cursorPosition += glyphs[i].advance*scale;
} }
/** @todo What about top-down text? */ /** @todo What about top-down text? */
@ -150,8 +200,8 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it) for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it)
it->position.x() += alignmentOffsetX; it->position.x() += alignmentOffsetX;
/* Extend the rectangle with final line bounds, similarly to /* Extend the rectangle with final line bounds, similarly to what was
AbstractFont::renderGlyph() */ done for each glyph above */
rectangle = Math::join(rectangle, lineRectangle); rectangle = Math::join(rectangle, lineRectangle);
/* Move to next line */ /* Move to next line */

12
src/Magnum/Text/Renderer.h

@ -67,8 +67,9 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer {
* @param text Text to render * @param text Text to render
* @param alignment Text alignment * @param alignment Text alignment
* *
* Returns tuple with vertex positions, texture coordinates, indices * Returns a tuple with vertex positions, texture coordinates, indices
* and rectangle spanning the rendered text. * and rectangle spanning the rendered text. Expects that @p font is
* present in @p cache and that @p cache isn't an array.
*/ */
static std::tuple<std::vector<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>, Range2D> render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, Alignment alignment = Alignment::LineLeft); static std::tuple<std::vector<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>, Range2D> render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, Alignment alignment = Alignment::LineLeft);
@ -291,9 +292,10 @@ template<UnsignedInt dimensions> class MAGNUM_TEXT_EXPORT Renderer: public Abstr
* @param usage Usage of vertex and index buffer * @param usage Usage of vertex and index buffer
* @param alignment Text alignment * @param alignment Text alignment
* *
* Returns mesh prepared for use with @ref Shaders::VectorGL or * Returns a mesh prepared for use with @ref Shaders::VectorGL or
* @ref Shaders::DistanceFieldVectorGL and rectangle spanning the * @ref Shaders::DistanceFieldVectorGL and a rectangle spanning the
* rendered text. * rendered text. Expects that @p font is present in @p cache and that
* @p cache isn't an array.
*/ */
static std::tuple<GL::Mesh, Range2D> 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); static std::tuple<GL::Mesh, Range2D> 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);

5
src/Magnum/Text/Test/CMakeLists.txt

@ -113,5 +113,8 @@ if(MAGNUM_TARGET_GL AND MAGNUM_BUILD_GL_TESTS)
MagnumText MagnumText
MagnumOpenGLTester MagnumOpenGLTester
MagnumDebugTools) MagnumDebugTools)
corrade_add_test(TextRendererGLTest RendererGLTest.cpp LIBRARIES MagnumText MagnumOpenGLTester) corrade_add_test(TextRendererGLTest RendererGLTest.cpp
LIBRARIES
MagnumTextTestLib
MagnumOpenGLTester)
endif() endif()

44
src/Magnum/Text/Test/RendererGLTest.cpp

@ -23,12 +23,15 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <sstream>
#include <Corrade/Containers/Array.h> #include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringView.h> #include <Corrade/Containers/StringView.h>
#include <Corrade/Containers/Triple.h> #include <Corrade/Containers/Triple.h>
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h> /** @todo drop once Debug is stream-free */
#include "Magnum/PixelFormat.h"
#include "Magnum/GL/Context.h" #include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h" #include "Magnum/GL/Extensions.h"
#include "Magnum/GL/OpenGLTester.h" #include "Magnum/GL/OpenGLTester.h"
@ -48,6 +51,9 @@ struct RendererGLTest: GL::OpenGLTester {
void mutableText(); void mutableText();
void multiline(); void multiline();
void arrayGlyphCache();
void fontNotFoundInCache();
}; };
const struct { const struct {
@ -85,7 +91,10 @@ RendererGLTest::RendererGLTest() {
&RendererGLTest::renderMeshIndexType, &RendererGLTest::renderMeshIndexType,
&RendererGLTest::mutableText, &RendererGLTest::mutableText,
&RendererGLTest::multiline}); &RendererGLTest::multiline,
&RendererGLTest::arrayGlyphCache,
&RendererGLTest::fontNotFoundInCache});
} }
struct TestShaper: AbstractShaper { struct TestShaper: AbstractShaper {
@ -543,6 +552,39 @@ void RendererGLTest::multiline() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void RendererGLTest::arrayGlyphCache() {
CORRADE_SKIP_IF_NO_ASSERT();
TestFont font;
font.openFile({}, 0.5f);
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
} cache{PixelFormat::R8Unorm, {100, 100, 3}};
std::ostringstream out;
Error redirectError{&out};
AbstractRenderer::render(font, cache, 0.25f, "abc");
CORRADE_COMPARE(out.str(), "Text::Renderer: array glyph caches are not supported\n");
}
void RendererGLTest::fontNotFoundInCache() {
CORRADE_SKIP_IF_NO_ASSERT();
TestFont font;
font.openFile({}, 0.5f);
GlyphCache cache{{100, 100}};
cache.addFont(34);
cache.addFont(25);
std::ostringstream out;
Error redirectError{&out};
AbstractRenderer::render(font, cache, 0.25f, "abc");
CORRADE_COMPARE(out.str(), "Text::Renderer: font not found among 2 fonts in passed glyph cache\n");
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::RendererGLTest) CORRADE_TEST_MAIN(Magnum::Text::Test::RendererGLTest)

Loading…
Cancel
Save