Browse Source

Text: separate glyph ID and offset/advance retrieval in AbstractShaper.

In basically all cases it's two independent operations so forcing them
to be done together doesn't really bring any potential efficiency
advantages. On the other hand, splitting them allows allows the caller
to better make use of available memory, as the new
renderGlyphQuadsInto() allows the input and output arrays to be aliased.

Bumping AbstractFont plugin interface versions as this is a breaking
change.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
9bc1b9b3b1
  1. 32
      doc/snippets/MagnumText.cpp
  2. 8
      src/Magnum/Text/AbstractFont.cpp
  3. 2
      src/Magnum/Text/AbstractFont.h
  4. 17
      src/Magnum/Text/AbstractShaper.cpp
  5. 61
      src/Magnum/Text/AbstractShaper.h
  6. 16
      src/Magnum/Text/Direction.h
  7. 8
      src/Magnum/Text/Renderer.cpp
  8. 10
      src/Magnum/Text/Test/AbstractFontTest.cpp
  9. 67
      src/Magnum/Text/Test/AbstractShaperTest.cpp
  10. 7
      src/Magnum/Text/Test/RendererGLTest.cpp
  11. 13
      src/Magnum/Text/Test/RendererTest.cpp
  12. 7
      src/MagnumPlugins/MagnumFont/MagnumFont.cpp
  13. 12
      src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp

32
doc/snippets/MagnumText.cpp

@ -278,9 +278,11 @@ struct GlyphInfo {
Vector2 advance;
};
Containers::Array<GlyphInfo> glyphs{NoInit, shaper->glyphCount()};
shaper->glyphsInto(stridedArrayView(glyphs).slice(&GlyphInfo::id),
stridedArrayView(glyphs).slice(&GlyphInfo::offset),
stridedArrayView(glyphs).slice(&GlyphInfo::advance));
shaper->glyphIdsInto(
stridedArrayView(glyphs).slice(&GlyphInfo::id));
shaper->glyphOffsetsAdvancesInto(
stridedArrayView(glyphs).slice(&GlyphInfo::offset),
stridedArrayView(glyphs).slice(&GlyphInfo::advance));
/* [AbstractShaper-shape] */
}
@ -313,25 +315,31 @@ Containers::Array<GlyphInfo> glyphs;
shaper->shape("Hello, world!", 0, 7);
Containers::StridedArrayView1D<GlyphInfo> glyphs1 =
arrayAppend(glyphs, NoInit, shaper->glyphCount());
shaper->glyphsInto(glyphs1.slice(&GlyphInfo::id),
glyphs1.slice(&GlyphInfo::offset),
glyphs1.slice(&GlyphInfo::advance));
shaper->glyphIdsInto(
glyphs1.slice(&GlyphInfo::id));
shaper->glyphOffsetsAdvancesInto(
glyphs1.slice(&GlyphInfo::offset),
glyphs1.slice(&GlyphInfo::advance));
/* Append "world" shaped with a bold font */
boldShaper->shape("Hello, world!", 7, 12);
Containers::StridedArrayView1D<GlyphInfo> glyphs2 =
arrayAppend(glyphs, NoInit, boldShaper->glyphCount());
boldShaper->glyphsInto(glyphs2.slice(&GlyphInfo::id),
glyphs2.slice(&GlyphInfo::offset),
glyphs2.slice(&GlyphInfo::advance));
shaper->glyphIdsInto(
glyphs2.slice(&GlyphInfo::id));
shaper->glyphOffsetsAdvancesInto(
glyphs2.slice(&GlyphInfo::offset),
glyphs2.slice(&GlyphInfo::advance));
/* Finally shape "!" shaped with a regular font again */
shaper->shape("Hello, world!", 12, 13);
Containers::StridedArrayView1D<GlyphInfo> glyphs3 =
arrayAppend(glyphs, NoInit, shaper->glyphCount());
shaper->glyphsInto(glyphs3.slice(&GlyphInfo::id),
glyphs3.slice(&GlyphInfo::offset),
glyphs3.slice(&GlyphInfo::advance));
shaper->glyphIdsInto(
glyphs3.slice(&GlyphInfo::id));
shaper->glyphOffsetsAdvancesInto(
glyphs3.slice(&GlyphInfo::offset),
glyphs3.slice(&GlyphInfo::advance));
/* [AbstractShaper-shape-multiple] */
}

