Browse Source

Text: add renderGlyphQuadsInto() overload taking cache-global glyph IDs.

Which also allows the internals to be a bit simpler / potentially more
efficient, as the implementation can now access the glyph cache contents
directly, without a getter.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
32485baae8
  1. 67
      src/Magnum/Text/Renderer.cpp
  2. 61
      src/Magnum/Text/Renderer.h
  3. 88
      src/Magnum/Text/Test/RendererTest.cpp

67
src/Magnum/Text/Renderer.cpp

@ -87,7 +87,7 @@ Range2D renderLineGlyphPositionsInto(const AbstractFont& font, const Float size,
namespace { namespace {
Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector2>& vertexTextureCoordinates, const Containers::StridedArrayView1D<Float>& vertexTextureLayers) { Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, const Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector2>& vertexTextureCoordinates, const Containers::StridedArrayView1D<Float>& vertexTextureLayers) {
CORRADE_ASSERT(glyphIds.size() == glyphPositions.size(), CORRADE_ASSERT(glyphIds.size() == glyphPositions.size(),
"Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got" << glyphIds.size() << "and" << glyphPositions.size(), {}); "Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got" << glyphIds.size() << "and" << glyphPositions.size(), {});
CORRADE_ASSERT(vertexPositions.size() == glyphPositions.size()*4 && CORRADE_ASSERT(vertexPositions.size() == glyphPositions.size()*4 &&
@ -96,32 +96,26 @@ Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const A
/* Should be ensured by the callers below */ /* Should be ensured by the callers below */
CORRADE_INTERNAL_ASSERT(!vertexTextureLayers || vertexTextureLayers.size() == vertexTextureCoordinates.size()); CORRADE_INTERNAL_ASSERT(!vertexTextureLayers || vertexTextureLayers.size() == vertexTextureCoordinates.size());
CORRADE_ASSERT(font.isOpened(), /* Direct views on the cache data */
"Text::renderGlyphQuadsInto(): no font opened", {});
const Float scale = size/font.size();
const Vector2 inverseCacheSize = 1.0f/Vector2{cache.size().xy()}; const Vector2 inverseCacheSize = 1.0f/Vector2{cache.size().xy()};
const Containers::StridedArrayView1D<const Vector2i> cacheGlyphOffsets = cache.glyphOffsets();
const Containers::StridedArrayView1D<const Int> cacheGlyphLayers = cache.glyphLayers();
const Containers::StridedArrayView1D<const Range2Di> cacheGlyphRectangles = cache.glyphRectangles();
const Containers::Optional<UnsignedInt> fontId = cache.findFont(&font); /* Create quads for each glyph and calculate the glyph bound rectangle
CORRADE_ASSERT(fontId, along the way. */
"Text::renderGlyphQuadsInto(): font not found among" << cache.fontCount() << "fonts in passed glyph cache", {});
/* Get all glyphs from the glyph cache, create quads for each and calculate
the glyph bound rectangle along the way. */
Range2D rectangle; Range2D rectangle;
for(std::size_t i = 0; i != glyphIds.size(); ++i) { for(std::size_t i = 0; i != glyphIds.size(); ++i) {
/* Offset of the glyph rectangle relative to the cursor, layer,
texture coordinates */
const Containers::Triple<Vector2i, Int, Range2Di> cacheGlyph = cache.glyph(*fontId, glyphIds[i]);
/* 2---3 /* 2---3
| | | |
| | | |
| | | |
0---1 */ 0---1 */
const UnsignedInt glyphId = glyphIds[i];
const Range2D quad = Range2D::fromSize( const Range2D quad = Range2D::fromSize(
glyphPositions[i] + Vector2{cacheGlyph.first()}*scale, glyphPositions[i] + Vector2{cacheGlyphOffsets[glyphId]}*scale,
Vector2{cacheGlyph.third().size()}*scale); Vector2{cacheGlyphRectangles[glyphId].size()}*scale);
const Range2D texture = Range2D{cacheGlyph.third()} const Range2D texture = Range2D{cacheGlyphRectangles[glyphId]}
.scaled(inverseCacheSize); .scaled(inverseCacheSize);
const std::size_t i4 = i*4; const std::size_t i4 = i*4;
for(UnsignedByte j = 0; j != 4; ++j) { for(UnsignedByte j = 0; j != 4; ++j) {
@ -133,7 +127,7 @@ Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const A
/* Fill also a texture layer if desirable. For 2D output the caller /* Fill also a texture layer if desirable. For 2D output the caller
already checked that the cache is 2D. */ already checked that the cache is 2D. */
if(vertexTextureLayers) for(std::size_t j = 0; j != 4; ++j) if(vertexTextureLayers) for(std::size_t j = 0; j != 4; ++j)
vertexTextureLayers[i4 + j] = cacheGlyph.second(); vertexTextureLayers[i4 + j] = cacheGlyphLayers[glyphId];
/* Extend the rectangle with current glyph bounds */ /* Extend the rectangle with current glyph bounds */
rectangle = Math::join(rectangle, quad); rectangle = Math::join(rectangle, quad);
@ -142,6 +136,33 @@ Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const A
return rectangle; return rectangle;
} }
Range2D renderGlyphQuadsInto(const AbstractFont& font, const 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<Vector2>& vertexTextureCoordinates, const Containers::StridedArrayView1D<Float>& vertexTextureLayers) {
CORRADE_ASSERT(font.isOpened(),
"Text::renderGlyphQuadsInto(): no font opened", {});
const Containers::Optional<UnsignedInt> fontId = cache.findFont(&font);
CORRADE_ASSERT(fontId,
"Text::renderGlyphQuadsInto(): font not found among" << cache.fontCount() << "fonts in passed glyph cache", {});
/* First map the font-local glyph IDs to cache-global, abusing the texture
coordinate output array as the storage. Not vertex positions, as those
are allowed to be aliased with glyphPositions by the caller and this
process would overwrite them.
This also means we need to duplicate the size assertions here, to avoid
asserting inside glyphIdsInto() instead and confusing the user. */
CORRADE_ASSERT(fontGlyphIds.size() == glyphPositions.size(),
"Text::renderGlyphQuadsInto(): expected fontGlyphIds and glyphPositions views to have the same size, got" << fontGlyphIds.size() << "and" << glyphPositions.size(), {});
CORRADE_ASSERT(vertexPositions.size() == glyphPositions.size()*4 &&
vertexTextureCoordinates.size() == glyphPositions.size()*4,
"Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have" << glyphPositions.size()*4 << "elements, got" << vertexPositions.size() << "and" << vertexTextureCoordinates.size(), {});
const Containers::StridedArrayView1D<UnsignedInt> glyphIds = Containers::arrayCast<UnsignedInt>(vertexTextureCoordinates.every(4));
cache.glyphIdsInto(*fontId, fontGlyphIds, glyphIds);
/* Delegate to the above */
return renderGlyphQuadsInto(cache, size/font.size(), glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates, vertexTextureLayers);
}
} }
Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates) { Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates) {
@ -154,6 +175,16 @@ Range2D renderGlyphQuadsInto(const AbstractFont& font, const Float size, const A
return renderGlyphQuadsInto(font, size, cache, glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates, nullptr); return renderGlyphQuadsInto(font, size, cache, glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates, nullptr);
} }
Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, const Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates) {
return renderGlyphQuadsInto(cache, scale, glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates.slice(&Vector3::xy), vertexTextureCoordinates.slice(&Vector3::z));
}
Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, const Float scale, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector2>& vertexTextureCoordinates) {
CORRADE_ASSERT(cache.size().z() == 1,
"Text::renderGlyphQuadsInto(): can't use this overload with an array glyph cache", {});
return renderGlyphQuadsInto(cache, scale, glyphPositions, glyphIds, vertexPositions, vertexTextureCoordinates, nullptr);
}
Range2D alignRenderedLine(const Range2D& lineRectangle, const LayoutDirection direction, const Alignment alignment, const Containers::StridedArrayView1D<Vector2>& positions) { Range2D alignRenderedLine(const Range2D& lineRectangle, const LayoutDirection direction, const Alignment alignment, const Containers::StridedArrayView1D<Vector2>& positions) {
CORRADE_ASSERT(direction == LayoutDirection::HorizontalTopToBottom, CORRADE_ASSERT(direction == LayoutDirection::HorizontalTopToBottom,
"Text::alignRenderedLine(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << direction, {}); "Text::alignRenderedLine(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << direction, {});

61
src/Magnum/Text/Renderer.h

@ -107,14 +107,14 @@ together with positions for the whole text to @ref alignRenderedBlock().
MAGNUM_TEXT_EXPORT Range2D renderLineGlyphPositionsInto(const AbstractFont& font, Float size, LayoutDirection direction, const Containers::StridedArrayView1D<const Vector2>& glyphOffsets, const Containers::StridedArrayView1D<const Vector2>& glyphAdvances, Vector2& cursor, const Containers::StridedArrayView1D<Vector2>& glyphPositions); MAGNUM_TEXT_EXPORT Range2D renderLineGlyphPositionsInto(const AbstractFont& font, Float size, LayoutDirection direction, const Containers::StridedArrayView1D<const Vector2>& glyphOffsets, const Containers::StridedArrayView1D<const Vector2>& glyphAdvances, Vector2& cursor, const Containers::StridedArrayView1D<Vector2>& glyphPositions);
/** /**
@brief Render glyph quads for a (part of a) single line @brief Render glyph quads for a (part of a) single line from font-specific glyph IDs
@param[in] font Font to query metrics from @param[in] font Font the glyphs are coming from
@param[in] size Size to render the glyphs at @param[in] size Size to render the glyphs at
@param[in] cache Glyph cache to query for glyph rectangles @param[in] cache Glyph cache to query for glyph rectangles
@param[in] glyphPositions Glyph positions coming from an earlier call to @param[in] glyphPositions Glyph positions coming from an earlier call to
@ref renderLineGlyphPositionsInto() @ref renderLineGlyphPositionsInto()
@param[in] glyphIds Matching glyph IDs coming from @param[in] fontGlyphIds Glyph IDs coming from @ref AbstractShaper
@ref AbstractShaper instance(s) associated with @p font instance(s) associated with @p font
@param[out] vertexPositions Where to put output vertex positions @param[out] vertexPositions Where to put output vertex positions
@param[out] vertexTextureCoordinates Where to put output texture coordinates @param[out] vertexTextureCoordinates Where to put output texture coordinates
@return Rectangle spanning the rendered glyph quads @return Rectangle spanning the rendered glyph quads
@ -125,11 +125,12 @@ as shown below. The @p glyphPositions and @p glyphIds views are expected to
have the same size, the @p vertexPositions and @p vertexTextureCoordinates have the same size, the @p vertexPositions and @p vertexTextureCoordinates
views are then expected to be four times larger than @p glyphPositions and views are then expected to be four times larger than @p glyphPositions and
@p glyphIds, in order to ultimately contain four corner vertices for each @p glyphIds, in order to ultimately contain four corner vertices for each
glyph. To optimize memory use, it's possible to alias @p glyphPositions and glyph. To optimize memory use, it's possible to alias @p glyphPositions with
@p glyphIds with @cpp vertexPositions.every(4) @ce and @cpp vertexPositions.every(4) @ce and @p glyphIds with
@cpp vertexTextureCoordinates.every(4) @ce --- the rendering is performed in a @cpp vertexTextureCoordinates.every(4) @ce. The @p vertexTextureCoordinates are
way that first reads the position and ID for each glyph and only then fills in temporarily used to store resolved cache-global glyph IDs, the rendering is
the vertex data. then performed in a way that first reads the position and ID for each glyph and
only then fills in the vertex data.
@verbatim @verbatim
2---3 2---3
@ -149,20 +150,54 @@ then the @p vertexPositions passed further to @ref alignRenderedLine().
Expects that @p font is contained in @p cache. Glyph IDs not found in the cache Expects that @p font is contained in @p cache. Glyph IDs not found in the cache
are replaced with the cache-global invalid glyph. If the @p cache is only 2D, are replaced with the cache-global invalid glyph. If the @p cache is only 2D,
you can use the @ref renderGlyphQuadsInto(const AbstractFont&, Float, const AbstractGlyphCache&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) you can use the @ref renderGlyphQuadsInto(const AbstractFont&, Float, const AbstractGlyphCache&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&)
overload to get just 2D texture coordinates out. Use 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. @ref renderGlyphQuadIndicesInto() to populate the corresponding index array.
*/ */
MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractFont& font, Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector3>& vertexTextureCoordinates); 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);
/**
@brief Render glyph quads for a (part of a) single line from font-specific glyph IDs and a 2D glyph cache
@m_since_latest
Compared to @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>&)
outputs just 2D texture coordinates. Expects that @ref AbstractGlyphCache::size()
depth is @cpp 1 @ce.
*/
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<Vector2>& vertexTextureCoordinates);
/** /**
@brief Render glyph quads for a (part of a) single line and a 2D glyph cache @brief Render glyph quads for a (part of a) single line 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
@param[out] vertexPositions Where to put output vertex positions
@param[out] vertexTextureCoordinates Where to put output texture coordinates
@return Rectangle spanning the rendered glyph quads
@m_since_latest @m_since_latest
Compared to @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>&) Compared to @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 operates takes cache-global glyph IDs as an input, i.e. no
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.
*/
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);
/**
@brief Render glyph quads for a (part of a) single line from cache-global glyph IDs and a 2D glyph cache
@m_since_latest
Compared to @ref renderGlyphQuadsInto(const AbstractGlyphCache&, Float, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector3>&)
outputs just 2D texture coordinates. Expects that @ref AbstractGlyphCache::size() outputs just 2D texture coordinates. Expects that @ref AbstractGlyphCache::size()
depth is @cpp 1 @ce. depth is @cpp 1 @ce.
*/ */
MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractFont& font, Float size, const AbstractGlyphCache& cache, const Containers::StridedArrayView1D<const Vector2>& glyphPositions, const Containers::StridedArrayView1D<const UnsignedInt>& glyphIds, const Containers::StridedArrayView1D<Vector2>& vertexPositions, const Containers::StridedArrayView1D<Vector2>& vertexTextureCoordinates); 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 Align a rendered line @brief Align a rendered line

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

