From 41f4d2fa5d302d9926aa6b2e46e4a447aee890ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 18:03:14 +0200 Subject: [PATCH] Text: added tests for TextRenderer. Exposed vertex and index buffer of mutable TextRenderer, fixed computation of bounding rectangle. --- src/Text/Test/CMakeLists.txt | 1 + src/Text/Test/TextRendererGLTest.cpp | 247 +++++++++++++++++++++++++++ src/Text/TextRenderer.cpp | 69 +++++--- src/Text/TextRenderer.h | 10 +- 4 files changed, 298 insertions(+), 29 deletions(-) create mode 100644 src/Text/Test/TextRendererGLTest.cpp diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt index cea16a17b..44adf1510 100644 --- a/src/Text/Test/CMakeLists.txt +++ b/src/Text/Test/CMakeLists.txt @@ -32,4 +32,5 @@ corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIB if(BUILD_GL_TESTS) corrade_add_test(TextGlyphCacheGLTest GlyphCacheGLTest.cpp LIBRARIES MagnumText ${GL_TEST_LIBRARIES}) + corrade_add_test(TextRendererGLTest TextRendererGLTest.cpp LIBRARIES MagnumText ${GL_TEST_LIBRARIES}) endif() diff --git a/src/Text/Test/TextRendererGLTest.cpp b/src/Text/Test/TextRendererGLTest.cpp new file mode 100644 index 000000000..9bae32ab7 --- /dev/null +++ b/src/Text/Test/TextRendererGLTest.cpp @@ -0,0 +1,247 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Test/AbstractOpenGLTester.h" +#include "Text/AbstractFont.h" +#include "Text/TextRenderer.h" + +namespace Magnum { namespace Text { namespace Test { + +class TextRendererGLTest: public Magnum::Test::AbstractOpenGLTester { + public: + explicit TextRendererGLTest(); + + void renderData(); + void renderMesh(); + void mutableText(); +}; + +TextRendererGLTest::TextRendererGLTest() { + addTests({&TextRendererGLTest::renderData, + &TextRendererGLTest::renderMesh, + &TextRendererGLTest::mutableText}); +} + +namespace { + +class TestLayouter: public Text::AbstractLayouter { + public: + explicit TestLayouter(Float size, std::size_t glyphCount): _size(size) { + _glyphCount = glyphCount; + } + + std::tuple renderGlyph(const Vector2& cursorPosition, UnsignedInt i) override { + return std::make_tuple( + Rectangle(cursorPosition, cursorPosition+Vector2(3.0f, 2.0f)*((i+1)*_size)), + Rectangle({i*6.0f, 0.0f}, {(i+1)*6.0f, 10.0f}), + (Vector2::xAxis((i+1)*3.0f)+Vector2(1.0f, -1.0f))*_size + ); + } + + private: + Float _size; +}; + +class TestFont: public Text::AbstractFont { + public: + Features doFeatures() const override { return Feature::OpenData; } + + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return 0; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + + AbstractLayouter* doLayout(const GlyphCache*, Float size, const std::string& text) override { + return new TestLayouter(size, text.size()); + } +}; + +} + +void TextRendererGLTest::renderData() { + TestFont font; + std::vector positions; + std::vector textureCoordinates; + std::vector indices; + Rectangle bounds; + std::tie(positions, textureCoordinates, indices, bounds) = Text::AbstractTextRenderer::render(&font, nullptr, 0.25f, "abc"); + + /* Three glyphs, three quads -> 12 vertices, 18 indices */ + CORRADE_COMPARE(positions.size(), 12); + CORRADE_COMPARE(textureCoordinates.size(), 12); + CORRADE_COMPARE(indices.size(), 18); + + /* Vertex positions and texture coordinates + 0---2 + | | + | | + | | + 1---3 */ + + /* Vertex positions + +---+ + +-+ | | + a |b| | c | + +-+ | | + +---+ */ + CORRADE_COMPARE(positions, (std::vector{ + {0.0f, 0.5f}, + {0.0f, 0.0f}, + {0.75f, 0.5f}, + {0.75f, 0.0f}, + + {1.0f, 0.75f}, + {1.0f, -0.25f}, + {2.5f, 0.75f}, + {2.5f, -0.25f}, + + {2.75f, 1.0f}, + {2.75f, -0.5f}, + {5.0f, 1.0f}, + {5.0f, -0.5f} + })); + + /* Texture coordinates + +-+ +-+ +-+ + |a| |b| |c| + +-+ +-+ +-+ */ + CORRADE_COMPARE(textureCoordinates, (std::vector{ + {0.0f, 10.0f}, + {0.0f, 0.0f}, + {6.0f, 10.0f}, + {6.0f, 0.0f}, + + { 6.0f, 10.0f}, + { 6.0f, 0.0f}, + {12.0f, 10.0f}, + {12.0f, 0.0f}, + + {12.0f, 10.0f}, + {12.0f, 0.0f}, + {18.0f, 10.0f}, + {18.0f, 0.0f} + })); + + /* Indices + 0---2 0---2 5 + | | | / /| + | | | / / | + | | |/ / | + 1---3 1 3---4 */ + CORRADE_COMPARE(indices, (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10 + })); + + /* Bounds */ + CORRADE_COMPARE(bounds, Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +void TextRendererGLTest::renderMesh() { + TestFont font; + Mesh mesh; + Buffer vertexBuffer, indexBuffer; + Rectangle bounds; + std::tie(mesh, bounds) = Text::TextRenderer3D::render(&font, nullptr, 0.25f, "abc", &vertexBuffer, &indexBuffer, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + + /* Vertex buffer contents */ + Containers::Array vertices = vertexBuffer.data(); + CORRADE_COMPARE(std::vector(vertices.begin(), vertices.end()), (std::vector{ + 0.0f, 0.5f, 0.0f, 10.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.75f, 0.5f, 6.0f, 10.0f, + 0.75f, 0.0f, 6.0f, 0.0f, + + 1.0f, 0.75f, 6.0f, 10.0f, + 1.0f, -0.25f, 6.0f, 0.0f, + 2.5f, 0.75f, 12.0f, 10.0f, + 2.5f, -0.25f, 12.0f, 0.0f, + + 2.75f, 1.0f, 12.0f, 10.0f, + 2.75f, -0.5f, 12.0f, 0.0f, + 5.0f, 1.0f, 18.0f, 10.0f, + 5.0f, -0.5f, 18.0f, 0.0f + })); + + Containers::Array indices = indexBuffer.data(); + CORRADE_COMPARE(std::vector(indices.begin(), indices.end()), (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10 + })); + + /* Bounds */ + CORRADE_COMPARE(bounds, Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +void TextRendererGLTest::mutableText() { + TestFont font; + Text::TextRenderer2D renderer(&font, nullptr, 0.25f); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(renderer.capacity(), 0); + CORRADE_COMPARE(renderer.rectangle(), Rectangle()); + + /* Reserve some capacity */ + renderer.reserve(4, Buffer::Usage::StaticDraw, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(renderer.capacity(), 4); + Containers::Array indices = renderer.indexBuffer()->data(); + CORRADE_COMPARE(std::vector(indices.begin(), indices.end()), (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10, + 12, 13, 14, 13, 15, 14 + })); + + /* Render text */ + renderer.render("abc"); + MAGNUM_VERIFY_NO_ERROR(); + Containers::Array vertices = renderer.vertexBuffer()->subData(0, 48); + CORRADE_COMPARE(std::vector(vertices.begin(), vertices.end()), (std::vector{ + 0.0f, 0.5f, 0.0f, 10.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.75f, 0.5f, 6.0f, 10.0f, + 0.75f, 0.0f, 6.0f, 0.0f, + + 1.0f, 0.75f, 6.0f, 10.0f, + 1.0f, -0.25f, 6.0f, 0.0f, + 2.5f, 0.75f, 12.0f, 10.0f, + 2.5f, -0.25f, 12.0f, 0.0f, + + 2.75f, 1.0f, 12.0f, 10.0f, + 2.75f, -0.5f, 12.0f, 0.0f, + 5.0f, 1.0f, 18.0f, 10.0f, + 5.0f, -0.5f, 18.0f, 0.0f + })); + + /* Updated bounds */ + CORRADE_COMPARE(renderer.rectangle(), Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::TextRendererGLTest) diff --git a/src/Text/TextRenderer.cpp b/src/Text/TextRenderer.cpp index 8ef22ea3a..e12ebc252 100644 --- a/src/Text/TextRenderer.cpp +++ b/src/Text/TextRenderer.cpp @@ -37,11 +37,11 @@ namespace { template void createIndices(void* output, const UnsignedInt glyphCount) { T* const out = reinterpret_cast(output); for(UnsignedInt i = 0; i != glyphCount; ++i) { - /* 0---2 2 - | / /| - | / / | - |/ / | - 1 1---3 */ + /* 0---2 0---2 5 + | | | / /| + | | | / / | + | | |/ / | + 1---3 1 3---4 */ const T vertex = i*4; const T pos = i*6; @@ -69,6 +69,9 @@ std::tuple, std::vector, std::vector, positions.reserve(vertexCount); texcoords.reserve(vertexCount); + /* Rendered rectangle */ + Rectangle rectangle; + /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -77,6 +80,12 @@ std::tuple, std::vector, std::vector, Vector2 advance; std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); + /* 0---2 + | | + | | + | | + 1---3 */ + positions.insert(positions.end(), { quadPosition.topLeft(), quadPosition.bottomLeft(), @@ -90,6 +99,10 @@ std::tuple, std::vector, std::vector, textureCoordinates.bottomRight() }); + /* Extend rectangle with current quad bounds */ + rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), quadPosition.bottomLeft()); + rectangle.topRight() = Math::max(rectangle.topRight(), quadPosition.topRight()); + /* Advance cursor position to next character */ cursorPosition += advance; } @@ -98,10 +111,6 @@ std::tuple, std::vector, std::vector, std::vector indices(layouter->glyphCount()*6); createIndices(indices.data(), layouter->glyphCount()); - /* Rendered rectangle */ - Rectangle rectangle; - if(layouter->glyphCount()) rectangle = {positions[1], positions[positions.size()-2]}; - delete layouter; return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle); } @@ -116,6 +125,9 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon std::vector vertices; vertices.reserve(vertexCount); + /* Rendered rectangle */ + Rectangle rectangle; + /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -131,6 +143,10 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon {quadPosition.bottomRight(), textureCoordinates.bottomRight()} }); + /* Extend rectangle with current quad bounds */ + rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), quadPosition.bottomLeft()); + rectangle.topRight() = Math::max(rectangle.topRight(), quadPosition.topRight()); + /* Advance cursor position to next character */ cursorPosition += advance; } @@ -159,10 +175,6 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon indexBuffer->setData(indicesSize, indices, usage); delete indices; - /* Rendered rectangle */ - Rectangle rectangle; - if(layouter->glyphCount()) rectangle = {vertices[1].position, vertices[vertices.size()-2].position}; - /* Configure mesh except for vertex buffer (depends on dimension count, done in subclass) */ Mesh mesh; @@ -185,7 +197,7 @@ template std::tuple TextRenderer TextRenderer::TextRenderer(AbstractFont* const font, const GlyphCache* const cache, const Float size): AbstractTextRenderer(font, cache, size) { /* Finalize mesh configuration */ - _mesh.addInterleavedVertexBuffer(&vertexBuffer, 0, + _mesh.addInterleavedVertexBuffer(&_vertexBuffer, 0, typename Shaders::AbstractVector::Position(Shaders::AbstractVector::Position::Components::Two), typename Shaders::AbstractVector::TextureCoordinates()); } @@ -214,7 +226,7 @@ void AbstractTextRenderer::reserve(const uint32_t glyphCount, const Buffer::Usag const UnsignedInt indexCount = glyphCount*6; /* Allocate vertex buffer, reset vertex count */ - vertexBuffer.setData(vertexCount*sizeof(Vertex), nullptr, vertexBufferUsage); + _vertexBuffer.setData(vertexCount*sizeof(Vertex), nullptr, vertexBufferUsage); _mesh.setVertexCount(0); /* Allocate index buffer, reset index count and reconfigure buffer binding */ @@ -230,28 +242,32 @@ void AbstractTextRenderer::reserve(const uint32_t glyphCount, const Buffer::Usag indexType = Mesh::IndexType::UnsignedInt; indicesSize = indexCount*sizeof(UnsignedInt); } - indexBuffer.setData(indicesSize, nullptr, indexBufferUsage); + _indexBuffer.setData(indicesSize, nullptr, indexBufferUsage); _mesh.setIndexCount(0) - ->setIndexBuffer(&indexBuffer, 0, indexType, 0, vertexCount); + ->setIndexBuffer(&_indexBuffer, 0, indexType, 0, vertexCount); /* Prefill index buffer */ - void* indices = indexBuffer.map(0, indicesSize, Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write); + void* indices = _indexBuffer.map(0, indicesSize, Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write); if(vertexCount < 255) createIndices(indices, glyphCount); else if(vertexCount < 65535) createIndices(indices, glyphCount); else createIndices(indices, glyphCount); - CORRADE_INTERNAL_ASSERT_OUTPUT(indexBuffer.unmap()); + CORRADE_INTERNAL_ASSERT_OUTPUT(_indexBuffer.unmap()); } void AbstractTextRenderer::render(const std::string& text) { AbstractLayouter* layouter = font->layout(cache, size, text); - CORRADE_ASSERT(layouter->glyphCount() <= _capacity, "Text::TextRenderer::render(): capacity" << _capacity << "too small to render" << layouter->glyphCount() << "glyphs", ); + CORRADE_ASSERT(layouter->glyphCount() <= _capacity, + "Text::TextRenderer::render(): capacity" << _capacity << "too small to render" << layouter->glyphCount() << "glyphs", ); + + /* Reset rendered rectangle */ + _rectangle = {}; /* Render all glyphs */ - Vertex* const vertices = static_cast(vertexBuffer.map(0, layouter->glyphCount()*4*sizeof(Vertex), + Vertex* const vertices = static_cast(_vertexBuffer.map(0, layouter->glyphCount()*4*sizeof(Vertex), Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write)); Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -260,10 +276,9 @@ void AbstractTextRenderer::render(const std::string& text) { Vector2 advance; std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); - if(i == 0) - _rectangle.bottomLeft() = quadPosition.bottomLeft(); - else if(i == layouter->glyphCount()-1) - _rectangle.topRight() = quadPosition.topRight(); + /* Extend rectangle with current quad bounds */ + _rectangle.bottomLeft() = Math::min(_rectangle.bottomLeft(), quadPosition.bottomLeft()); + _rectangle.topRight() = Math::max(_rectangle.topRight(), quadPosition.topRight()); const std::size_t vertex = i*4; vertices[vertex] = {quadPosition.topLeft(), textureCoordinates.topLeft()}; @@ -274,7 +289,7 @@ void AbstractTextRenderer::render(const std::string& text) { /* Advance cursor position to next character */ cursorPosition += advance; } - CORRADE_INTERNAL_ASSERT_OUTPUT(vertexBuffer.unmap()); + CORRADE_INTERNAL_ASSERT_OUTPUT(_vertexBuffer.unmap()); /* Update index count */ _mesh.setIndexCount(layouter->glyphCount()*6); diff --git a/src/Text/TextRenderer.h b/src/Text/TextRenderer.h index 4c1299a58..ee0ad3abe 100644 --- a/src/Text/TextRenderer.h +++ b/src/Text/TextRenderer.h @@ -82,7 +82,13 @@ class MAGNUM_TEXT_EXPORT AbstractTextRenderer { /** @brief Rectangle spanning the rendered text */ Rectangle rectangle() const { return _rectangle; } - /** @brief Text mesh */ + /** @brief Vertex buffer */ + Buffer* vertexBuffer() { return &_vertexBuffer; } + + /** @brief Index buffer */ + Buffer* indexBuffer() { return &_indexBuffer; } + + /** @brief Mesh */ Mesh* mesh() { return &_mesh; } /** @@ -120,7 +126,7 @@ class MAGNUM_TEXT_EXPORT AbstractTextRenderer { static std::tuple MAGNUM_LOCAL render(AbstractFont* font, const GlyphCache* cache, Float size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage); Mesh _mesh; - Buffer vertexBuffer, indexBuffer; + Buffer _vertexBuffer, _indexBuffer; private: AbstractFont* const font;