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)
list(APPEND MagnumText_SRCS
GlyphCache.cpp
Renderer.cpp)
GlyphCache.cpp)
list(APPEND MagnumText_GracefulAssert_SRCS
DistanceFieldGlyphCache.cpp)
DistanceFieldGlyphCache.cpp
Renderer.cpp)
list(APPEND MagnumText_HEADERS
DistanceFieldGlyphCache.h
GlyphCache.h

92
src/Magnum/Text/Renderer.cpp

@ -27,7 +27,9 @@
#include <Corrade/Containers/Array.h>
#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/Triple.h>
#include "Magnum/Mesh.h"
#include "Magnum/GL/Context.h"
@ -37,6 +39,7 @@
#include "Magnum/Shaders/GenericGL.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractGlyphCache.h"
#include "Magnum/Text/AbstractShaper.h"
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) {
/* 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
reality the actual vertex count will be smaller, but allocating more at
once is better than reallocating many times later. */
std::vector<Vertex> vertices;
vertices.reserve(text.size()*4);
/* Total rendered bounds, initial line position, line increment, last+1
vertex on previous line */
/* Scaling factor, line advance, total rendered bounds, initial line
position, last+1 vertex on previous line */
const Float scale = size/font.size();
const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*scale);
Range2D rectangle;
Vector2 linePosition;
const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*size/font.size());
std::size_t lastLineLastVertex = 0;
/* 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;
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 */
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 */
line.assign(text, prevPos, pos-prevPos);
/* Layout the line */
Containers::Pointer<AbstractLayouter> layouter = font.layout(cache, size, line);
/* Shape the line, get the results */
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
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. */
CORRADE_INTERNAL_ASSERT(vertices.size() + layouter->glyphCount()*4 <= vertices.capacity());
CORRADE_INTERNAL_ASSERT(vertices.size() + shaper->glyphCount()*4 <= vertices.capacity());
/* Bounds of rendered line */
Range2D lineRectangle;
/* Render all glyphs */
/* Create quads for all glyphs */
Vector2 cursorPosition(linePosition);
for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) {
const Containers::Pair<Range2D, Range2D> quadPositionTextureCoordinates = layouter->renderGlyph(i, cursorPosition, lineRectangle);
for(UnsignedInt i = 0; i != lineGlyphs.size(); ++i) {
/* 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
| |
| |
| |
1---3 */
vertices.insert(vertices.end(), {
{quadPositionTextureCoordinates.first().topLeft(),
quadPositionTextureCoordinates.second().topLeft()},
{quadPositionTextureCoordinates.first().bottomLeft(),
quadPositionTextureCoordinates.second().bottomLeft()},
{quadPositionTextureCoordinates.first().topRight(),
quadPositionTextureCoordinates.second().topRight()},
{quadPositionTextureCoordinates.first().bottomRight(),
quadPositionTextureCoordinates.second().bottomRight()}
{quadPosition.topLeft(), quadTextureCoordinates.topLeft()},
{quadPosition.bottomLeft(), quadTextureCoordinates.bottomLeft()},
{quadPosition.topRight(), quadTextureCoordinates.topRight()},
{quadPosition.bottomRight(), quadTextureCoordinates.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? */
@ -150,8 +200,8 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it)
it->position.x() += alignmentOffsetX;
/* Extend the rectangle with final line bounds, similarly to
AbstractFont::renderGlyph() */
/* Extend the rectangle with final line bounds, similarly to what was
done for each glyph above */
rectangle = Math::join(rectangle, lineRectangle);
/* 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 alignment Text alignment
*
* Returns tuple with vertex positions, texture coordinates, indices
* and rectangle spanning the rendered text.
* Returns a tuple with vertex positions, texture coordinates, indices
* 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);
@ -291,9 +292,10 @@ template<UnsignedInt dimensions> class MAGNUM_TEXT_EXPORT Renderer: public Abstr
* @param usage Usage of vertex and index buffer
* @param alignment Text alignment
*
* Returns mesh prepared for use with @ref Shaders::VectorGL or
* @ref Shaders::DistanceFieldVectorGL and rectangle spanning the
* rendered text.
* Returns a mesh prepared for use with @ref Shaders::VectorGL or
* @ref Shaders::DistanceFieldVectorGL and a 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<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
MagnumOpenGLTester
MagnumDebugTools)
corrade_add_test(TextRendererGLTest RendererGLTest.cpp LIBRARIES MagnumText MagnumOpenGLTester)
corrade_add_test(TextRendererGLTest RendererGLTest.cpp
LIBRARIES
MagnumTextTestLib
MagnumOpenGLTester)
endif()

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

@ -23,12 +23,15 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Containers/Triple.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/Extensions.h"
#include "Magnum/GL/OpenGLTester.h"
@ -48,6 +51,9 @@ struct RendererGLTest: GL::OpenGLTester {
void mutableText();
void multiline();
void arrayGlyphCache();
void fontNotFoundInCache();
};
const struct {
@ -85,7 +91,10 @@ RendererGLTest::RendererGLTest() {
&RendererGLTest::renderMeshIndexType,
&RendererGLTest::mutableText,
&RendererGLTest::multiline});
&RendererGLTest::multiline,
&RendererGLTest::arrayGlyphCache,
&RendererGLTest::fontNotFoundInCache});
}
struct TestShaper: AbstractShaper {
@ -543,6 +552,39 @@ void RendererGLTest::multiline() {
}), 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)

Loading…
Cancel
Save