Browse Source

Text: add glyphQuadBounds().

For when one needs to get the rectangle for aligning with
Alignment::*GlyphBounds but doesn't want to generate the actual quads
just yet.
pull/674/head
Vladimír Vondruš 2 years ago
parent
commit
1553328cd7
  1. 7
      doc/changelog.dox
  2. 22
      src/Magnum/Text/Renderer.cpp
  3. 40
      src/Magnum/Text/Renderer.h
  4. 53
      src/Magnum/Text/Test/RendererTest.cpp

7
doc/changelog.dox

@ -458,9 +458,10 @@ See also:
@ref TextureTools::AtlasLandfill allowing more efficient and incremental
glyph packing together with support for texture arrays
- New @ref Text::renderLineGlyphPositionsInto(),
@ref Text::renderGlyphQuadsInto(), @ref Text::alignRenderedLine(),
@ref Text::alignRenderedBlock() and @ref Text::renderGlyphQuadIndicesInto()
APIs providing low-level access to the text renderer building blocks
@ref Text::renderGlyphQuadsInto(), @ref Text::glyphQuadBounds(),
@ref Text::alignRenderedLine(), @ref Text::alignRenderedBlock() and
@ref Text::renderGlyphQuadIndicesInto() APIs providing low-level access to
the text renderer building blocks
- New @ref Text::glyphRangeForBytes() API for providing byte-to-glyph mapping
for arbitrarily complex shapers using the output from
@ref Text::AbstractShaper::glyphClustersInto()

22
src/Magnum/Text/Renderer.cpp