8
src/Magnum/Text/AbstractFont.cpp

@ -346,9 +346,11 @@ Containers::Pointer<AbstractLayouter> AbstractFont::layout(const AbstractGlyphCa
Vector2 advance;
};
Containers::Array<Glyph> glyphs{NoInit, shaper->glyphCount()};
shaper->glyphsInto(stridedArrayView(glyphs).slice(&Glyph::id),
stridedArrayView(glyphs).slice(&Glyph::offset),
stridedArrayView(glyphs).slice(&Glyph::advance));
shaper->glyphIdsInto(
stridedArrayView(glyphs).slice(&Glyph::id));
shaper->glyphOffsetsAdvancesInto(
stridedArrayView(glyphs).slice(&Glyph::offset),
stridedArrayView(glyphs).slice(&Glyph::advance));
/* Create the data to return from AbstractLayouter::renderGlyph(). Most of
this used to be copypasted in various *Layouter::doRenderGlyph()

2
src/Magnum/Text/AbstractFont.h

@ -772,7 +772,7 @@ updated interface string.
*/
/* Silly indentation to make the string appear in pluginInterface() docs */
#define MAGNUM_TEXT_ABSTRACTFONT_PLUGIN_INTERFACE /* [interface] */ \
"cz.mosra.magnum.Text.AbstractFont/0.3.4"
"cz.mosra.magnum.Text.AbstractFont/0.3.5"
/* [interface] */
#ifndef DOXYGEN_GENERATING_OUTPUT

17
src/Magnum/Text/AbstractShaper.cpp

@ -115,13 +115,22 @@ ShapeDirection AbstractShaper::doDirection() const {
return ShapeDirection::Unspecified;
}
void AbstractShaper::glyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const {
CORRADE_ASSERT(ids.size() == _glyphCount && offsets.size() == _glyphCount && advances.size() == _glyphCount,
"Text::AbstractShaper::glyphsInto(): expected the ids, offsets and advanced views to have a size of" << _glyphCount << "but got" << ids.size() << Debug::nospace << "," << offsets.size() << "and" << advances.size(), );
void AbstractShaper::glyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const {
CORRADE_ASSERT(ids.size() == _glyphCount,
"Text::AbstractShaper::glyphIdsInto(): expected the ids view to have a size of" << _glyphCount << "but got" << ids.size(), );
/* Call into the implementation only if there's actually anything shaped,
otherwise it might not yet have everything properly set up */
if(_glyphCount)
doGlyphsInto(ids, offsets, advances);
doGlyphIdsInto(ids);
}
void AbstractShaper::glyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const {
CORRADE_ASSERT(offsets.size() == _glyphCount && advances.size() == _glyphCount,
"Text::AbstractShaper::glyphOffsetsAdvancesInto(): expected the offsets and advanced views to have a size of" << _glyphCount << "but got" << offsets.size() << "and" << advances.size(), );
/* Call into the implementation only if there's actually anything shaped,
otherwise it might not yet have everything properly set up */
if(_glyphCount)
doGlyphOffsetsAdvancesInto(offsets, advances);
}
}}

61
src/Magnum/Text/AbstractShaper.h