@ -80,6 +80,14 @@ struct RendererTest: TestSuite::Tester {
#endif #endif
}; };
const struct {
const char* name;
bool globalIds;
} GlyphQuadsData[]{
{"font-specific glyph IDs", false},
{"cache-global glyph IDs", true}
};
const struct { const struct {
const char* name; const char* name;
Alignment alignment; Alignment alignment;
@ -252,16 +260,20 @@ RendererTest::RendererTest() {
&RendererTest::lineGlyphPositionsAliasedViews, &RendererTest::lineGlyphPositionsAliasedViews,
&RendererTest::lineGlyphPositionsInvalidViewSizes, &RendererTest::lineGlyphPositionsInvalidViewSizes,
&RendererTest::lineGlyphPositionsInvalidDirection, &RendererTest::lineGlyphPositionsInvalidDirection,
&RendererTest::lineGlyphPositionsNoFontOpened, &RendererTest::lineGlyphPositionsNoFontOpened});
&RendererTest::glyphQuads, addInstancedTests({&RendererTest::glyphQuads,
&RendererTest::glyphQuadsAliasedViews, &RendererTest::glyphQuadsAliasedViews},
&RendererTest::glyphQuadsInvalidViewSizes, Containers::arraySize(GlyphQuadsData));
addTests({&RendererTest::glyphQuadsInvalidViewSizes,
&RendererTest::glyphQuadsNoFontOpened, &RendererTest::glyphQuadsNoFontOpened,
&RendererTest::glyphQuadsFontNotFoundInCache, &RendererTest::glyphQuadsFontNotFoundInCache});
addInstancedTests({&RendererTest::glyphQuads2D},
Containers::arraySize(GlyphQuadsData));
&RendererTest::glyphQuads2D, addTests({&RendererTest::glyphQuads2DArrayGlyphCache});
&RendererTest::glyphQuads2DArrayGlyphCache});
addInstancedTests({&RendererTest::alignLine}, addInstancedTests({&RendererTest::alignLine},
Containers::arraySize(AlignLineData)); Containers::arraySize(AlignLineData));
@ -355,10 +367,12 @@ DummyGlyphCache testGlyphCache(AbstractFont& font) {
cache.addFont(96); cache.addFont(96);
UnsignedInt fontId = cache.addFont(font.glyphCount(), &font); UnsignedInt fontId = cache.addFont(font.glyphCount(), &font);
/* Three glyphs, covering bottom, top left and top right of the cache */ /* Three glyphs, covering bottom, top right and top left of the cache.
Adding them in a shuffled order to verify non-trivial font-specific to
cache-global glyph mapping in glyphQuads() below. */
cache.addGlyph(fontId, 3, {5, 10}, {{}, {20, 10}}); cache.addGlyph(fontId, 3, {5, 10}, {{}, {20, 10}});
cache.addGlyph(fontId, 7, {10, 5}, {{0, 10}, {10, 20}});
cache.addGlyph(fontId, 9, {5, 5}, {{10, 10}, {20, 20}}); cache.addGlyph(fontId, 9, {5, 5}, {{10, 10}, {20, 20}});
cache.addGlyph(fontId, 7, {10, 5}, {{0, 10}, {10, 20}});
return cache; return cache;
} }
@ -370,10 +384,12 @@ DummyGlyphCache testGlyphCacheArray(AbstractFont& font) {
cache.addFont(96); cache.addFont(96);
UnsignedInt fontId = cache.addFont(font.glyphCount(), &font); UnsignedInt fontId = cache.addFont(font.glyphCount(), &font);
/* Three glyphs, covering bottom, top left and top right of the cache */ /* Three glyphs, covering bottom, top right and top left of the cache.
Adding them in a shuffled order to verify non-trivial font-specific to
cache-global glyph mapping in glyphQuads() below. */
cache.addGlyph(fontId, 3, {5, 10}, 2, {{}, {20, 10}}); cache.addGlyph(fontId, 3, {5, 10}, 2, {{}, {20, 10}});
cache.addGlyph(fontId, 7, {10, 5}, 0, {{0, 10}, {10, 20}});
cache.addGlyph(fontId, 9, {5, 5}, 1, {{10, 10}, {20, 20}}); cache.addGlyph(fontId, 9, {5, 5}, 1, {{10, 10}, {20, 20}});
cache.addGlyph(fontId, 7, {10, 5}, 0, {{0, 10}, {10, 20}});
return cache; return cache;
} }
@ -481,6 +497,9 @@ void RendererTest::lineGlyphPositionsNoFontOpened() {
} }
void RendererTest::glyphQuads() { void RendererTest::glyphQuads() {
auto&& data = GlyphQuadsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
TestFont font; TestFont font;
font.openFile({}, 2.5f); font.openFile({}, 2.5f);
DummyGlyphCache cache = testGlyphCacheArray(font); DummyGlyphCache cache = testGlyphCacheArray(font);
@ -490,15 +509,21 @@ void RendererTest::glyphQuads() {
{103.0f, 202.0f}, {103.0f, 202.0f},
{107.0f, 196.0f} {107.0f, 196.0f}
}; };
UnsignedInt glyphIds[]{ UnsignedInt fontGlyphIds[]{
3, 7, 9 3, 7, 9
}; };
UnsignedInt glyphIds[]{
/* Glyph 0 is the cache-global invalid glyph */
1, 3, 2
};
Vector2 positions[3*4]; Vector2 positions[3*4];
Vector3 textureCoordinates[3*4]; Vector3 textureCoordinates[3*4];
/* The font is opened at 2.5, rendering at 1.25, so everything will be /* The font is opened at 2.5, rendering at 1.25, so everything will be
scaled by 0.5 */ scaled by 0.5 */
Range2D rectangle = renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, glyphIds, positions, textureCoordinates); Range2D rectangle = data.globalIds ?
renderGlyphQuadsInto(cache, 1.25f/2.5f, glyphPositions, glyphIds, positions, textureCoordinates) :
renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, fontGlyphIds, positions, textureCoordinates);
CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}})); CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}}));
/* 2---3 /* 2---3
@ -548,6 +573,9 @@ void RendererTest::glyphQuads() {
} }
void RendererTest::glyphQuadsAliasedViews() { void RendererTest::glyphQuadsAliasedViews() {
auto&& data = GlyphQuadsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like lineGlyphPositions(), but with the input data stored in the output /* Like lineGlyphPositions(), but with the input data stored in the output
array. The internals should be written in a way that doesn't overwrite array. The internals should be written in a way that doesn't overwrite
the input before it's read. */ the input before it's read. */
@ -567,11 +595,13 @@ void RendererTest::glyphQuadsAliasedViews() {
}, glyphPositions); }, glyphPositions);
Containers::StridedArrayView1D<UnsignedInt> glyphIds = Containers::arrayCast<UnsignedInt>(Containers::stridedArrayView(textureCoordinates).every(4)); Containers::StridedArrayView1D<UnsignedInt> glyphIds = Containers::arrayCast<UnsignedInt>(Containers::stridedArrayView(textureCoordinates).every(4));
Utility::copy({ data.globalIds ?
3, 7, 9 Utility::copy({1, 3, 2}, glyphIds) :
}, glyphIds); Utility::copy({3, 7, 9}, glyphIds);
Range2D rectangle = renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, glyphIds, positions, textureCoordinates); Range2D rectangle = data.globalIds ?
renderGlyphQuadsInto(cache, 1.25f/2.5f, glyphPositions, glyphIds, positions, textureCoordinates) :
renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, glyphIds, positions, textureCoordinates);
CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}})); CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}}));
CORRADE_COMPARE_AS(Containers::arrayView(positions), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(Containers::arrayView(positions), Containers::arrayView<Vector2>({
@ -613,10 +643,12 @@ void RendererTest::glyphQuadsInvalidViewSizes() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
TestFont font; TestFont font;
font.openFile({}, 5.0f);
DummyGlyphCache cache{PixelFormat::R8Unorm, {20, 20}}; DummyGlyphCache cache{PixelFormat::R8Unorm, {20, 20}};
cache.addFont(96, &font);
Vector2 glyphPositions[4]; Vector2 glyphPositions[4];
Vector2 glyphPositionsInvalid[5]; Vector2 glyphPositionsInvalid[5];
UnsignedInt glyphIds[4]; UnsignedInt glyphIds[4]{};
UnsignedInt glyphIdsInvalid[3]; UnsignedInt glyphIdsInvalid[3];
Vector2 positions[16]; Vector2 positions[16];
Vector2 positionsInvalid[15]; Vector2 positionsInvalid[15];
@ -626,13 +658,21 @@ void RendererTest::glyphQuadsInvalidViewSizes() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIdsInvalid, positions, textureCoordinates); renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIdsInvalid, positions, textureCoordinates);
renderGlyphQuadsInto(cache, 2.0f, glyphPositions, glyphIdsInvalid, positions, textureCoordinates);
renderGlyphQuadsInto(font, 10.0f, cache, glyphPositionsInvalid, glyphIds, positions, textureCoordinates); renderGlyphQuadsInto(font, 10.0f, cache, glyphPositionsInvalid, glyphIds, positions, textureCoordinates);
renderGlyphQuadsInto(cache, 2.0f, glyphPositionsInvalid, glyphIds, positions, textureCoordinates);
renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIds, positions, textureCoordinatesInvalid); renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIds, positions, textureCoordinatesInvalid);
renderGlyphQuadsInto(cache, 2.0f, glyphPositions, glyphIds, positions, textureCoordinatesInvalid);
renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIds, positionsInvalid, textureCoordinates); renderGlyphQuadsInto(font, 10.0f, cache, glyphPositions, glyphIds, positionsInvalid, textureCoordinates);
renderGlyphQuadsInto(cache, 10.0f, glyphPositions, glyphIds, positionsInvalid, textureCoordinates);
CORRADE_COMPARE_AS(out.str(), CORRADE_COMPARE_AS(out.str(),
"Text::renderGlyphQuadsInto(): expected fontGlyphIds and glyphPositions views to have the same size, got 3 and 4\n"
"Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got 3 and 4\n" "Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got 3 and 4\n"
"Text::renderGlyphQuadsInto(): expected fontGlyphIds and glyphPositions views to have the same size, got 4 and 5\n"
"Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got 4 and 5\n" "Text::renderGlyphQuadsInto(): expected glyphIds and glyphPositions views to have the same size, got 4 and 5\n"
"Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 16 and 17\n" "Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 16 and 17\n"
"Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 16 and 17\n"
"Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 15 and 16\n"
"Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 15 and 16\n", "Text::renderGlyphQuadsInto(): expected vertexPositions and vertexTextureCoordinates views to have 16 elements, got 15 and 16\n",
TestSuite::Compare::String); TestSuite::Compare::String);
} }
@ -665,6 +705,9 @@ void RendererTest::glyphQuadsFontNotFoundInCache() {
} }
void RendererTest::glyphQuads2D() { void RendererTest::glyphQuads2D() {
auto&& data = GlyphQuadsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like lineGlyphPositions(), but with just a 2D glyph cache and using the /* Like lineGlyphPositions(), but with just a 2D glyph cache and using the
three-component overload. */ three-component overload. */
@ -677,13 +720,18 @@ void RendererTest::glyphQuads2D() {
{103.0f, 202.0f}, {103.0f, 202.0f},
{107.0f, 196.0f} {107.0f, 196.0f}
}; };
UnsignedInt glyphIds[]{ UnsignedInt fontGlyphIds[]{
3, 7, 9 3, 7, 9
}; };
UnsignedInt glyphIds[]{
1, 3, 2
};
Vector2 positions[3*4]; Vector2 positions[3*4];
Vector2 textureCoordinates[3*4]; Vector2 textureCoordinates[3*4];
Range2D rectangle = renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, glyphIds, positions, textureCoordinates); Range2D rectangle = data.globalIds ?
renderGlyphQuadsInto(cache, 1.25f/2.5f, glyphPositions, glyphIds, positions, textureCoordinates) :
renderGlyphQuadsInto(font, 1.25f, cache, glyphPositions, fontGlyphIds, positions, textureCoordinates);
CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}})); CORRADE_COMPARE(rectangle, (Range2D{{102.5f, 198.5f}, {114.5f, 210.0f}}));
CORRADE_COMPARE_AS(Containers::arrayView(positions), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(Containers::arrayView(positions), Containers::arrayView<Vector2>({

Loading…
Cancel
Save