From a2e21020aa52326162074fb13c5bc5316f946ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 3 Feb 2013 14:35:48 +0100 Subject: [PATCH] Text: reworked TextRenderer internals. * Common layouting code in separate non-templated class. * Direct creation of interleaved vertex array and compressed index buffer, saves some memory operations and removes MeshTools dependency. * Preparation for mutable TextRenderer implementation. --- src/Text/CMakeLists.txt | 2 +- src/Text/TextRenderer.cpp | 238 +++++++++++++++++++++++++------------- 2 files changed, 160 insertions(+), 80 deletions(-) diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index 2bdebfb92..26f254e3f 100644 --- a/src/Text/CMakeLists.txt +++ b/src/Text/CMakeLists.txt @@ -17,7 +17,7 @@ set(MagnumText_HEADERS add_library(MagnumText SHARED ${MagnumText_SRCS}) -target_link_libraries(MagnumText Magnum MagnumMeshTools MagnumTextureTools ${FREETYPE_LIBRARIES} ${HARFBUZZ_LIBRARIES}) +target_link_libraries(MagnumText Magnum MagnumTextureTools ${FREETYPE_LIBRARIES} ${HARFBUZZ_LIBRARIES}) install(TARGETS MagnumText DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) install(FILES ${MagnumText_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Text) diff --git a/src/Text/TextRenderer.cpp b/src/Text/TextRenderer.cpp index b81a0c6c8..e31d4fe44 100644 --- a/src/Text/TextRenderer.cpp +++ b/src/Text/TextRenderer.cpp @@ -20,9 +20,8 @@ #include "Math/Point2D.h" #include "Math/Point3D.h" +#include "Mesh.h" #include "Swizzle.h" -#include "MeshTools/CompressIndices.h" -#include "MeshTools/Interleave.h" #include "Shaders/AbstractTextShader.h" #include "Text/Font.h" @@ -30,9 +29,28 @@ namespace Magnum { namespace Text { namespace { -std::tuple, std::vector, std::vector, Rectangle> renderInternal(Font& font, GLfloat size, const std::string& text) { +class TextLayouter { + public: + TextLayouter(Font& font, const GLfloat size, const std::string& text); + + ~TextLayouter(); + + inline std::uint32_t glyphCount() { return _glyphCount; } + + std::tuple renderGlyph(const Vector2& cursorPosition, const std::uint32_t i); + + private: + const Font& font; + const GLfloat size; + hb_buffer_t* buffer; + hb_glyph_info_t* glyphInfo; + hb_glyph_position_t* glyphPositions; + std::uint32_t _glyphCount; +}; + +TextLayouter::TextLayouter(Font& font, const GLfloat size, const std::string& text): font(font), size(size) { /* Prepare HarfBuzz buffer */ - hb_buffer_t *buffer = hb_buffer_create(); + buffer = hb_buffer_create(); hb_buffer_set_unicode_funcs(buffer, hb_icu_get_unicode_funcs()); hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); hb_buffer_set_script(buffer, HB_SCRIPT_LATIN); @@ -42,58 +60,96 @@ std::tuple, std::vector, std::vector positions, texcoords; - std::vector indices; - positions.reserve(glyphCount*4); - texcoords.reserve(glyphCount*4); - indices.reserve(glyphCount*6); +TextLayouter::~TextLayouter() { + /* Destroy HarfBuzz buffer */ + hb_buffer_destroy(buffer); +} - /* Starting cursor position */ - Vector2 cursorPosition; +std::tuple TextLayouter::renderGlyph(const Vector2& cursorPosition, const std::uint32_t i) { + /* Position of the texture in the resulting glyph, texture coordinates */ + Rectangle texturePosition, textureCoordinates; + std::tie(texturePosition, textureCoordinates) = font[glyphInfo[i].codepoint]; - /* Create quads for all glyphs */ - for(std::uint32_t i = 0; i != glyphCount; ++i) { - /* Position of the texture in the resulting glyph, texture coordinates */ - Rectangle texturePosition, textureCoordinates; - std::tie(texturePosition, textureCoordinates) = font[glyphInfo[i].codepoint]; + /* Glyph offset and advance to next glyph in normalized coordinates */ + Vector2 offset = Vector2(glyphPositions[i].x_offset, + glyphPositions[i].y_offset)/(64*font.size()); + Vector2 advance = Vector2(glyphPositions[i].x_advance, + glyphPositions[i].y_advance)/(64*font.size()); - /* Glyph offset and advance to next glyph in normalized coordinates */ - Vector2 offset = Vector2(glyphPositions[i].x_offset, - glyphPositions[i].y_offset)/(64*font.size()); - Vector2 advance = Vector2(glyphPositions[i].x_advance, - glyphPositions[i].y_advance)/(64*font.size()); + /* Absolute quad position, composed from cursor position, glyph offset + and texture position, denormalized to requested text size */ + Rectangle quadPosition = Rectangle::fromSize( + (cursorPosition + offset + Vector2(texturePosition.left(), texturePosition.bottom()))*size, + texturePosition.size()*size); - /* Absolute quad position, composed from cursor position, glyph offset - and texture position, denormalized to requested text size */ - Rectangle quadPosition = Rectangle::fromSize( - (cursorPosition + offset + Vector2(texturePosition.left(), texturePosition.bottom()))*size, - texturePosition.size()*size); + return std::make_tuple(quadPosition, textureCoordinates, advance); +} +template void createIndices(void* output, const std::uint32_t glyphCount) { + T* const out = reinterpret_cast(output); + for(std::uint32_t i = 0; i != glyphCount; ++i) { /* 0---2 2 | / /| | / / | |/ / | 1 1---3 */ - /* Quad with texture coordinates */ - indices.insert(indices.end(), { - std::uint32_t(positions.size()), - std::uint32_t(positions.size()+1), - std::uint32_t(positions.size()+2), - std::uint32_t(positions.size()+1), - std::uint32_t(positions.size()+3), - std::uint32_t(positions.size()+2) - }); + const T vertex = i*4; + const T pos = i*6; + out[pos] = vertex; + out[pos+1] = vertex+1; + out[pos+2] = vertex+2; + out[pos+3] = vertex+1; + out[pos+4] = vertex+3; + out[pos+5] = vertex+2; + } +} + +template typename DimensionTraits::PointType point(const Vector2& vec); + +template<> inline Point2D point<2>(const Vector2& vec) { + return swizzle<'x', 'y', '1'>(vec); +} + +template<> inline Point3D point<3>(const Vector2& vec) { + return swizzle<'x', 'y', '0', '1'>(vec); +} + +template struct Vertex { + typename DimensionTraits::PointType position; + Vector2 texcoords; +}; + +} + +template std::tuple::PointType>, std::vector, std::vector, Rectangle> TextRenderer::render(Font& font, GLfloat size, const std::string& text) { + TextLayouter layouter(font, size, text); + + const std::uint32_t vertexCount = layouter.glyphCount()*4; + + /* Output data */ + std::vector::PointType> positions; + std::vector texcoords; + positions.reserve(vertexCount); + texcoords.reserve(vertexCount); + + /* Render all glyphs */ + Vector2 cursorPosition; + for(std::uint32_t i = 0; i != layouter.glyphCount(); ++i) { + /* Position of the texture in the resulting glyph, texture coordinates */ + Rectangle quadPosition, textureCoordinates; + Vector2 advance; + std::tie(quadPosition, textureCoordinates, advance) = layouter.renderGlyph(cursorPosition, i); + positions.insert(positions.end(), { - quadPosition.topLeft(), - quadPosition.bottomLeft(), - quadPosition.topRight(), - quadPosition.bottomRight(), + point(quadPosition.topLeft()), + point(quadPosition.bottomLeft()), + point(quadPosition.topRight()), + point(quadPosition.bottomRight()), }); texcoords.insert(texcoords.end(), { textureCoordinates.topLeft(), @@ -106,58 +162,82 @@ std::tuple, std::vector, std::vector indices(layouter.glyphCount()*6); + createIndices(indices.data(), layouter.glyphCount()); + /* Rendered rectangle */ Rectangle rectangle; - if(glyphCount) rectangle = {positions[1], positions[positions.size()-2]}; - - /* Destroy HarfBuzz buffer */ - hb_buffer_destroy(buffer); + if(layouter.glyphCount()) rectangle = {positions[1].xy(), positions[positions.size()-2].xy()}; return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle); } -template typename DimensionTraits::PointType point(const Vector2& vec); - -template<> inline Point2D point<2>(const Vector2& vec) { - return swizzle<'x', 'y', '1'>(vec); -} - -template<> inline Point3D point<3>(const Vector2& vec) { - return swizzle<'x', 'y', '0', '1'>(vec); -} - -} +template std::tuple TextRenderer::render(Font& font, GLfloat size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage) { + TextLayouter layouter(font, size, text); -template std::tuple::PointType>, std::vector, std::vector, Rectangle> TextRenderer::render(Font& font, GLfloat size, const std::string& text) { - std::vector positions, textureCoordinates; - std::vector indices; - Rectangle rectangle; - std::tie(positions, textureCoordinates, indices, rectangle) = renderInternal(font, size, text); + const std::uint32_t vertexCount = layouter.glyphCount()*4; + const std::uint32_t indexCount = layouter.glyphCount()*6; - /* Create PointXD from Vector2 */ - std::vector::PointType> positionsXD; - positionsXD.reserve(positions.size()); - for(const Vector2& position: positions) - positionsXD.push_back(point(position)); + /* Vertex buffer */ + std::vector> vertices; + vertices.reserve(vertexCount); - return std::make_tuple(std::move(positionsXD), std::move(textureCoordinates), std::move(indices), rectangle); -} + /* Render all glyphs */ + Vector2 cursorPosition; + for(std::uint32_t i = 0; i != layouter.glyphCount(); ++i) { + /* Position of the texture in the resulting glyph, texture coordinates */ + Rectangle quadPosition, textureCoordinates; + Vector2 advance; + std::tie(quadPosition, textureCoordinates, advance) = layouter.renderGlyph(cursorPosition, i); + + vertices.insert(vertices.end(), { + {point(quadPosition.topLeft()), textureCoordinates.topLeft()}, + {point(quadPosition.bottomLeft()), textureCoordinates.bottomLeft()}, + {point(quadPosition.topRight()), textureCoordinates.topRight()}, + {point(quadPosition.bottomRight()), textureCoordinates.bottomRight()} + }); -template std::tuple TextRenderer::render(Font& font, GLfloat size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage) { - Mesh mesh; + /* Advance cursor position to next character */ + cursorPosition += advance; + } + vertexBuffer->setData(vertices, usage); + + /* Fill index buffer */ + Mesh::IndexType indexType; + std::size_t indicesSize; + char* indices; + if(vertexCount < 255) { + indexType = Mesh::IndexType::UnsignedByte; + indicesSize = indexCount*sizeof(GLushort); + indices = new char[indicesSize]; + createIndices(indices, layouter.glyphCount()); + } else if(vertexCount < 65535) { + indexType = Mesh::IndexType::UnsignedShort; + indicesSize = indexCount*sizeof(GLushort); + indices = new char[indicesSize]; + createIndices(indices, layouter.glyphCount()); + } else { + indexType = Mesh::IndexType::UnsignedInt; + indicesSize = indexCount*sizeof(GLuint); + indices = new char[indicesSize]; + createIndices(indices, layouter.glyphCount()); + } + indexBuffer->setData(indicesSize, indices, usage); + delete indices; - std::vector::PointType> positions; - std::vector textureCoordinates; - std::vector indices; + /* Rendered rectangle */ Rectangle rectangle; - std::tie(positions, textureCoordinates, indices, rectangle) = render(font, size, text); + if(layouter.glyphCount()) rectangle = {vertices[1].position.xy(), vertices[vertices.size()-2].position.xy()}; - MeshTools::interleave(&mesh, vertexBuffer, usage, positions, textureCoordinates); - MeshTools::compressIndices(&mesh, indexBuffer, usage, indices); + /* Configure mesh */ + Mesh mesh; mesh.setPrimitive(Mesh::Primitive::Triangles) + ->setIndexCount(indexCount) ->addInterleavedVertexBuffer(vertexBuffer, 0, typename Shaders::AbstractTextShader::Position(), - typename Shaders::AbstractTextShader::TextureCoordinates()); + typename Shaders::AbstractTextShader::TextureCoordinates()) + ->setIndexBuffer(indexBuffer, 0, indexType, 0, vertexCount); return std::make_tuple(std::move(mesh), rectangle); }