/* Copyright © 2010, 2011, 2012 Vladimír Vondruš This file is part of Magnum. Magnum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 only, as published by the Free Software Foundation. Magnum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License version 3 for more details. */ #include "TextRenderer.h" #include "Context.h" #include "Extensions.h" #include "Mesh.h" #include "Shaders/AbstractVectorShader.h" #include "Text/Font.h" namespace Magnum { namespace Text { 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 */ 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; } } struct Vertex { Vector2 position, texcoords; }; } std::tuple, std::vector, std::vector, Rectangle> AbstractTextRenderer::render(Font& font, Float size, const std::string& text) { TextLayouter layouter(font, size, text); const UnsignedInt vertexCount = layouter.glyphCount()*4; /* Output data */ std::vector positions, texcoords; positions.reserve(vertexCount); texcoords.reserve(vertexCount); /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt 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(), }); texcoords.insert(texcoords.end(), { textureCoordinates.topLeft(), textureCoordinates.bottomLeft(), textureCoordinates.topRight(), textureCoordinates.bottomRight() }); /* Advance cursor position to next character */ cursorPosition += advance; } /* Create indices */ 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]}; return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle); } std::tuple AbstractTextRenderer::render(Font& font, Float size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage) { TextLayouter layouter(font, size, text); const UnsignedInt vertexCount = layouter.glyphCount()*4; const UnsignedInt indexCount = layouter.glyphCount()*6; /* Vertex buffer */ std::vector vertices; vertices.reserve(vertexCount); /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt 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(), { {quadPosition.topLeft(), textureCoordinates.topLeft()}, {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()}, {quadPosition.topRight(), textureCoordinates.topRight()}, {quadPosition.bottomRight(), textureCoordinates.bottomRight()} }); /* 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(UnsignedByte); indices = new char[indicesSize]; createIndices(indices, layouter.glyphCount()); } else if(vertexCount < 65535) { indexType = Mesh::IndexType::UnsignedShort; indicesSize = indexCount*sizeof(UnsignedShort); indices = new char[indicesSize]; createIndices(indices, layouter.glyphCount()); } else { indexType = Mesh::IndexType::UnsignedInt; indicesSize = indexCount*sizeof(UnsignedInt); indices = new char[indicesSize]; createIndices(indices, layouter.glyphCount()); } 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; mesh.setPrimitive(Mesh::Primitive::Triangles) ->setIndexCount(indexCount) ->setIndexBuffer(indexBuffer, 0, indexType, 0, vertexCount); return std::make_tuple(std::move(mesh), rectangle); } template std::tuple TextRenderer::render(Font& font, Float size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage) { /* Finalize mesh configuration and return the result */ auto r = AbstractTextRenderer::render(font, size, text, vertexBuffer, indexBuffer, usage); Mesh& mesh = std::get<0>(r); mesh.addInterleavedVertexBuffer(vertexBuffer, 0, typename Shaders::AbstractVectorShader::Position( Shaders::AbstractVectorShader::Position::Components::Two), typename Shaders::AbstractVectorShader::TextureCoordinates()); return std::move(r); } AbstractTextRenderer::AbstractTextRenderer(Font& font, Float size): vertexBuffer(Buffer::Target::Array), indexBuffer(Buffer::Target::ElementArray), font(font), size(size), _capacity(0) { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::map_buffer_range); #else #ifdef MAGNUM_TARGET_GLES2 MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::map_buffer_range); #endif #endif /* Vertex buffer configuration depends on dimension count, done in subclass */ _mesh.setPrimitive(Mesh::Primitive::Triangles); } AbstractTextRenderer::~AbstractTextRenderer() {} template TextRenderer::TextRenderer(Font& font, const Float size): AbstractTextRenderer(font, size) { /* Finalize mesh configuration */ _mesh.addInterleavedVertexBuffer(&vertexBuffer, 0, typename Shaders::AbstractVectorShader::Position(Shaders::AbstractVectorShader::Position::Components::Two), typename Shaders::AbstractVectorShader::TextureCoordinates()); } void AbstractTextRenderer::reserve(const uint32_t glyphCount, const Buffer::Usage vertexBufferUsage, const Buffer::Usage indexBufferUsage) { _capacity = glyphCount; const UnsignedInt vertexCount = glyphCount*4; const UnsignedInt indexCount = glyphCount*6; /* Allocate vertex buffer, reset vertex count */ vertexBuffer.setData(vertexCount*sizeof(Vertex), nullptr, vertexBufferUsage); _mesh.setVertexCount(0); /* Allocate index buffer, reset index count and reconfigure buffer binding */ Mesh::IndexType indexType; std::size_t indicesSize; if(vertexCount < 255) { indexType = Mesh::IndexType::UnsignedByte; indicesSize = indexCount*sizeof(UnsignedByte); } else if(vertexCount < 65535) { indexType = Mesh::IndexType::UnsignedShort; indicesSize = indexCount*sizeof(UnsignedShort); } else { indexType = Mesh::IndexType::UnsignedInt; indicesSize = indexCount*sizeof(UnsignedInt); } indexBuffer.setData(indicesSize, nullptr, indexBufferUsage); _mesh.setIndexCount(0) ->setIndexBuffer(&indexBuffer, 0, indexType, 0, vertexCount); /* Prefill index buffer */ 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()); } void AbstractTextRenderer::render(const std::string& text) { TextLayouter layouter(font, size, text); CORRADE_ASSERT(layouter.glyphCount() <= _capacity, "Text::TextRenderer::render(): capacity" << _capacity << "too small to render" << layouter.glyphCount() << "glyphs", ); /* Render all glyphs */ 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) { /* Position of the texture in the resulting glyph, texture coordinates */ Rectangle quadPosition, textureCoordinates; 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(); const std::size_t vertex = i*4; vertices[vertex] = {quadPosition.topLeft(), textureCoordinates.topLeft()}; vertices[vertex+1] = {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()}; vertices[vertex+2] = {quadPosition.topRight(), textureCoordinates.topRight()}; vertices[vertex+3] = {quadPosition.bottomRight(), textureCoordinates.bottomRight()}; /* Advance cursor position to next character */ cursorPosition += advance; } CORRADE_INTERNAL_ASSERT_OUTPUT(vertexBuffer.unmap()); /* Update index count */ _mesh.setIndexCount(layouter.glyphCount()*6); } template class TextRenderer<2>; template class TextRenderer<3>; }}