@ -123,10 +123,11 @@ instance has to stay in scope for at least as long as the @ref AbstractShaper
is alive.
A text is shaped by calling @ref shape(), retrieving the shaped glyph count
with @ref glyphCount() and then getting the glyph data with @ref glyphsInto().
Glyph IDs can be then queried in (or inserted into) an @ref AbstractGlyphCache,
and the rendered glyphs positioned at @p offsets with the cursor moving by
@p advances is what makes up the final shaped text.
with @ref glyphCount() and then getting the glyph data with @ref glyphIdsInto()
and @ref glyphOffsetsAdvancesInto(). Glyph IDs can be then queried in (or
inserted into) an @ref AbstractGlyphCache, and the rendered glyphs positioned
at @p offsets with the cursor moving by @p advances is what makes up the final
shaped text.
@snippet MagnumText.cpp AbstractShaper-shape
@ -198,12 +199,12 @@ per-font, per-script or per-language instances.
@section Text-AbstractShaper-subclassing Subclassing
The @ref AbstractFont plugin is meant to create a local @ref AbstractShaper
subclass. It implements at least @ref doShape() and @ref doGlyphsInto(), and
potentially also (a subset of) @ref doSetScript(), @ref doScript(),
@ref doSetLanguage(),@ref doLanguage(), @ref doSetDirection() and
@ref doDirection(). The public API does most sanity checks on its own, see
documentation of particular `do*()` functions for more information about the
guarantees.
subclass. It implements at least @ref doShape(), @ref doGlyphIdsInto() and
@ref doGlyphOffsetsAdvancesInto(), and potentially also (a subset of)
@ref doSetScript(), @ref doScript(), @ref doSetLanguage(),@ref doLanguage(),
@ref doSetDirection() and @ref doDirection(). The public API does most sanity
checks on its own, see documentation of particular `do*()` functions for more
information about the guarantees.
*/
class MAGNUM_TEXT_EXPORT AbstractShaper {
public:
@ -397,15 +398,24 @@ class MAGNUM_TEXT_EXPORT AbstractShaper {
ShapeDirection direction() const;
/**
* @brief Retrieve glyph information
* @brief Retrieve glyph IDs
* @param[out] ids Where to put glyph IDs
*
* The @p ids view is expected to have a size of @ref glyphCount().
* After calling this function, the @p ids are commonly looked up in or
* inserted into an @ref AbstractGlyphCache. Offsets and advances
* corresponding to the IDs can be retrieved with
* @ref glyphOffsetsAdvancesInto().
*/
void glyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const;
/**
* @brief Retrieve glyph offsets and advances
* @param[out] offsets Where to put glyph offsets
* @param[out] advances Where to put glyph advances
*
* The @p ids, @p offsets and @p advances views are all expected to
* have a size of @ref glyphCount(). After calling this function, the
* @p ids are commonly looked up in or inserted into an
* @ref AbstractGlyphCache, @p offsets specify where to put the glyph
* The @p offsets and @p advances views are all expected to have a size
* of @ref glyphCount(). The @p offsets specify where to put the glyph
* relative to current cursor (which is then further offset for the
* particular glyph rectangle returned from the glyph cache) and
* @p advances specify in which direction to move the cursor for the
@ -414,10 +424,11 @@ class MAGNUM_TEXT_EXPORT AbstractShaper {
* @relativeref{ShapeDirection,RightToLeft} Y components of @p advances
* are @cpp 0.0f @ce, for @relativeref{ShapeDirection,TopToBottom} or
* @relativeref{ShapeDirection,BottomToTop} X components of @p advances
* are @cpp 0.0f @ce.
* are @cpp 0.0f @ce. Glyph IDs corresponding to the offsets and
* advances can be retrieved with @ref glyphIdsInto().
* @see @ref direction()
*/
void glyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const;
void glyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const;
private:
/**
@ -473,13 +484,21 @@ class MAGNUM_TEXT_EXPORT AbstractShaper {
virtual ShapeDirection doDirection() const;
/**
* @brief Implemenation for @ref glyphsInto()
* @brief Implemenation for @ref glyphIdsInto()
*
* The @p ids are guaranteed to have a size of @ref glyphCount().
* Called only if @ref glyphCount() is not @cpp 0 @ce.
*/
virtual void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const = 0;
/**
* @brief Implemenation for @ref glyphOffsetsAdvancesInto()
*
* The @p ids, @p offsets and @p advances are guaranteed to have a size
* of @ref glyphCount(). Called only if @ref glyphCount() is not
* The @p offsets and @p advances are guaranteed to have a size of
* @ref glyphCount(). Called only if @ref glyphCount() is not
* @cpp 0 @ce.
*/
virtual void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const = 0;
virtual void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const = 0;
Containers::Reference<AbstractFont> _font;
UnsignedInt _glyphCount;

16
src/Magnum/Text/Direction.h

@ -56,29 +56,29 @@ enum class ShapeDirection: UnsignedByte {
/**
* Left to right. When returned from @ref AbstractShaper::direction(),
* the @p advances filled by @ref AbstractShaper::glyphsInto() have their Y
* components @cpp 0.0f @ce.
* the @p advances filled by @ref AbstractShaper::glyphOffsetsAdvancesInto()
* have their Y components @cpp 0.0f @ce.
*/
LeftToRight = 1,
/**
* Right to left. When returned from @ref AbstractShaper::direction(),
* the @p advances filled by @ref AbstractShaper::glyphsInto() have their Y
* components @cpp 0.0f @ce.
* the @p advances filled by @ref AbstractShaper::glyphOffsetsAdvancesInto()
* have their Y components @cpp 0.0f @ce.
*/
RightToLeft,
/**
* Top to bottom. When returned from @ref AbstractShaper::direction(),
* the @p advances filled by @ref AbstractShaper::glyphsInto() have their X
* components @cpp 0.0f @ce.
* the @p advances filled by @ref AbstractShaper::glyphOffsetsAdvancesInto()
* have their X components @cpp 0.0f @ce.
*/
TopToBottom,
/**
* Bottom to top. When returned from @ref AbstractShaper::direction(),
* the @p advances filled by @ref AbstractShaper::glyphsInto() have their X
* components @cpp 0.0f @ce.
* the @p advances filled by @ref AbstractShaper::glyphOffsetsAdvancesInto()
* have their X components @cpp 0.0f @ce.
*/
BottomToTop
};

8
src/Magnum/Text/Renderer.cpp

@ -316,9 +316,11 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
/* Shape the line, get the results */
shaper->shape(line);
const Containers::StridedArrayView1D<Glyph> lineGlyphs = glyphs.prefix(shaper->glyphCount());
shaper->glyphsInto(lineGlyphs.slice(&Glyph::id),
lineGlyphs.slice(&Glyph::offset),
lineGlyphs.slice(&Glyph::advance));
shaper->glyphIdsInto(
lineGlyphs.slice(&Glyph::id));
shaper->glyphOffsetsAdvancesInto(
lineGlyphs.slice(&Glyph::offset),
lineGlyphs.slice(&Glyph::advance));
/* Verify that we don't reallocate anything. The only problem might
arise when the layouter decides to compose one character from more

10
src/Magnum/Text/Test/AbstractFontTest.cpp

@ -1123,7 +1123,8 @@ void AbstractFontTest::createShaper() {
return 37;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
};
struct MyFont: AbstractFont {
@ -1190,8 +1191,10 @@ void AbstractFontTest::layout() {
return 3;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
Utility::copy({3, 7, 3}, ids);
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
Utility::copy({{0.5f, 1.0f},
{1.0f, 0.5f},
{2.0f, 2.0f}}, offsets);
@ -1342,11 +1345,12 @@ void AbstractFontTest::layoutGlyphOutOfRange() {
return 3;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
/* Clear the IDs as otherwise it'd result in OOB calls into the
glyph cache */
for(UnsignedInt& i: ids) i = 0;
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
};
struct MyFont: AbstractFont {

67
src/Magnum/Text/Test/AbstractShaperTest.cpp

@ -182,7 +182,8 @@ struct DummyShaper: AbstractShaper {
using AbstractShaper::AbstractShaper;
UnsignedInt doShape(Containers::StringView, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { return {}; }
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
};
void AbstractShaperTest::construct() {
@ -229,7 +230,8 @@ void AbstractShaperTest::setScript() {
}
UnsignedInt doShape(Containers::StringView, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { return {}; }
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool called = false;
} shaper{FakeFont};
@ -254,7 +256,8 @@ void AbstractShaperTest::setLanguage() {
}
UnsignedInt doShape(Containers::StringView, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { return {}; }
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool called = false;
} shaper{FakeFont};
@ -278,8 +281,8 @@ void AbstractShaperTest::setDirection() {
return true;
}
UnsignedInt doShape(Containers::StringView, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { return {}; }
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
UnsignedInt doShape(Containers::StringView, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { return {}; } void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool called = false;
} shaper{FakeFont};
@ -326,14 +329,17 @@ void AbstractShaperTest::shape() {
return ShapeDirection::BottomToTop;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
CORRADE_COMPARE(ids.size(), 24);
CORRADE_COMPARE(ids[0], 1337);
ids[1] = 666;
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
CORRADE_COMPARE(offsets.size(), 24);
CORRADE_COMPARE(offsets[0], (Vector2{13.0f, 37.0f}));
CORRADE_COMPARE(advances.size(), 24);
CORRADE_COMPARE(advances[0], (Vector2{42.0f, 69.0f}));
ids[1] = 666;
offsets[1] = {-4.0f, -5.0f};
advances[1] = {12.0f, 23.0f};
}
@ -366,7 +372,8 @@ void AbstractShaperTest::shape() {
ids[0] = 1337;
offsets[0] = {13.0f, 37.0f};
advances[0] = {42.0f, 69.0f};
shaper.glyphsInto(ids, offsets, advances);
shaper.glyphIdsInto(ids);
shaper.glyphOffsetsAdvancesInto(offsets, advances);
CORRADE_COMPARE(ids[1], 666);
CORRADE_COMPARE(offsets[1], (Vector2{-4.0f, -5.0f}));
CORRADE_COMPARE(advances[1], (Vector2{12.0f, 23.0f}));
@ -385,7 +392,8 @@ void AbstractShaperTest::shapeNoFeatures() {
return 24;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool shapeCalled = false;
} shaper{FakeFont};
@ -419,7 +427,8 @@ void AbstractShaperTest::shapeNoBeginEnd() {
return 24;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool shapeCalled = false;
} shaper{FakeFont};
@ -449,7 +458,8 @@ void AbstractShaperTest::shapeNoBeginEndFeatures() {
return 24;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
bool shapeCalled = false;
} shaper{FakeFont};
@ -470,7 +480,8 @@ void AbstractShaperTest::shapeScriptLanguageDirectionNotImplemented() {
return 24;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
} shaper{FakeFont};
/* Initially it won't call into any of the implementations */
@ -507,7 +518,8 @@ void AbstractShaperTest::shapeZeroGlyphs() {
return ShapeDirection::BottomToTop;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
} shaper{FakeFont};
CORRADE_COMPARE(shaper.shape("some text", 3, 8), 0);
@ -533,7 +545,8 @@ void AbstractShaperTest::shapeBeginEndOutOfRange() {
return 5;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {}
} shaper{FakeFont};
/* Capture correct function name */
@ -585,7 +598,10 @@ void AbstractShaperTest::glyphsIntoEmpty() {
return 0;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {
CORRADE_FAIL("This shouldn't be called");
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {
CORRADE_FAIL("This shouldn't be called");
}
} shaper{FakeFont};
@ -594,7 +610,8 @@ void AbstractShaperTest::glyphsIntoEmpty() {
CORRADE_VERIFY(true);
/* This should not assert but also not call anywhere */
shaper.glyphsInto(nullptr, nullptr, nullptr);
shaper.glyphIdsInto(nullptr);
shaper.glyphOffsetsAdvancesInto(nullptr, nullptr);
}
void AbstractShaperTest::glyphsIntoInvalidViewSizes() {
@ -607,14 +624,16 @@ void AbstractShaperTest::glyphsIntoInvalidViewSizes() {
return 5;
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>&) const override {
CORRADE_FAIL("This shouldn't be called");
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>&, const Containers::StridedArrayView1D<Vector2>&) const override {
CORRADE_FAIL("This shouldn't be called");
}
} shaper{FakeFont};
CORRADE_COMPARE(shaper.shape("yey"), 5);
UnsignedInt ids[5];
UnsignedInt idsWrong[6];
Vector2 offsets[5];
Vector2 offsetsWrong[6];
@ -623,13 +642,13 @@ void AbstractShaperTest::glyphsIntoInvalidViewSizes() {
std::ostringstream out;
Error redirectError{&out};
shaper.glyphsInto(idsWrong, offsets, advances);
shaper.glyphsInto(ids, offsetsWrong, advances);
shaper.glyphsInto(ids, offsets, advancesWrong);
shaper.glyphIdsInto(idsWrong);
shaper.glyphOffsetsAdvancesInto(offsetsWrong, advances);
shaper.glyphOffsetsAdvancesInto(offsets, advancesWrong);
CORRADE_COMPARE(out.str(),
"Text::AbstractShaper::glyphsInto(): expected the ids, offsets and advanced views to have a size of 5 but got 6, 5 and 5\n"
"Text::AbstractShaper::glyphsInto(): expected the ids, offsets and advanced views to have a size of 5 but got 5, 6 and 5\n"
"Text::AbstractShaper::glyphsInto(): expected the ids, offsets and advanced views to have a size of 5 but got 5, 5 and 6\n");
"Text::AbstractShaper::glyphIdsInto(): expected the ids view to have a size of 5 but got 6\n"
"Text::AbstractShaper::glyphOffsetsAdvancesInto(): expected the offsets and advanced views to have a size of 5 but got 6 and 5\n"
"Text::AbstractShaper::glyphOffsetsAdvancesInto(): expected the offsets and advanced views to have a size of 5 but got 5 and 6\n");
}
}}}}

7
src/Magnum/Text/Test/RendererGLTest.cpp

@ -61,7 +61,7 @@ struct TestShaper: AbstractShaper {
return text.size();
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
for(UnsignedInt i = 0; i != ids.size(); ++i) {
/* It just rotates between the three glyphs */
if(i % 3 == 0)
@ -70,7 +70,10 @@ struct TestShaper: AbstractShaper {
ids[i] = 7;
else
ids[i] = 9;
}
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
for(UnsignedInt i = 0; i != offsets.size(); ++i) {
/* Offset Y and advance X is getting larger with every glyph,
advance Y is flipping its sign with every glyph */
offsets[i] = Vector2::yAxis(i + 1);

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

@ -297,7 +297,7 @@ struct TestShaper: AbstractShaper {
return text.size();
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
for(UnsignedInt i = 0; i != ids.size(); ++i) {
/* It just rotates between the three glyphs */
if(i % 3 == 0)
@ -306,7 +306,10 @@ struct TestShaper: AbstractShaper {
ids[i] = 7;
else
ids[i] = 9;
}
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
for(UnsignedInt i = 0; i != offsets.size(); ++i) {
/* Offset Y and advance X is getting larger with every glyph,
advance Y is flipping its sign with every glyph */
offsets[i] = Vector2::yAxis(i + 1);
@ -961,9 +964,13 @@ void RendererTest::multiline() {
return text.size();
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
for(UnsignedInt i = 0; i != ids.size(); ++i) {
ids[i] = 0;
}
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
for(UnsignedInt i = 0; i != offsets.size(); ++i) {
offsets[i] = {};
advances[i] = Vector2::xAxis(4.0f);
}

7
src/MagnumPlugins/MagnumFont/MagnumFont.cpp

@ -208,10 +208,11 @@ Containers::Pointer<AbstractShaper> MagnumFont::doCreateShaper() {
return _glyphs.size();
}
void doGlyphsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids, const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
const Data& fontData = *static_cast<const MagnumFont&>(font())._opened;
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
Utility::copy(_glyphs, ids);
}
void doGlyphOffsetsAdvancesInto(const Containers::StridedArrayView1D<Vector2>& offsets, const Containers::StridedArrayView1D<Vector2>& advances) const override {
const Data& fontData = *static_cast<const MagnumFont&>(font())._opened;
for(std::size_t i = 0; i != _glyphs.size(); ++i) {
/* There's no glyph offsets in addition to advances */
offsets[i] = {};

12
src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp

@ -152,7 +152,8 @@ void MagnumFontTest::shape() {
UnsignedInt ids[4];
Vector2 offsets[4];
Vector2 advances[4];
shaper->glyphsInto(ids, offsets, advances);
shaper->glyphIdsInto(ids);
shaper->glyphOffsetsAdvancesInto(offsets, advances);
CORRADE_COMPARE_AS(Containers::arrayView(ids), Containers::arrayView({
2u, /* 'W' */
0u, /* 'a' (not found) */
@ -197,7 +198,8 @@ void MagnumFontTest::shaperReuse() {
UnsignedInt ids[2];
Vector2 offsets[2];
Vector2 advances[2];
shaper->glyphsInto(ids, offsets, advances);
shaper->glyphIdsInto(ids);
shaper->glyphOffsetsAdvancesInto(offsets, advances);
CORRADE_COMPARE_AS(Containers::arrayView(ids), Containers::arrayView({
2u, /* 'W' */
1u /* 'e' */
@ -216,7 +218,8 @@ void MagnumFontTest::shaperReuse() {
UnsignedInt ids[4];
Vector2 offsets[4];
Vector2 advances[4];
shaper->glyphsInto(ids, offsets, advances);
shaper->glyphIdsInto(ids);
shaper->glyphOffsetsAdvancesInto(offsets, advances);
CORRADE_COMPARE_AS(Containers::arrayView(ids), Containers::arrayView({
2u, /* 'W' */
0u, /* 'a' (not found) */
@ -239,7 +242,8 @@ void MagnumFontTest::shaperReuse() {
UnsignedInt ids[1];
Vector2 offsets[1];
Vector2 advances[1];
shaper->glyphsInto(ids, offsets, advances);
shaper->glyphIdsInto(ids);
shaper->glyphOffsetsAdvancesInto(offsets, advances);
CORRADE_COMPARE_AS(Containers::arrayView(ids), Containers::arrayView({
0u,
}), TestSuite::Compare::Container);

Loading…
Cancel
Save