diff --git a/doc/changelog.dox b/doc/changelog.dox index 0192722c1..0987c64c1 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1590,6 +1590,14 @@ See also: @ref TextureTools::DistanceFieldGL along with correspondingly renamed headers, to make room for implementations with other backends such as Vulkan +- The @cpp Text::AbstractRenderer @ce class and the + @cpp Text::Renderer2D @ce and @cpp Text::Renderer3D @ce typedefs are + deprecated in favor of @ref Text::Renderer and @ref Text::RendererGL + instead, which expose a superset of the functionality through a more + efficient interface. The new API doesn't distinguish between 2D and 3D + anymore as the original implementation had two-component vertex positions + in 3D as well. If you really need a third coordinate for transforming + individual glyphs, use a @ref Text-Renderer-allocators "custom Renderer vertex allocator". - @cpp Trade::AbstractMaterialData @ce as well as its containing header `Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new and more flexible @ref Trade::MaterialData. @@ -1991,11 +1999,13 @@ See also: - The @ref Text::AbstractGlyphCache::image() query now returns a @ref MutableImageView3D instead of an @ref Image2D in order to support incremental population and texture arrays -- The @cpp Text::Renderer @ce class template was renamed to - @ref Text::BasicRenderer in order to make room for a new, more flexible - implementation. The assumption is that most existing code uses it through - the @ref Text::Renderer2D and @ref Text::Renderer3D typedefs and thus it - shouldn't result in any breakage. +- The @cpp Text::Renderer @ce class template is now gone and its + functionality was merged to the now-deprecated @ref Text::AbstractRenderer + class. The assumption is that most existing code uses it through the + @ref Text::Renderer2D and @ref Text::Renderer3D typedefs and thus it + shouldn't result in any breakage, and the original implementation didn't + distinguish between 2D and 3D anyway, having two-component vertex positions + in 3D as well. - The @ref Text::Alignment left, right, top and middle values now work with the font metrics and cursor position instead of glyph bounding rectangles, as that's the more commonly expected behavior. The original behavior with diff --git a/doc/snippets/Text-gl.cpp b/doc/snippets/Text-gl.cpp index 18cfb364e..3e108637e 100644 --- a/doc/snippets/Text-gl.cpp +++ b/doc/snippets/Text-gl.cpp @@ -24,14 +24,13 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include #include "Magnum/PixelFormat.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Range.h" #include "Magnum/GL/MeshView.h" #include "Magnum/GL/Renderer.h" #include "Magnum/Shaders/VectorGL.h" @@ -41,6 +40,14 @@ #include "Magnum/Text/DistanceFieldGlyphCacheGL.h" #include "Magnum/Text/RendererGL.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include + +#include "Magnum/GL/Buffer.h" +#include "Magnum/GL/Mesh.h" +#endif + #define DOXYGEN_ELLIPSIS(...) __VA_ARGS__ using namespace Magnum; @@ -199,12 +206,14 @@ shader /* [Renderer-usage-blocks-draw] */ } +#ifdef MAGNUM_BUILD_DEPRECATED { /* -Wnonnull in GCC 11+ "helpfully" says "this is null" if I don't initialize the font pointer. I don't care, I just want you to check compilation errors, not more! */ PluginManager::Manager manager; -/* [BasicRenderer-usage1] */ +CORRADE_IGNORE_DEPRECATED_PUSH +/* [Renderer2D-usage1] */ /* Font instance, received from a plugin manager */ Containers::Pointer font = DOXYGEN_ELLIPSIS(manager.loadAndInstantiate("")); @@ -236,9 +245,11 @@ shader .setColor(0xffffff_rgbf) .bindVectorTexture(cache.texture()) .draw(mesh); -/* [BasicRenderer-usage1] */ +/* [Renderer2D-usage1] */ +CORRADE_IGNORE_DEPRECATED_POP -/* [BasicRenderer-usage2] */ +CORRADE_IGNORE_DEPRECATED_PUSH +/* [Renderer2D-usage2] */ /* Initialize the renderer and reserve memory for enough glyphs */ Text::Renderer2D renderer{*font, cache, 12.0f, Text::Alignment::LineCenter}; renderer.reserve(32, GL::BufferUsage::DynamicDraw, GL::BufferUsage::StaticDraw); @@ -251,6 +262,8 @@ shader.setTransformationProjectionMatrix(projectionMatrix) .setColor(0xffffff_rgbf) .bindVectorTexture(cache.texture()) .draw(renderer.mesh()); -/* [BasicRenderer-usage2] */ +/* [Renderer2D-usage2] */ +CORRADE_IGNORE_DEPRECATED_POP } +#endif } diff --git a/doc/snippets/Text.cpp b/doc/snippets/Text.cpp index 93e9ae9be..aacbcf411 100644 --- a/doc/snippets/Text.cpp +++ b/doc/snippets/Text.cpp @@ -49,6 +49,7 @@ #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Range.h" +#include "Magnum/Text/Alignment.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFontConverter.h" #include "Magnum/Text/AbstractGlyphCache.h" diff --git a/src/Magnum/Text/AbstractFont.h b/src/Magnum/Text/AbstractFont.h index 6ce2e653d..62a564e8b 100644 --- a/src/Magnum/Text/AbstractFont.h +++ b/src/Magnum/Text/AbstractFont.h @@ -641,8 +641,8 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * @ref AbstractShaper class instead. * * Note that the layouters support rendering of single-line text only. - * See @ref BasicRenderer "Renderer*D" class for more advanced text - * layouting. Expects that a font is opened. + * See @ref AbstractRenderer class for more advanced text layouting. + * Expects that a font is opened. * @see @ref fillGlyphCache(), @ref createGlyphCache() */ CORRADE_DEPRECATED("use createShaper() instead") Containers::Pointer layout(const AbstractGlyphCache& cache, Float size, Containers::StringView text); diff --git a/src/Magnum/Text/Renderer.cpp b/src/Magnum/Text/Renderer.cpp index 61f0312d9..1dad33a13 100644 --- a/src/Magnum/Text/Renderer.cpp +++ b/src/Magnum/Text/Renderer.cpp @@ -54,19 +54,6 @@ #include "Magnum/Text/Feature.h" #endif -#ifdef MAGNUM_TARGET_GL -#include -#include -#include -#include /** @todo remove once Renderer is STL-free */ -#include /** @todo remove once Renderer is STL-free */ - -#include "Magnum/GL/Context.h" -#include "Magnum/GL/Extensions.h" -#include "Magnum/GL/Mesh.h" -#include "Magnum/Shaders/GenericGL.h" -#endif - namespace Magnum { namespace Text { Debug& operator<<(Debug& debug, const RendererCoreFlag value) { @@ -1678,386 +1665,4 @@ Containers::Pair glyphRangeForBytes(const Containers:: out; } -#ifdef MAGNUM_TARGET_GL -namespace { - -struct Vertex { - Vector2 position, textureCoordinates; -}; - -std::tuple, 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 and assert in the high-level API already to - avoid confusion */ - CORRADE_ASSERT(cache.findFont(font), - "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 vertices; - vertices.reserve(text.size()*4); - - /* 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; - - /* Temp buffer so we don't allocate for each new line */ - std::string line; - line.reserve(text.size()); - - /* Create a shaper */ - /** @todo even with reusing a shaper this is all horrific, rework!! */ - Containers::Pointer shaper = font.createShaper(); - - /* Start/End alignment resolved based on what the shaper detects for the - first line. Not great, but can't do much better with this old limited - API. */ - /** @todo rework all this, again */ - Containers::Optional resolvedAlignment; - - /* Render each line separately and align it horizontally */ - std::size_t pos, prevPos = 0; - do { - /* Empty line, nothing to do (the rest is done below in while expression) */ - if((pos = text.find('\n', prevPos)) == prevPos) continue; - - /* Copy the line into the temp buffer */ - line.assign(text, prevPos, pos-prevPos); - - /* Shape the line */ - shaper->shape(line); - - /* 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 asserts when this - issue arises. */ - CORRADE_INTERNAL_ASSERT(vertices.size() + shaper->glyphCount()*4 <= vertices.capacity()); - vertices.resize(vertices.size() + shaper->glyphCount()*4); - - /* Retrieve glyph offsets and advances directly into the output array - to not have to allocate a temp buffer; the offsets then get - converted to absolute positions. The renderLineGlyphPositionsInto() - is aware of this and will make sure to read the input before writing - to it. Taking every fourth item as the positions are subsequently - in-place converted to quads by renderGlyphQuadsInto() below and - putting them just into a prefix would cause them to be overwritten - too early. */ - const Containers::StridedArrayView1D lineVertices = Containers::stridedArrayView(vertices).exceptPrefix(vertices.size() - shaper->glyphCount()*4); - const Containers::StridedArrayView1D glyphOffsetsPositions = lineVertices.slice(&Vertex::position).every(4); - const Containers::StridedArrayView1D glyphAdvances = lineVertices.slice(&Vertex::textureCoordinates).every(4); - shaper->glyphOffsetsAdvancesInto( - glyphOffsetsPositions, - glyphAdvances); - - Vector2 cursor = linePosition; - - /* Render line glyph positions, aliasing the offsets */ - const Range2D lineRectangle = renderLineGlyphPositionsInto( - font, - size, - /** @todo direction hardcoded here */ - LayoutDirection::HorizontalTopToBottom, - glyphOffsetsPositions, - glyphAdvances, - cursor, - glyphOffsetsPositions); - - /* Retrieve the glyph IDs directly into the output array, again to not - have to allocate a temp buffer. The place where IDs get stored is - where glyph advances were stored before and which were combined into - glyph positions, and ultimately this location is where texture - coordinates get written. Again the renderGlyphQuadsInto() is aware - of this and will make sure to read the IDs before writing the quads. - Again taking every fourth item as these are subsequently converted - to quads by the function and putting them just into a prefix would - cause them to be overwritten too early. */ - const Containers::StridedArrayView1D glyphIds = Containers::arrayCast(glyphAdvances); - shaper->glyphIdsInto(glyphIds); - - /* Create quads from the positions */ - const Range2D lineQuadRectangle = renderGlyphQuadsInto( - font, - size, - cache, - glyphOffsetsPositions, - glyphIds, - lineVertices.slice(&Vertex::position), - lineVertices.slice(&Vertex::textureCoordinates)); - - /* Resolve the alignment based on what the shaper detected (if - anything). Assume there are no font plugins that would produce - vertical shape direction by default. */ - /** @todo drop all this once the shaper instance is configurable from - outside */ - if(!resolvedAlignment) { - const ShapeDirection shapeDirection = shaper->direction(); - CORRADE_INTERNAL_ASSERT( - shapeDirection != ShapeDirection::TopToBottom && - shapeDirection != ShapeDirection::BottomToTop); - resolvedAlignment = alignmentForDirection(alignment, - /** @todo direction hardcoded here */ - LayoutDirection::HorizontalTopToBottom, - shapeDirection); - } - - /* Horizontally align the line, using either of the rectangles based on - which alignment is desired */ - const Range2D alignedLineRectangle = alignRenderedLine( - UnsignedByte(*resolvedAlignment) & Implementation::AlignmentGlyphBounds ? - lineQuadRectangle : lineRectangle, - /** @todo direction hardcoded here */ - LayoutDirection::HorizontalTopToBottom, - *resolvedAlignment, - lineVertices.slice(&Vertex::position)); - - /* Extend the rectangle with final line bounds */ - rectangle = Math::join(rectangle, alignedLineRectangle); - - /* Move to next line */ - } while(prevPos = pos+1, - linePosition -= lineAdvance, - pos != std::string::npos); - - const Range2D alignedRectangle = alignRenderedBlock( - rectangle, - /** @todo direction hardcoded here */ - LayoutDirection::HorizontalTopToBottom, - *resolvedAlignment, - Containers::stridedArrayView(vertices).slice(&Vertex::position)); - - return std::make_tuple(Utility::move(vertices), alignedRectangle); -} - -std::pair, MeshIndexType> renderIndicesInternal(const UnsignedInt glyphCount) { - const UnsignedInt vertexCount = glyphCount*4; - const UnsignedInt indexCount = glyphCount*6; - - Containers::Array indices; - MeshIndexType indexType; - if(vertexCount <= 256) { - indexType = MeshIndexType::UnsignedByte; - indices = Containers::Array{NoInit, indexCount*sizeof(UnsignedByte)}; - renderGlyphQuadIndicesInto(0, Containers::arrayCast(indices)); - } else if(vertexCount <= 65536) { - indexType = MeshIndexType::UnsignedShort; - indices = Containers::Array{NoInit, indexCount*sizeof(UnsignedShort)}; - renderGlyphQuadIndicesInto(0, Containers::arrayCast(indices)); - } else { - indexType = MeshIndexType::UnsignedInt; - indices = Containers::Array{NoInit, indexCount*sizeof(UnsignedInt)}; - renderGlyphQuadIndicesInto(0, Containers::arrayCast(indices)); - } - - return {Utility::move(indices), indexType}; -} - -std::tuple renderInternal(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage usage, Alignment alignment) { - /* Render vertices and upload them */ - std::vector vertices; - Range2D rectangle; - std::tie(vertices, rectangle) = renderVerticesInternal(font, cache, size, text, alignment); - vertexBuffer.setData(vertices, usage); - - const UnsignedInt glyphCount = vertices.size()/4; - const UnsignedInt indexCount = glyphCount*6; - - /* Render indices and upload them */ - Containers::Array indices; - MeshIndexType indexType; - std::tie(indices, indexType) = renderIndicesInternal(glyphCount); - indexBuffer.setData(indices, usage); - - /* Configure mesh except for vertex buffer (depends on dimension count, done - in subclass) */ - GL::Mesh mesh; - mesh.setPrimitive(MeshPrimitive::Triangles) - .setCount(indexCount) - .setIndexBuffer(indexBuffer, 0, indexType, 0, vertices.size()); - - return std::make_tuple(Utility::move(mesh), rectangle); -} - -} - -std::tuple, std::vector, std::vector, Range2D> AbstractRenderer::render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, Alignment alignment) { - /* Render vertices */ - std::vector vertices; - Range2D rectangle; - std::tie(vertices, rectangle) = renderVerticesInternal(font, cache, size, text, alignment); - - /* Deinterleave the vertices */ - std::vector positions, textureCoordinates; - positions.reserve(vertices.size()); - positions.reserve(textureCoordinates.size()); - for(const auto& v: vertices) { - positions.push_back(v.position); - textureCoordinates.push_back(v.textureCoordinates); - } - - /* Render indices */ - const UnsignedInt glyphCount = vertices.size()/4; - std::vector indices(glyphCount*6); - renderGlyphQuadIndicesInto(0, indices); - - return std::make_tuple(Utility::move(positions), Utility::move(textureCoordinates), Utility::move(indices), rectangle); -} - -template std::tuple BasicRenderer::render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage usage, Alignment alignment) { - /* Finalize mesh configuration and return the result */ - auto r = renderInternal(font, cache, size, text, vertexBuffer, indexBuffer, usage, alignment); - GL::Mesh& mesh = std::get<0>(r); - mesh.addVertexBuffer(vertexBuffer, 0, - typename Shaders::GenericGL::Position( - Shaders::GenericGL::Position::Components::Two), - typename Shaders::GenericGL::TextureCoordinates()); - return r; -} - -#if defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_TARGET_EMSCRIPTEN) -AbstractRenderer::BufferMapImplementation AbstractRenderer::bufferMapImplementation = &AbstractRenderer::bufferMapImplementationFull; -AbstractRenderer::BufferUnmapImplementation AbstractRenderer::bufferUnmapImplementation = &AbstractRenderer::bufferUnmapImplementationDefault; - -void* AbstractRenderer::bufferMapImplementationFull(GL::Buffer& buffer, GLsizeiptr) { - return buffer.map(GL::Buffer::MapAccess::WriteOnly); -} -#endif - -#if !defined(MAGNUM_TARGET_GLES2) || defined(CORRADE_TARGET_EMSCRIPTEN) -inline void* AbstractRenderer::bufferMapImplementation(GL::Buffer& buffer, GLsizeiptr length) -#else -void* AbstractRenderer::bufferMapImplementationRange(GL::Buffer& buffer, GLsizeiptr length) -#endif -{ - #ifndef CORRADE_TARGET_EMSCRIPTEN - return buffer.map(0, length, GL::Buffer::MapFlag::InvalidateBuffer|GL::Buffer::MapFlag::Write); - #else - static_cast(length); - return &buffer == &_indexBuffer ? _indexBufferData : _vertexBufferData; - #endif -} - -#if !defined(MAGNUM_TARGET_GLES2) || defined(CORRADE_TARGET_EMSCRIPTEN) -inline void AbstractRenderer::bufferUnmapImplementation(GL::Buffer& buffer) -#else -void AbstractRenderer::bufferUnmapImplementationDefault(GL::Buffer& buffer) -#endif -{ - #ifndef CORRADE_TARGET_EMSCRIPTEN - buffer.unmap(); - #else - buffer.setSubData(0, &buffer == &_indexBuffer ? _indexBufferData : _vertexBufferData); - #endif -} - -AbstractRenderer::AbstractRenderer(AbstractFont& font, const AbstractGlyphCache& cache, const Float size, const Alignment alignment): _vertexBuffer{GL::Buffer::TargetHint::Array}, _indexBuffer{GL::Buffer::TargetHint::ElementArray}, font(font), cache(cache), _fontSize{size}, _alignment(alignment), _capacity(0) { - #ifndef MAGNUM_TARGET_GLES - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::map_buffer_range); - #elif defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_TARGET_EMSCRIPTEN) - if(GL::Context::current().isExtensionSupported()) { - bufferMapImplementation = &AbstractRenderer::bufferMapImplementationRange; - } else { - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::OES::mapbuffer); - Warning() << "Text::Renderer:" << GL::Extensions::EXT::map_buffer_range::string() - << "is not supported, using inefficient" << GL::Extensions::OES::mapbuffer::string() - << "instead"; - } - #endif - - /* These two are done in renderVerticesInternal() as well, but it makes - sense to verify the same here already, not only once .render() is - called */ - CORRADE_ASSERT(cache.size().z() == 1, - "Text::AbstractRenderer: array glyph caches are not supported", ); - CORRADE_ASSERT(cache.findFont(font), - "Text::AbstractRenderer: font not found among" << cache.fontCount() << "fonts in passed glyph cache", ); - - /* Vertex buffer configuration depends on dimension count, done in subclass */ - _mesh.setPrimitive(MeshPrimitive::Triangles); -} - -AbstractRenderer::AbstractRenderer(AbstractRenderer&&) noexcept = default; - -AbstractRenderer::~AbstractRenderer() = default; - -template BasicRenderer::BasicRenderer(AbstractFont& font, const AbstractGlyphCache& cache, const Float size, const Alignment alignment): AbstractRenderer(font, cache, size, alignment) { - /* Finalize mesh configuration */ - _mesh.addVertexBuffer(_vertexBuffer, 0, - typename Shaders::GenericGL::Position( - Shaders::GenericGL::Position::Components::Two), - typename Shaders::GenericGL::TextureCoordinates()); -} - -void AbstractRenderer::reserve(const uint32_t glyphCount, const GL::BufferUsage vertexBufferUsage, const GL::BufferUsage indexBufferUsage) { - _capacity = glyphCount; - - const UnsignedInt vertexCount = glyphCount*4; - - /* Allocate vertex buffer, reset vertex count */ - _vertexBuffer.setData({nullptr, vertexCount*sizeof(Vertex)}, vertexBufferUsage); - #ifdef CORRADE_TARGET_EMSCRIPTEN - _vertexBufferData = Containers::Array(vertexCount*sizeof(Vertex)); - #endif - _mesh.setCount(0); - - /* Render indices */ - Containers::Array indexData; - MeshIndexType indexType; - std::tie(indexData, indexType) = renderIndicesInternal(glyphCount); - - /* Allocate index buffer, reset index count and reconfigure buffer binding */ - _indexBuffer.setData({nullptr, indexData.size()}, indexBufferUsage); - #ifdef CORRADE_TARGET_EMSCRIPTEN - _indexBufferData = Containers::Array(indexData.size()); - #endif - _mesh.setCount(0) - .setIndexBuffer(_indexBuffer, 0, indexType, 0, vertexCount); - - /* Prefill index buffer */ - char* const indices = static_cast(bufferMapImplementation(_indexBuffer, indexData.size())); - CORRADE_INTERNAL_ASSERT(indices); - /** @todo Emscripten: it can be done without this copying altogether */ - std::copy(indexData.begin(), indexData.end(), indices); - bufferUnmapImplementation(_indexBuffer); -} - -void AbstractRenderer::render(const std::string& text) { - /* Render vertex data */ - std::vector vertexData; - _rectangle = {}; - std::tie(vertexData, _rectangle) = renderVerticesInternal(font, cache, _fontSize, text, _alignment); - - const UnsignedInt glyphCount = vertexData.size()/4; - const UnsignedInt vertexCount = glyphCount*4; - const UnsignedInt indexCount = glyphCount*6; - - CORRADE_ASSERT(glyphCount <= _capacity, - "Text::Renderer::render(): capacity" << _capacity << "too small to render" << glyphCount << "glyphs", ); - - /* Interleave the data into mapped buffer, if there are any glyphs */ - if(vertexCount) { - Containers::ArrayView vertices(static_cast(bufferMapImplementation(_vertexBuffer, - vertexCount*sizeof(Vertex))), vertexCount); - CORRADE_INTERNAL_ASSERT_OUTPUT(vertices); - std::copy(vertexData.begin(), vertexData.end(), vertices.begin()); - bufferUnmapImplementation(_vertexBuffer); - } - - /* Update index count */ - _mesh.setCount(indexCount); -} - -template class MAGNUM_TEXT_EXPORT BasicRenderer<2>; -template class MAGNUM_TEXT_EXPORT BasicRenderer<3>; -#endif - }} diff --git a/src/Magnum/Text/Renderer.h b/src/Magnum/Text/Renderer.h index 7216d9661..6d160a4bb 100644 --- a/src/Magnum/Text/Renderer.h +++ b/src/Magnum/Text/Renderer.h @@ -37,20 +37,14 @@ #include "Magnum/Text/Text.h" #include "Magnum/Text/visibility.h" -#ifdef MAGNUM_TARGET_GL +#if defined(MAGNUM_TARGET_GL) && defined(MAGNUM_BUILD_DEPRECATED) #include #include #include -#include "Magnum/DimensionTraits.h" #include "Magnum/Math/Range.h" -#include "Magnum/GL/Buffer.h" -#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/GL.h" #include "Magnum/Text/Alignment.h" - -#ifdef CORRADE_TARGET_EMSCRIPTEN -#include -#endif #endif namespace Magnum { namespace Text { @@ -1737,20 +1731,51 @@ shaper features used. */ MAGNUM_TEXT_EXPORT Containers::Pair glyphRangeForBytes(const Containers::StridedArrayView1D& clusters, UnsignedInt begin, UnsignedInt end); -#ifdef MAGNUM_TARGET_GL +/* As the deprecated AbstractRenderer relies on GL functionality, it'd be + ideally put into RendererGL.h, but that'd be a breaking change as existing + code expects it in Renderer.h. OTOH, the implementation is in RendererGL.cpp + to avoid including GL headers and nasty STL things in Renderer.cpp as + well. */ +#if defined(MAGNUM_TARGET_GL) && defined(MAGNUM_BUILD_DEPRECATED) /** -@brief Base for OpenGL text renderers +@brief OpenGL text renderer +@m_deprecated_since_latest Use @ref Renderer or @ref RendererGL instead, which + expose a superset of the functionality through a more efficient interface. + It doesn't distinguish between 2D and 3D anymore as the original + implementation had two-component vertex positions in 3D as well. If you + really need a third coordinate for transforming individual glyphs, use a + @ref Text-Renderer-allocators "custom Renderer vertex allocator". + +Lays out the text into mesh using given font. Use of ligatures, kerning etc. +depends on features supported by particular font and its layouter. + +@section Text-BasicRenderer-usage Usage + +Immutable text (e.g. menu items, credits) can be simply rendered using static +methods, returning result either as data arrays or as fully configured mesh. +The text can be then drawn as usual by configuring the shader and drawing the +mesh: + +@snippet Text-gl.cpp Renderer2D-usage1 + +See @ref render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, Alignment) +and @ref render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, GL::Buffer&, GL::Buffer&, GL::BufferUsage, Alignment) +for more information. -Not meant to be used directly, see the @ref BasicRenderer class for more -information. +While this method is sufficient for one-shot rendering of static texts, for +mutable texts (e.g. FPS counters, chat messages) there is another approach +that doesn't recreate everything on each text change: + +@snippet Text-gl.cpp Renderer2D-usage2 @note This class is available only if Magnum is compiled with @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features for more information. -@see @ref Renderer2D, @ref Renderer3D +@see @ref Renderer2D, @ref Renderer3D, @ref AbstractFont, + @ref Shaders::VectorGL, @ref Shaders::DistanceFieldVectorGL */ -class MAGNUM_TEXT_EXPORT AbstractRenderer { +class CORRADE_DEPRECATED("use Renderer or RendererGL instead") MAGNUM_TEXT_EXPORT AbstractRenderer { public: /** * @brief Render text @@ -1766,26 +1791,73 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer { */ static std::tuple, std::vector, std::vector, Range2D> render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, Alignment alignment = Alignment::LineLeft); + /** + * @brief Render text + * @param font Font + * @param cache Glyph cache + * @param size Font size + * @param text Text to render + * @param vertexBuffer Buffer where to store vertices + * @param indexBuffer Buffer where to store indices + * @param usage Ignored, provided just for backward + * compatibility + * @param alignment Text alignment + * + * 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 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); + + /** + * @brief Constructor + * @param font Font + * @param cache Glyph cache + * @param size Font size + * @param alignment Text alignment + * + * Expects that @p font is present in @p cache and that @p cache isn't + * an array. + */ + explicit AbstractRenderer(AbstractFont& font, const AbstractGlyphCache& cache, Float size, Alignment alignment = Alignment::LineLeft); + explicit AbstractRenderer(AbstractFont&, AbstractGlyphCache&&, Float, Alignment alignment = Alignment::LineLeft) = delete; /**< @overload */ + /** @brief Copying is not allowed */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC 4.8 warns due to the argument */ AbstractRenderer(AbstractRenderer&) = delete; + CORRADE_IGNORE_DEPRECATED_POP - /** @brief Move constructor */ + /** + * @brief Move constructor + * + * Performs a destructive move, i.e. the original object isn't usable + * afterwards anymore. + */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC 4.8 warns due to the argument */ AbstractRenderer(AbstractRenderer&&) noexcept; + CORRADE_IGNORE_DEPRECATED_POP + + ~AbstractRenderer(); /** @brief Copying is not allowed */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC 4.8 warns due to the argument */ AbstractRenderer& operator=(AbstractRenderer&) = delete; + CORRADE_IGNORE_DEPRECATED_POP /** @brief Move assignment is not allowed */ /* Because it contains reference members. Not going to fix this, just pinning down existing behavior. */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC 4.8 warns due to the argument */ AbstractRenderer& operator=(AbstractRenderer&&) = delete; + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Capacity for rendered glyphs * * @see @ref reserve() */ - UnsignedInt capacity() const { return _capacity; } + UnsignedInt capacity() const; /** * @brief Font size in points @@ -1797,22 +1869,21 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer { Range2D rectangle() const { return _rectangle; } /** @brief Vertex buffer */ - GL::Buffer& vertexBuffer() { return _vertexBuffer; } + GL::Buffer& vertexBuffer(); /** @brief Index buffer */ - GL::Buffer& indexBuffer() { return _indexBuffer; } + GL::Buffer& indexBuffer(); /** @brief Mesh */ - GL::Mesh& mesh() { return _mesh; } + GL::Mesh& mesh(); /** * @brief Reserve capacity for rendered glyphs * * Reallocates memory in buffers to hold @p glyphCount glyphs and - * prefills index buffer. Consider using appropriate @p vertexBufferUsage - * if the text will be changed frequently. Index buffer is changed - * only by calling this function, thus @p indexBufferUsage generally - * doesn't need to be so dynamic if the capacity won't be changed much. + * prefills index buffer. The @p vertexBufferUsage and + * @p indexBufferUsage parameters are ignored and provided just for + * backward compatibility. * * Initially zero capacity is reserved. * @see @ref capacity() @@ -1827,157 +1898,52 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer { * available through @ref rectangle(). * * Initially no text is rendered. - * @attention The capacity must be large enough to contain all glyphs, - * see @ref reserve() for more information. */ void render(const std::string& text); - #ifndef DOXYGEN_GENERATING_OUTPUT - protected: - #else - private: - #endif - explicit MAGNUM_TEXT_LOCAL AbstractRenderer(AbstractFont& font, const AbstractGlyphCache& cache, Float size, Alignment alignment); - - ~AbstractRenderer(); - - GL::Mesh _mesh; - GL::Buffer _vertexBuffer, _indexBuffer; - #ifdef CORRADE_TARGET_EMSCRIPTEN - Containers::Array _vertexBufferData, _indexBufferData; - #endif - private: - AbstractFont& font; - const AbstractGlyphCache& cache; + /* Cannot be a base because RendererGL is defined in another header, + and making a cyclic dependency between the two for *all* users is + worse than one extra allocation for just some */ + Containers::Pointer _renderer; + AbstractFont& _font; Float _fontSize; - Alignment _alignment; - UnsignedInt _capacity; Range2D _rectangle; - - #if defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_TARGET_EMSCRIPTEN) - typedef void*(*BufferMapImplementation)(GL::Buffer&, GLsizeiptr); - static MAGNUM_TEXT_LOCAL void* bufferMapImplementationFull(GL::Buffer& buffer, GLsizeiptr length); - static MAGNUM_TEXT_LOCAL void* bufferMapImplementationRange(GL::Buffer& buffer, GLsizeiptr length); - static BufferMapImplementation bufferMapImplementation; - #else - #ifndef CORRADE_TARGET_EMSCRIPTEN - static - #else - MAGNUM_TEXT_LOCAL - #endif - void* bufferMapImplementation(GL::Buffer& buffer, GLsizeiptr length); - #endif - - #if defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_TARGET_EMSCRIPTEN) - typedef void(*BufferUnmapImplementation)(GL::Buffer&); - static MAGNUM_TEXT_LOCAL void bufferUnmapImplementationDefault(GL::Buffer& buffer); - static MAGNUM_TEXT_LOCAL BufferUnmapImplementation bufferUnmapImplementation; - #else - #ifndef CORRADE_TARGET_EMSCRIPTEN - static - #else - MAGNUM_TEXT_LOCAL - #endif - void bufferUnmapImplementation(GL::Buffer& buffer); - #endif -}; - -/** -@brief OpenGL text renderer - -Lays out the text into mesh using given font. Use of ligatures, kerning etc. -depends on features supported by particular font and its layouter. - -@section Text-BasicRenderer-usage Usage - -Immutable text (e.g. menu items, credits) can be simply rendered using static -methods, returning result either as data arrays or as fully configured mesh. -The text can be then drawn as usual by configuring the shader and drawing the -mesh: - -@snippet Text-gl.cpp BasicRenderer-usage1 - -See @ref render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, Alignment) -and @ref render(AbstractFont&, const AbstractGlyphCache&, Float, const std::string&, GL::Buffer&, GL::Buffer&, GL::BufferUsage, Alignment) -for more information. - -While this method is sufficient for one-shot rendering of static texts, for -mutable texts (e.g. FPS counters, chat messages) there is another approach -that doesn't recreate everything on each text change: - -@snippet Text-gl.cpp BasicRenderer-usage2 - -@section Text-BasicRenderer-required-opengl-functionality Required OpenGL functionality - -Mutable text rendering requires @gl_extension{ARB,map_buffer_range} on desktop -OpenGL (also part of OpenGL ES 3.0). If @gl_extension{EXT,map_buffer_range} is not -available in ES 2.0, at least @gl_extension{OES,mapbuffer} must be supported for -asynchronous buffer updates. There is no similar extension in WebGL, thus plain -(and slow) buffer updates are used there. - -@note This class is available only if Magnum is compiled with - @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features - for more information. - -@see @ref Renderer2D, @ref Renderer3D, @ref AbstractFont, - @ref Shaders::VectorGL, @ref Shaders::DistanceFieldVectorGL -*/ -template class MAGNUM_TEXT_EXPORT BasicRenderer: public AbstractRenderer { - public: - /** - * @brief Render text - * @param font Font - * @param cache Glyph cache - * @param size Font size - * @param text Text to render - * @param vertexBuffer Buffer where to store vertices - * @param indexBuffer Buffer where to store indices - * @param usage Usage of vertex and index buffer - * @param alignment Text alignment - * - * 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 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); - - /** - * @brief Constructor - * @param font Font - * @param cache Glyph cache - * @param size Font size - * @param alignment Text alignment - * - * Expects that @p font is present in @p cache and that @p cache isn't - * an array. - */ - explicit BasicRenderer(AbstractFont& font, const AbstractGlyphCache& cache, Float size, Alignment alignment = Alignment::LineLeft); - BasicRenderer(AbstractFont&, AbstractGlyphCache&&, Float, Alignment alignment = Alignment::LineLeft) = delete; /**< @overload */ - - #ifndef DOXYGEN_GENERATING_OUTPUT - using AbstractRenderer::render; - #endif }; /** @brief Two-dimensional text renderer +@m_deprecated_since_latest Use @ref Renderer or @ref RendererGL instead, which + expose a superset of the functionality through a more efficient interface. + It doesn't distinguish between 2D and 3D anymore as the original + implementation had two-component vertex positions in 3D as well. If you + really need a third coordinate for transforming individual glyphs, use a + @ref Text-Renderer-allocators "custom Renderer vertex allocator". @note This class is available only if Magnum is compiled with @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features for more information. */ -typedef BasicRenderer<2> Renderer2D; +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated APIs using deprecated APIs */ +typedef CORRADE_DEPRECATED("use Renderer or RendererGL instead") AbstractRenderer Renderer2D; +CORRADE_IGNORE_DEPRECATED_POP /** @brief Three-dimensional text renderer +@m_deprecated_since_latest Use @ref Renderer or @ref RendererGL instead, which + expose a superset of the functionality through a more efficient interface. + It doesn't distinguish between 2D and 3D anymore as the original + implementation had two-component vertex positions in 3D as well. If you + really need a third coordinate for transforming individual glyphs, use a + @ref Text-Renderer-allocators "custom Renderer vertex allocator". @note This class is available only if Magnum is compiled with @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features for more information. */ -typedef BasicRenderer<3> Renderer3D; +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated APIs using deprecated APIs */ +typedef CORRADE_DEPRECATED("use Renderer or RendererGL instead") AbstractRenderer Renderer3D; +CORRADE_IGNORE_DEPRECATED_POP #endif }} diff --git a/src/Magnum/Text/RendererGL.cpp b/src/Magnum/Text/RendererGL.cpp index f1c4b5bce..355c4b6b3 100644 --- a/src/Magnum/Text/RendererGL.cpp +++ b/src/Magnum/Text/RendererGL.cpp @@ -29,6 +29,8 @@ #include #include +#include "Magnum/GL/Buffer.h" +#include "Magnum/GL/Mesh.h" #include "Magnum/Shaders/GenericGL.h" /* no link-time dependency here */ #include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/Implementation/rendererState.h" @@ -45,6 +47,18 @@ #include "Magnum/Text/Feature.h" #endif +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include +#include +#include +#include +#include + +#include "Magnum/Text/AbstractFont.h" +#include "Magnum/Text/AbstractShaper.h" +#endif + namespace Magnum { namespace Text { Debug& operator<<(Debug& debug, const RendererGLFlag value) { @@ -303,4 +317,135 @@ Containers::Pair RendererGL::render(AbstractShaper& shaper, return render(shaper, size, text, Containers::arrayView(features)); } +/* The deprecated AbstractRenderer is defined in Renderer.h but the + implementation is here to avoid including GL headers in Renderer.cpp as + well. Additionally, since I need to include the nasty STL stuff for the GL + implementation here anyway, the non-GL part is put here too, again to not + have `#include ` and such in more places than strictly necessary. */ +#if defined(MAGNUM_TARGET_GL) && defined(MAGNUM_BUILD_DEPRECATED) +std::tuple, std::vector, std::vector, Range2D> AbstractRenderer::render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, 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 new Renderer API. + Shouldn't get triggered by existing code that uses the old Renderer2D/3D + API with 2D caches. */ + CORRADE_ASSERT(cache.size().z() == 1, + "Text::AbstractRenderer::render(): array glyph caches are not supported", {}); + + Renderer renderer{cache}; + renderer + .setIndexType(MeshIndexType::UnsignedInt) + .setAlignment(alignment) + /* Yes, this allocates a shaper every time. The old implementation, + when ported to the new AbstractShaper, did so as well, so this + doesn't make it any worse. */ + .add(*font.createShaper(), size, text); + + Range2D rectangle = renderer.render().first(); + + /* 🤮 */ + std::vector indices(renderer.glyphCount()*6); + std::vector positions(renderer.glyphCount()*4); + std::vector textureCoordinates(renderer.glyphCount()*4); + Utility::copy(renderer.indices(), indices); + Utility::copy(renderer.vertexPositions(), positions); + Utility::copy(renderer.vertexTextureCoordinates(), textureCoordinates); + + return std::make_tuple(Utility::move(positions), Utility::move(textureCoordinates), Utility::move(indices), rectangle); +} + +std::tuple AbstractRenderer::render(AbstractFont& font, const AbstractGlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage, 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 new Renderer API. + Shouldn't get triggered by existing code that uses the old Renderer2D/3D + API with 2D caches. */ + CORRADE_ASSERT(cache.size().z() == 1, + "Text::AbstractRenderer::render(): array glyph caches are not supported", (std::tuple{})); + + RendererGL renderer{cache}; + renderer + /* The old implementation defaulted to 8-bit indices while the new uses + 16-bit, preserve the old behavior */ + .setIndexType(MeshIndexType::UnsignedByte) + .setAlignment(alignment) + .add(*font.createShaper(), size, text); + + Range2D rectangle = renderer.render().first(); + + RendererGL::State& state = static_cast(*renderer._state); + GL::Mesh mesh = Utility::move(state.mesh); + vertexBuffer = Utility::move(state.vertices); + indexBuffer = Utility::move(state.indices); + + return std::make_tuple(Utility::move(mesh), rectangle); +} + +AbstractRenderer::AbstractRenderer(AbstractFont& font, const AbstractGlyphCache& cache, const Float size, const Alignment alignment): _font(font), _fontSize{size} { + /* 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 new Renderer API. + Shouldn't get triggered by existing code that uses the old Renderer2D/3D + API with 2D caches. */ + CORRADE_ASSERT(cache.size().z() == 1, + "Text::AbstractRenderer: array glyph caches are not supported", ); + /* Without this, the assert would fire only once .render() is called. The + root cause is in the constructor call, so fire it here already. */ + CORRADE_ASSERT(cache.findFont(font), + "Text::AbstractRenderer: font not found among" << cache.fontCount() << "fonts in passed glyph cache", ); + + /* Construct the renderer only after the above asserts, so an assertion in + RendererGL about array glyph caches not being supported on ES2 doesn't + fire before ours */ + _renderer.emplace(cache); + + (*_renderer) + .setAlignment(alignment) + /* The old implementation defaulted to 8-bit indices while the new uses + 16-bit, preserve the old behavior */ + .setIndexType(MeshIndexType::UnsignedByte); +} + +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated APIs using deprecated APIs */ +AbstractRenderer::AbstractRenderer(AbstractRenderer&&) noexcept = default; +CORRADE_IGNORE_DEPRECATED_POP + +AbstractRenderer::~AbstractRenderer() = default; + +UnsignedInt AbstractRenderer::capacity() const { + return _renderer->glyphCapacity(); +} + +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated API *implementations*, but only sometimes! */ +GL::Buffer& AbstractRenderer::indexBuffer() { + return static_cast(*_renderer->_state).indices; +} +CORRADE_IGNORE_DEPRECATED_POP + +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated API *implementations*, but only sometimes! */ +GL::Buffer& AbstractRenderer::vertexBuffer() { + return static_cast(*_renderer->_state).vertices; +} +CORRADE_IGNORE_DEPRECATED_POP + +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated API *implementations*, but only sometimes! */ +GL::Mesh& AbstractRenderer::mesh() { + return _renderer->mesh(); +} +CORRADE_IGNORE_DEPRECATED_POP + +void AbstractRenderer::reserve(const UnsignedInt glyphCount, GL::BufferUsage, GL::BufferUsage) { + _renderer->reserve(glyphCount, 0); +} + +void AbstractRenderer::render(const std::string& text) { + _rectangle = (*_renderer) + .clear() + /* Yes, this allocates a shaper every time. The old implementation, + when ported to the new AbstractShaper, did so as well, so this + doesn't make it any worse. */ + .render(*_font.createShaper(), _fontSize, text).first(); +} +#endif + }} diff --git a/src/Magnum/Text/RendererGL.h b/src/Magnum/Text/RendererGL.h index b57622be9..bf2bf947e 100644 --- a/src/Magnum/Text/RendererGL.h +++ b/src/Magnum/Text/RendererGL.h @@ -34,6 +34,7 @@ #include "Magnum/configure.h" #ifdef MAGNUM_TARGET_GL +#include "Magnum/GL/GL.h" #include "Magnum/Text/Renderer.h" namespace Magnum { namespace Text { @@ -240,6 +241,13 @@ class MAGNUM_TEXT_EXPORT RendererGL: public Renderer { private: struct State; + + /* Needs to extract the GL::Buffer instances from inside */ + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + friend AbstractRenderer; + CORRADE_IGNORE_DEPRECATED_POP + #endif }; }} diff --git a/src/Magnum/Text/Test/RendererGLTest.cpp b/src/Magnum/Text/Test/RendererGLTest.cpp index bff3f9a6c..08cebc38c 100644 --- a/src/Magnum/Text/Test/RendererGLTest.cpp +++ b/src/Magnum/Text/Test/RendererGLTest.cpp @@ -24,16 +24,12 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include #include #include #include #include #include #include -#include #include #include "Magnum/Image.h" @@ -42,20 +38,31 @@ #include "Magnum/PixelFormat.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Range.h" #include "Magnum/DebugTools/CompareImage.h" -#include "Magnum/GL/Context.h" -#include "Magnum/GL/Extensions.h" #include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" #include "Magnum/GL/OpenGLTester.h" #include "Magnum/GL/Renderbuffer.h" #include "Magnum/GL/RenderbufferFormat.h" #include "Magnum/Shaders/VectorGL.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractShaper.h" +#include "Magnum/Text/Alignment.h" #include "Magnum/Text/GlyphCacheGL.h" #include "Magnum/Text/RendererGL.h" #include "Magnum/Trade/AbstractImporter.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include +#include +#include + +#include "Magnum/GL/Context.h" +#include "Magnum/GL/Extensions.h" +#endif + #include "configure.h" namespace Magnum { namespace Text { namespace Test { namespace { @@ -78,16 +85,18 @@ struct RendererGLTest: GL::OpenGLTester { void renderClearReset(); void renderIndexTypeChanged(); - template void constructND(); - template void constructNDCopy(); - template void constructNDMove(); + #ifdef MAGNUM_BUILD_DEPRECATED + void deprecatedConstruct(); + void deprecatedConstructCopy(); + void deprecatedConstructMove(); - void renderMesh(); - void renderMeshIndexType(); - void mutableText(); + void deprecatedRenderMesh(); + void deprecatedRenderMeshIndexType(); + void deprecatedMutableText(); - void arrayGlyphCache(); - void fontNotFoundInCache(); + void deprecatedArrayGlyphCache(); + void deprecatedFontNotFoundInCache(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -225,19 +234,18 @@ RendererGLTest::RendererGLTest() { &RendererGLTest::renderSetup, &RendererGLTest::renderTeardown); - addTests({&RendererGLTest::constructND<2>, - &RendererGLTest::constructND<3>, - &RendererGLTest::constructNDCopy<2>, - &RendererGLTest::constructNDCopy<3>, - &RendererGLTest::constructNDMove<2>, - &RendererGLTest::constructNDMove<3>, + #ifdef MAGNUM_BUILD_DEPRECATED + addTests({&RendererGLTest::deprecatedConstruct, + &RendererGLTest::deprecatedConstructCopy, + &RendererGLTest::deprecatedConstructMove, - &RendererGLTest::renderMesh, - &RendererGLTest::renderMeshIndexType, - &RendererGLTest::mutableText, + &RendererGLTest::deprecatedRenderMesh, + &RendererGLTest::deprecatedRenderMeshIndexType, + &RendererGLTest::deprecatedMutableText, - &RendererGLTest::arrayGlyphCache, - &RendererGLTest::fontNotFoundInCache}); + &RendererGLTest::deprecatedArrayGlyphCache, + &RendererGLTest::deprecatedFontNotFoundInCache}); + #endif /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ @@ -840,11 +848,12 @@ void RendererGLTest::renderIndexTypeChanged() { (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); } +#ifdef MAGNUM_BUILD_DEPRECATED struct TestShaper: AbstractShaper { using AbstractShaper::AbstractShaper; - UnsignedInt doShape(Containers::StringView text, UnsignedInt, UnsignedInt, Containers::ArrayView) override { - return text.size(); + UnsignedInt doShape(Containers::StringView, UnsignedInt begin, UnsignedInt end, Containers::ArrayView) override { + return end - begin; } void doGlyphIdsInto(const Containers::StridedArrayView1D& ids) const override { @@ -914,9 +923,7 @@ GlyphCacheGL testGlyphCache(AbstractFont& font) { return cache; } -template void RendererGLTest::constructND() { - setTestCaseTemplateName(Utility::format("{}", dimensions)); - +void RendererGLTest::deprecatedConstruct() { struct: AbstractGlyphCache { using AbstractGlyphCache::AbstractGlyphCache; @@ -927,12 +934,14 @@ template void RendererGLTest::constructND() { font.openFile({}, 0.5f); glyphCache.addFont(3, &font); - BasicRenderer a{font, glyphCache, 3.0f}; + CORRADE_IGNORE_DEPRECATED_PUSH + AbstractRenderer a{font, glyphCache, 3.0f}; CORRADE_COMPARE(a.capacity(), 0); CORRADE_COMPARE(a.fontSize(), 3.0f); CORRADE_COMPARE(a.rectangle(), Range2D{}); CORRADE_VERIFY(a.indexBuffer().id()); CORRADE_VERIFY(a.vertexBuffer().id()); + CORRADE_IGNORE_DEPRECATED_POP { #ifndef MAGNUM_TARGET_GLES CORRADE_EXPECT_FAIL_IF(!GL::Context::current().isExtensionSupported(), @@ -941,20 +950,20 @@ template void RendererGLTest::constructND() { CORRADE_EXPECT_FAIL_IF(!GL::Context::current().isExtensionSupported(), "There's no way to know if the mesh was initialized without" << GL::Extensions::OES::vertex_array_object::string() << Debug::nospace << "."); #endif + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_VERIFY(a.mesh().id()); + CORRADE_IGNORE_DEPRECATED_POP } } -template void RendererGLTest::constructNDCopy() { - setTestCaseTemplateName(Utility::format("{}", dimensions)); - - CORRADE_VERIFY(!std::is_copy_constructible>{}); - CORRADE_VERIFY(!std::is_copy_assignable>{}); +void RendererGLTest::deprecatedConstructCopy() { + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); + CORRADE_IGNORE_DEPRECATED_POP } -template void RendererGLTest::constructNDMove() { - setTestCaseTemplateName(Utility::format("{}", dimensions)); - +void RendererGLTest::deprecatedConstructMove() { struct: AbstractGlyphCache { using AbstractGlyphCache::AbstractGlyphCache; @@ -965,20 +974,20 @@ template void RendererGLTest::constructNDMove() { font.openFile({}, 0.5f); glyphCache.addFont(3, &font); - BasicRenderer a{font, glyphCache, 3.0f}; + CORRADE_IGNORE_DEPRECATED_PUSH + AbstractRenderer a{font, glyphCache, 3.0f}; - BasicRenderer b = Utility::move(a); + AbstractRenderer b = Utility::move(a); CORRADE_COMPARE(b.fontSize(), 3.0f); - CORRADE_VERIFY(std::is_nothrow_move_constructible>::value); + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); /* Because it contains reference members. Not going to fix this, just pinning down existing behavior. */ - CORRADE_VERIFY(!std::is_move_assignable>{}); + CORRADE_VERIFY(!std::is_move_assignable{}); + CORRADE_IGNORE_DEPRECATED_POP } -void RendererGLTest::renderMesh() { - /* Like render(middle center), but with a mesh output instead of data */ - +void RendererGLTest::deprecatedRenderMesh() { TestFont font; font.openFile({}, 0.5f); GlyphCacheGL cache = testGlyphCache(font); @@ -990,8 +999,10 @@ void RendererGLTest::renderMesh() { GL::Buffer vertexBuffer{GL::Buffer::TargetHint::Array}, indexBuffer{GL::Buffer::TargetHint::ElementArray}; Range2D bounds; + CORRADE_IGNORE_DEPRECATED_PUSH std::tie(mesh, bounds) = Renderer3D::render(font, cache, 0.25f, "abc", vertexBuffer, indexBuffer, GL::BufferUsage::StaticDraw, Alignment::MiddleCenter); + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); /* Alignment offset */ @@ -1031,7 +1042,7 @@ void RendererGLTest::renderMesh() { #endif } -void RendererGLTest::renderMeshIndexType() { +void RendererGLTest::deprecatedRenderMeshIndexType() { #ifndef MAGNUM_TARGET_GLES TestFont font; font.openFile({}, 0.5f); @@ -1047,8 +1058,10 @@ void RendererGLTest::renderMeshIndexType() { texture coordinates, each float is four bytes; six indices per glyph. */ /* 8-bit indices (exactly 256 vertices) */ + CORRADE_IGNORE_DEPRECATED_PUSH std::tie(mesh, std::ignore) = Renderer3D::render(font, cache, 1.0f, std::string(64, 'a'), vertexBuffer, indexBuffer, GL::BufferUsage::StaticDraw); + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); Containers::Array indicesByte = indexBuffer.data(); CORRADE_COMPARE(vertexBuffer.size(), 256*(2 + 2)*4); @@ -1061,8 +1074,10 @@ void RendererGLTest::renderMeshIndexType() { }), TestSuite::Compare::Container); /* 16-bit indices (260 vertices) */ + CORRADE_IGNORE_DEPRECATED_PUSH std::tie(mesh, std::ignore) = Renderer3D::render(font, cache, 1.0f, std::string(65, 'a'), vertexBuffer, indexBuffer, GL::BufferUsage::StaticDraw); + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); Containers::Array indicesShort = indexBuffer.data(); CORRADE_COMPARE(vertexBuffer.size(), 260*(2 + 2)*4); @@ -1078,35 +1093,29 @@ void RendererGLTest::renderMeshIndexType() { #endif } -void RendererGLTest::mutableText() { - #ifndef MAGNUM_TARGET_GLES - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::ARB::map_buffer_range::string() << "is not supported."); - #elif defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - if(!GL::Context::current().isExtensionSupported() && - !GL::Context::current().isExtensionSupported()) - CORRADE_SKIP("No required extension is supported"); - #endif - - /* Like render(middle center) and renderMesh(), but modifying an instance - instead of rendering once */ - +void RendererGLTest::deprecatedMutableText() { TestFont font; font.openFile({}, 0.5f); GlyphCacheGL cache = testGlyphCache(font); + CORRADE_IGNORE_DEPRECATED_PUSH Renderer2D renderer(font, cache, 0.25f, Alignment::MiddleCenter); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(renderer.capacity(), 0); CORRADE_COMPARE(renderer.fontSize(), 0.25f); CORRADE_COMPARE(renderer.rectangle(), Range2D()); + CORRADE_IGNORE_DEPRECATED_POP /* Reserve some capacity */ + CORRADE_IGNORE_DEPRECATED_PUSH renderer.reserve(4, GL::BufferUsage::DynamicDraw, GL::BufferUsage::DynamicDraw); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(renderer.capacity(), 4); + CORRADE_IGNORE_DEPRECATED_POP /** @todo How to verify this on ES? */ #ifndef MAGNUM_TARGET_GLES + CORRADE_IGNORE_DEPRECATED_PUSH Containers::Array indices = renderer.indexBuffer().data(); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE_AS(Containers::arrayCast(indices).prefix(24), Containers::arrayView({ 0, 1, 2, 2, 1, 3, @@ -1117,19 +1126,25 @@ void RendererGLTest::mutableText() { #endif /* Render text */ + CORRADE_IGNORE_DEPRECATED_PUSH renderer.render("abc"); + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); /* Alignment offset */ const Vector2 offset{-1.5f, -0.5f}; /* Updated bounds and mesh vertex count */ + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(renderer.rectangle(), (Range2D{{0.0f, -1.25f}, {3.0f, 2.25f}}.translated(offset))); CORRADE_COMPARE(renderer.mesh().count(), 3*6); + CORRADE_IGNORE_DEPRECATED_POP /** @todo How to verify this on ES? */ #ifndef MAGNUM_TARGET_GLES + CORRADE_IGNORE_DEPRECATED_PUSH Containers::Array vertices = renderer.vertexBuffer().data(); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE_AS(Containers::arrayCast(vertices).prefix(2*4*3), Containers::arrayView({ Vector2{ 2.5f, 5.5f} + offset, {0.0f, 0.0f}, Vector2{12.5f, 5.5f} + offset, {1.0f, 0.0f}, @@ -1150,12 +1165,14 @@ void RendererGLTest::mutableText() { /* Rendering again replaces previous contents. I.e., if rendering an empty string, it makes the output empty. */ + CORRADE_IGNORE_DEPRECATED_PUSH renderer.render(""); - CORRADE_COMPARE(renderer.rectangle(), (Range2D{{0.0f, -1.75f}, {0.0f, 1.75f}})); + CORRADE_COMPARE(renderer.rectangle(), Range2D{}); CORRADE_COMPARE(renderer.mesh().count(), 0); + CORRADE_IGNORE_DEPRECATED_POP } -void RendererGLTest::arrayGlyphCache() { +void RendererGLTest::deprecatedArrayGlyphCache() { CORRADE_SKIP_IF_NO_ASSERT(); TestFont font; @@ -1168,16 +1185,21 @@ void RendererGLTest::arrayGlyphCache() { GL::Buffer buffer; + /* This used to fire with the old internals and the new internals support + arrays but the user may still attempt to use the deprecated class with + an array-unaware shader, which should still be caught like before. */ Containers::String out; Error redirectError{&out}; - Renderer2D::render(font, cache, 1.0f, "", buffer, buffer, GL::BufferUsage::StaticDraw); + CORRADE_IGNORE_DEPRECATED_PUSH Renderer2D{font, cache, 1.0f}; + Renderer2D::render(font, cache, 1.0f, "", buffer, buffer, GL::BufferUsage::StaticDraw); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out, - "Text::Renderer: array glyph caches are not supported\n" - "Text::AbstractRenderer: array glyph caches are not supported\n"); + "Text::AbstractRenderer: array glyph caches are not supported\n" + "Text::AbstractRenderer::render(): array glyph caches are not supported\n"); } -void RendererGLTest::fontNotFoundInCache() { +void RendererGLTest::deprecatedFontNotFoundInCache() { CORRADE_SKIP_IF_NO_ASSERT(); TestFont font; @@ -1193,14 +1215,21 @@ void RendererGLTest::fontNotFoundInCache() { GL::Buffer buffer; + /* The first delegates to RendererCore so just verify that nothing strange + happens during delegation, in the second case the constructor should + fire already because it's better than doing that only once .render() is + called */ Containers::String out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH Renderer2D::render(font, cache, 1.0f, "", buffer, buffer, GL::BufferUsage::StaticDraw); Renderer2D{font, cache, 1.0f}; + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out, - "Text::Renderer: font not found among 2 fonts in passed glyph cache\n" + "Text::RendererCore::add(): shaper font not found among 2 fonts in associated glyph cache\n" "Text::AbstractRenderer: font not found among 2 fonts in passed glyph cache\n"); } +#endif }}}} diff --git a/src/Magnum/Text/Test/RendererTest.cpp b/src/Magnum/Text/Test/RendererTest.cpp index 839a881a8..40df2497f 100644 --- a/src/Magnum/Text/Test/RendererTest.cpp +++ b/src/Magnum/Text/Test/RendererTest.cpp @@ -24,9 +24,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include #include #include #include @@ -43,13 +40,21 @@ #include "Magnum/Mesh.h" #include "Magnum/PixelFormat.h" +#include "Magnum/Math/Range.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/AbstractShaper.h" +#include "Magnum/Text/Alignment.h" #include "Magnum/Text/Direction.h" #include "Magnum/Text/Feature.h" #include "Magnum/Text/Renderer.h" +#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL) +#include +#include +#include +#endif + namespace Magnum { namespace Text { namespace Test { namespace { struct RendererTest: TestSuite::Tester { @@ -144,13 +149,13 @@ struct RendererTest: TestSuite::Tester { void clearReset(); void clearResetAllocators(); - #ifdef MAGNUM_TARGET_GL - void renderData(); + #if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL) + void deprecatedRenderData(); - void multiline(); + void deprecatedMultiline(); - void arrayGlyphCache(); - void fontNotFoundInCache(); + void deprecatedArrayGlyphCache(); + void deprecatedFontNotFoundInCache(); #endif }; @@ -1466,13 +1471,13 @@ const struct { {"reset while in progress", {}, true, true}, }; -#ifdef MAGNUM_TARGET_GL +#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL) const struct { TestSuite::TestCaseDescriptionSourceLocation name; Alignment alignment; ShapeDirection shapeDirection; Vector2 offset; -} RenderDataData[]{ +} DeprecatedRenderDataData[]{ /* Not testing all combinations, just making sure that each horizontal, vertical, glyph bounds and integer variant is covered */ {"line left", @@ -1578,7 +1583,7 @@ const struct { /* The Y offset value could be calculated, but this is easier to grasp and makes it possible to test overrideable line height later, for example */ Vector2 offset0, offset1, offset2; -} MultilineData[]{ +} DeprecatedMultilineData[]{ {"line left", Alignment::LineLeft, {0.0f, -0.0f}, {0.0f, -4.0f}, @@ -1797,15 +1802,15 @@ RendererTest::RendererTest() { &RendererTest::clearResetAllocators}, Containers::arraySize(ClearResetData)); - #ifdef MAGNUM_TARGET_GL - addInstancedTests({&RendererTest::renderData}, - Containers::arraySize(RenderDataData)); + #if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL) + addInstancedTests({&RendererTest::deprecatedRenderData}, + Containers::arraySize(DeprecatedRenderDataData)); - addInstancedTests({&RendererTest::multiline}, - Containers::arraySize(MultilineData)); + addInstancedTests({&RendererTest::deprecatedMultiline}, + Containers::arraySize(DeprecatedMultilineData)); - addTests({&RendererTest::arrayGlyphCache, - &RendererTest::fontNotFoundInCache}); + addTests({&RendererTest::deprecatedArrayGlyphCache, + &RendererTest::deprecatedFontNotFoundInCache}); #endif } @@ -7467,9 +7472,9 @@ void RendererTest::clearResetAllocators() { is tested in allocateDifferentIndexType(). */ } -#ifdef MAGNUM_TARGET_GL -void RendererTest::renderData() { - auto&& data = RenderDataData[testCaseInstanceId()]; +#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL) +void RendererTest::deprecatedRenderData() { + auto&& data = DeprecatedRenderDataData[testCaseInstanceId()]; setTestCaseDescription(data.name); TestFont font; @@ -7484,7 +7489,9 @@ void RendererTest::renderData() { std::vector textureCoordinates; std::vector indices; Range2D bounds; + CORRADE_IGNORE_DEPRECATED_PUSH std::tie(positions, textureCoordinates, indices, bounds) = AbstractRenderer::render(font, cache, 0.25f, "abc", data.alignment); + CORRADE_IGNORE_DEPRECATED_POP /* Three glyphs, three quads -> 12 vertices, 18 indices */ CORRADE_COMPARE(positions.size(), 12); @@ -7571,15 +7578,15 @@ void RendererTest::renderData() { }), TestSuite::Compare::Container); } -void RendererTest::multiline() { - auto&& data = MultilineData[testCaseInstanceId()]; +void RendererTest::deprecatedMultiline() { + auto&& data = DeprecatedMultilineData[testCaseInstanceId()]; setTestCaseDescription(data.name); struct Shaper: AbstractShaper { using AbstractShaper::AbstractShaper; - UnsignedInt doShape(Containers::StringView text, UnsignedInt, UnsignedInt, Containers::ArrayView) override { - return text.size(); + UnsignedInt doShape(Containers::StringView, UnsignedInt begin, UnsignedInt end, Containers::ArrayView) override { + return end - begin; } void doGlyphIdsInto(const Containers::StridedArrayView1D& ids) const override { @@ -7638,8 +7645,10 @@ void RendererTest::multiline() { Range2D rectangle; std::vector indices; std::vector positions, textureCoordinates; + CORRADE_IGNORE_DEPRECATED_PUSH std::tie(positions, textureCoordinates, indices, rectangle) = Renderer2D::render(font, cache, 0.25f, "abcd\nef\n\nghi", data.alignment); + CORRADE_IGNORE_DEPRECATED_POP /* We're rendering text at 0.25f size and the font is scaled to 0.5f, so the line advance should be 8.0f*0.25f/0.5f = 4.0f */ @@ -7726,7 +7735,7 @@ void RendererTest::multiline() { }), TestSuite::Compare::Container); } -void RendererTest::arrayGlyphCache() { +void RendererTest::deprecatedArrayGlyphCache() { CORRADE_SKIP_IF_NO_ASSERT(); TestFont font; @@ -7737,13 +7746,17 @@ void RendererTest::arrayGlyphCache() { GlyphCacheFeatures doFeatures() const override { return {}; } } cache{PixelFormat::R8Unorm, {100, 100, 3}}; + /* The function returns two-component texture coordinates so it can't be + done any other way even though the new internals support it */ Containers::String out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH AbstractRenderer::render(font, cache, 0.25f, "abc"); - CORRADE_COMPARE(out, "Text::Renderer: array glyph caches are not supported\n"); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out, "Text::AbstractRenderer::render(): array glyph caches are not supported\n"); } -void RendererTest::fontNotFoundInCache() { +void RendererTest::deprecatedFontNotFoundInCache() { CORRADE_SKIP_IF_NO_ASSERT(); TestFont font; @@ -7753,10 +7766,14 @@ void RendererTest::fontNotFoundInCache() { cache.addFont(34); cache.addFont(25); + /* It delegates to RendererCore so just verify that nothing strange happens + during delegation */ Containers::String out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH AbstractRenderer::render(font, cache, 0.25f, "abc"); - CORRADE_COMPARE(out, "Text::Renderer: font not found among 2 fonts in passed glyph cache\n"); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out, "Text::RendererCore::add(): shaper font not found among 2 fonts in associated glyph cache\n"); } #endif diff --git a/src/Magnum/Text/Text.h b/src/Magnum/Text/Text.h index c5e9d7cae..822d774ca 100644 --- a/src/Magnum/Text/Text.h +++ b/src/Magnum/Text/Text.h @@ -69,11 +69,14 @@ typedef CORRADE_DEPRECATED("use GlyphCacheGL instead") GlyphCacheGL GlyphCache; #ifndef MAGNUM_TARGET_GLES2 class GlyphCacheArrayGL; #endif -class AbstractRenderer; -template class BasicRenderer; -typedef BasicRenderer<2> Renderer2D; -typedef BasicRenderer<3> Renderer3D; class RendererGL; +#ifdef MAGNUM_BUILD_DEPRECATED +class CORRADE_DEPRECATED("use Renderer or RendererGL instead") AbstractRenderer; +CORRADE_IGNORE_DEPRECATED_PUSH /* idiotic MSVC warns for deprecated APIs using deprecated APIs */ +typedef CORRADE_DEPRECATED("use Renderer or RendererGL instead") AbstractRenderer Renderer2D; +typedef CORRADE_DEPRECATED("use Renderer or RendererGL instead") AbstractRenderer Renderer3D; +CORRADE_IGNORE_DEPRECATED_POP +#endif #endif }}