@ -191,6 +191,28 @@ Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, const Float scale,
return renderGlyphQuadsInto(cache, scale, glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates, nullptr);
}
Range2D glyphQuadBounds(const AbstractGlyphCache& cache, const Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds) {
CORRADE_ASSERT(glyphIds.size() == glyphPositions.size(),
"Text::glyphQuadBounds(): expected glyphIds and glyphPositions views to have the same size, got" << glyphIds.size() << "and" << glyphPositions.size(), {});
/* Direct views on the cache data */
const Containers::StridedArrayView1D<const Vector2i> cacheGlyphOffsets = cache.glyphOffsets();
const Containers::StridedArrayView1D<const Range2Di> cacheGlyphRectangles = cache.glyphRectangles();
/* Basically what renderGlyphQuadsInto() does above, but producing just the
rectangle */
Range2D rectangle;
for(std::size_t i = 0; i != glyphIds.size(); ++i) {
const UnsignedInt glyphId = glyphIds[i];
const Range2D quad = Range2D::fromSize(
glyphPositions[i] + Vector2{cacheGlyphOffsets[glyphId]}*scale,
Vector2{cacheGlyphRectangles[glyphId].size()}*scale);
rectangle = Math::join(rectangle, quad);
}
return rectangle;
}
Range2D alignRenderedLine(const Range2D& lineRectangle, const LayoutDirection direction, const Alignment alignment, const Containers::StridedArrayView1D<Vector2>& positions) {
CORRADE_ASSERT(direction == LayoutDirection::HorizontalTopToBottom,
"Text::alignRenderedLine(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << direction, {});

40
src/Magnum/Text/Renderer.h

@ -27,7 +27,7 @@
*/
/** @file
* @brief Class @ref Magnum::Text::AbstractRenderer, typedef @ref Magnum::Text::Renderer2D, @ref Magnum::Text::Renderer3D, function @ref Magnum::Text::renderLineGlyphPositionsInto(), @ref Magnum::Text::renderGlyphQuadsInto(), @ref Magnum::Text::alignRenderedLine(), @ref Magnum::Text::alignRenderedBlock(), @ref Magnum::Text::renderGlyphQuadIndicesInto(), @ref Magnum::Text::glyphRangeForBytes()
* @brief Class @ref Magnum::Text::AbstractRenderer, typedef @ref Magnum::Text::Renderer2D, @ref Magnum::Text::Renderer3D, function @ref Magnum::Text::renderLineGlyphPositionsInto(), @ref Magnum::Text::renderGlyphQuadsInto(), @ref Magnum::Text::glyphQuadBounds(), @ref Magnum::Text::alignRenderedLine(), @ref Magnum::Text::alignRenderedBlock(), @ref Magnum::Text::renderGlyphQuadIndicesInto(), @ref Magnum::Text::glyphRangeForBytes()
*/
#include "Magnum/Magnum.h"
@ -155,6 +155,7 @@ overload to get just 2D texture coordinates out. Use the
@ref renderGlyphQuadsInto(const AbstractGlyphCache&, Float, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&)
overload if you already have cache-global glyph IDs. Use
@ref renderGlyphQuadIndicesInto() to populate the corresponding index array.
@see @ref glyphQuadBounds()
*/
MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractFont& font, Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& fontGlyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates);
@ -187,6 +188,7 @@ mapping from font-specific glyph IDs to cache-global IDs happens in this case.
As with the above overload, to optimize memory use, it's possible to alias
@p glyphPositions and @p glyphIds with @cpp vertexPositions.every(4) @ce and
@cpp vertexTextureCoordinates.every(4) @ce.
@see @ref AbstractGlyphCache::glyphIdsInto(), @ref glyphQuadBounds()
*/
MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates);
@ -200,6 +202,29 @@ depth is @cpp 1 @ce.
*/
MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector2>& vertexTextureCoordinates);
/**
@brief Calculate glyph quad bounds from cache-global glyph IDs
@param[in] cache Glyph cache to query for glyph rectangles
@param[in] scale Size to render the glyphs at divided by size of
the input font
@param[in] glyphPositions Glyph positions coming from an earlier call to
@ref renderLineGlyphPositionsInto()
@param[in] glyphIds Cache-global glyph IDs
@return Rectangle spanning the glyph quads
@m_since_latest
Returns the same rectangle as @ref renderGlyphQuadsInto(const AbstractGlyphCache&, Float, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector3>&)
but without actually generating the glyph quads. Use the returned value for
@ref alignRenderedLine() with a `*GlyphBounds` @ref Alignment value.
Note that, unlike with @ref renderGlyphQuadsInto(const AbstractFont&, Float, const AbstractGlyphCache&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector3>&),
this function doesn't have an overload taking font-local glyph IDs, as it
doesn't have any scratch space to store them. Use
@ref AbstractGlyphCache::glyphIdsInto() instead to convert them to cache-global
and then call this function with the result.
*/
MAGNUM_TEXT_EXPORT Range2D glyphQuadBounds(const AbstractGlyphCache& cache, Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds);
/**
@brief Align a rendered line
@param[in] lineRectangle Rectangle spanning the whole line
@ -223,9 +248,12 @@ If @p alignment isn't `*GlyphBounds`, this function should get glyph
and @p lineRectangle being all rectangles returned by that function combined
together with @ref Math::join().
If @p alignment is `*GlyphBounds`, this function should get vertex @p positions
for a whole line coming from @ref renderGlyphQuadsInto() and @p lineRectangle
being all rectangles returned by that function combined together with
If @p alignment is `*GlyphBounds`, this function should get glyph @p positions
for the whole line coming from @ref renderLineGlyphPositionsInto() and a
@p lineRectangle being generated with @ref glyphQuadBounds() from those
positions. Alternatively, it should get vertex @p positions for a whole line
coming from @ref renderGlyphQuadsInto() and @p lineRectangle being all
rectangles returned by that function combined together with
@ref Math::join().
The @p positions are translated in one axis based on the @p inputRectangle and
@ -312,8 +340,8 @@ Assuming @p clusters is a view containing cluster IDs returned from
@ref AbstractShaper::glyphClustersInto() and @p begin and @p end are byte
positions in the text passed to @ref AbstractShaper::shape() for which the
cluster IDs were retrieved, returns a range in the glyph array that contains
given range. Assumes that @p clusters are either monotonically non-dereasing or
non-increasing.
given range. Assumes that @p clusters are either monotonically non-decreasing
or non-increasing.
If @p clusters are empty or @p end is less or equal to all @p clusters, returns
@cpp {0, 0} @ce. If @p begin is greater than all @p clusters are, both return

53
src/Magnum/Text/Test/RendererTest.cpp

@ -62,6 +62,9 @@ struct RendererTest: TestSuite::Tester {
void glyphQuads2D();
void glyphQuads2DArrayGlyphCache();
void glyphQuadBounds();
void glyphQuadBoundsInvalidViewSizes();
void alignLine();
void alignLineInvalidDirection();
@ -344,7 +347,10 @@ RendererTest::RendererTest() {
addInstancedTests({&RendererTest::glyphQuads2D},
Containers::arraySize(GlyphQuadsData));
addTests({&RendererTest::glyphQuads2DArrayGlyphCache});
addTests({&RendererTest::glyphQuads2DArrayGlyphCache,
&RendererTest::glyphQuadBounds,
&RendererTest::glyphQuadBoundsInvalidViewSizes});
addInstancedTests({&RendererTest::alignLine},
Containers::arraySize(AlignLineData));
@ -877,6 +883,51 @@ void RendererTest::glyphQuads2DArrayGlyphCache() {
CORRADE_COMPARE(out, "Text::renderGlyphQuadsInto(): can't use this overload with an array glyph cache\n");
}
void RendererTest::glyphQuadBounds() {
/* Input like in glyphQuads(), verifying just the output rectangle */
TestFont font;
font.openFile({}, 2.5f);
DummyGlyphCache cache = testGlyphCacheArray(font);
Vector2 glyphPositions[]{
{100.0f, 200.0f},
{103.0f, 202.0f},
{107.0f, 196.0f}
};
UnsignedInt glyphIds[]{
/* Glyph 0 is the cache-global invalid glyph */
1, 3, 2
};
/* The font is opened at 2.5, rendering at 1.25, so everything will be
scaled by 0.5 */
Range2D rectangle = Text::glyphQuadBounds(cache, 1.25f/2.5f, glyphPositions, glyphIds);
CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}}));
}
void RendererTest::glyphQuadBoundsInvalidViewSizes() {
CORRADE_SKIP_IF_NO_ASSERT();
TestFont font;
font.openFile({}, 5.0f);
DummyGlyphCache cache{PixelFormat::R8Unorm, {20, 20}};
cache.addFont(96, &font);
Vector2 glyphPositions[4];
Vector2 glyphPositionsInvalid[5];
UnsignedInt glyphIds[4]{};
UnsignedInt glyphIdsInvalid[3];
Containers::String out;
Error redirectError{&out};
Text::glyphQuadBounds(cache, 2.0f, glyphPositions, glyphIdsInvalid);
Text::glyphQuadBounds(cache, 2.0f, glyphPositionsInvalid, glyphIds);
CORRADE_COMPARE_AS(out,
"Text::glyphQuadBounds(): expected glyphIds and glyphPositions views to have the same size, got 3 and 4\n"
"Text::glyphQuadBounds(): expected glyphIds and glyphPositions views to have the same size, got 4 and 5\n",
TestSuite::Compare::String);
}
void RendererTest::alignLine() {
auto&& data = AlignLineData[testCaseInstanceId()];
setTestCaseDescription(data.name);

Loading…
Cancel
Save