Browse Source

Text: align based just on font metric and cursor by default.

So it's possible to shape the text even before having all glyphs ready.
That's one reason, second reason is that this is a more common behavior
-- it usually doesn't make sense to make the text jump based on whether
it's "zaxaca", "KEKEKE" or "yqpyq".

The original alignment based on glyph bounds is now moved into dedicated
`*GlyphBounds` variants. Additionally the `*LeftGlyphBounds` were
changed to subtract the initial glyph offset as well, `*Integer` now
rounds only in the direction where it's needed because a division by 2
happened, and there's a set of `*Bottom*` values that somehow weren't
there before.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
818442a391
  1. 17
      doc/changelog.dox
  2. 324
      src/Magnum/Text/Alignment.h
  3. 61
      src/Magnum/Text/Renderer.cpp
  4. 133
      src/Magnum/Text/Test/RendererGLTest.cpp

17
doc/changelog.dox

@ -785,8 +785,13 @@ See also:
- Added @ref Text::AbstractFont::glyphCount() and - Added @ref Text::AbstractFont::glyphCount() and
@relativeref{Text::AbstractFont,glyphSize()} @relativeref{Text::AbstractFont,glyphSize()}
- Added @ref Text::Renderer::fontSize() - Added @ref Text::Renderer::fontSize()
- Added @ref Text::Alignment::TopCenterIntegral, which is also needed in - @ref Text::Alignment now has separate values for alignment based on font
order to prevent aligning pixel-perfect fonts to fractional positions metrics and cursor position alone, which is the more commonly expected
behavior, and `*GlyphBounds` values that align based on actual glyph
bounds. There's a set of new `Bottom*` values that were for some reason
missing before, and @ref Text::Alignment::TopCenterIntegral, which is also
needed in order to prevent aligning pixel-perfect fonts to fractional
positions.
@subsubsection changelog-latest-changes-trade Trade library @subsubsection changelog-latest-changes-trade Trade library
@ -1660,6 +1665,14 @@ See also:
- The @ref Text::AbstractGlyphCache::image() query now returns a - The @ref Text::AbstractGlyphCache::image() query now returns a
@ref MutableImageView3D instead of an @ref Image2D in order to support @ref MutableImageView3D instead of an @ref Image2D in order to support
incremental population and texture arrays incremental population and texture arrays
- 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 alignment based on glyph bounds is now in the `*GlyphBounds` variants
except for `*LeftGlyphBounds`, where the left glyph offset is also removed
by the alignment compared to `*Left` which performs no horizontal
translation like in the original case. Additionally, the `*Integer`
variants now only round the axis where division by 2 happened for the
middle / center alignment, with the other axis kept unchanged.
- @ref TextureTools::DistanceField now asserts that the output texture has a - @ref TextureTools::DistanceField now asserts that the output texture has a
framebuffer-drawable format. Before it only printed an error message and framebuffer-drawable format. Before it only printed an error message and
silently did nothing, causing a hard-to-track error. silently did nothing, causing a hard-to-track error.

324
src/Magnum/Text/Alignment.h

@ -35,23 +35,35 @@ namespace Magnum { namespace Text {
namespace Implementation { namespace Implementation {
enum: UnsignedByte { enum: UnsignedByte {
AlignmentLeft = 1, /* Line/Left, which causes no shift of the shaped text whatsoever,
AlignmentCenter = 2, is deliberately 0 to signify a default */
AlignmentRight = 3,
AlignmentLeft = 0,
AlignmentCenter = 1 << 0,
AlignmentRight = 2 << 0,
AlignmentHorizontal = AlignmentLeft|AlignmentCenter|AlignmentRight, AlignmentHorizontal = AlignmentLeft|AlignmentCenter|AlignmentRight,
AlignmentLine = 1 << 3, AlignmentLine = 0,
AlignmentMiddle = 2 << 3, AlignmentBottom = 1 << 2,
AlignmentTop = 3 << 3, AlignmentMiddle = 2 << 2,
AlignmentVertical = AlignmentLine|AlignmentMiddle|AlignmentTop, AlignmentTop = 3 << 2,
AlignmentVertical = AlignmentLine|AlignmentBottom|AlignmentMiddle|AlignmentTop,
AlignmentIntegral = 1 << 6, AlignmentIntegral = 1 << 4,
AlignmentGlyphBounds = 1 << 5
}; };
} }
/** /**
@brief Text rendering alignment @brief Text rendering alignment
By default, the alignment is performed based on cursor position and font metric
alone, without taking actual glyph offsets and rectangles into account. This
allows the alignment to be performed even before actual glyph bounds are known
and avoids the position changing based on what concrete glyphs are present.
Aligning to actual glyph rectangle bounds can be done with the `*GlyphBounds`
variants.
The `*Integer` values are meant to be used for pixel-perfect fonts that always The `*Integer` values are meant to be used for pixel-perfect fonts that always
have glyph sizes, advances and other metrics whole pixels, and need to be have glyph sizes, advances and other metrics whole pixels, and need to be
positioned on whole pixels as well in order to avoid making them blurry. These positioned on whole pixels as well in order to avoid making them blurry. These
@ -61,89 +73,301 @@ half-pixel shifts when divided by 2.
@see @ref Renderer::render(), @ref Renderer::Renderer() @see @ref Renderer::render(), @ref Renderer::Renderer()
*/ */
enum class Alignment: UnsignedByte { enum class Alignment: UnsignedByte {
/** Text start and line is at origin */ /**
* Leftmost cursor position and vertical line position is at origin.
* @see @ref Alignment::LineLeftGlyphBounds
*/
LineLeft = Implementation::AlignmentLine|Implementation::AlignmentLeft, LineLeft = Implementation::AlignmentLine|Implementation::AlignmentLeft,
/** /**
* Text center and line is at origin * Left side of the glyph bounding rectangle and vertical line position is
* * at origin.
* @see @ref Alignment::LineCenterIntegral * @see @ref Alignment::LineLeft
* @m_since_latest
*/
LineLeftGlyphBounds = LineLeft|Implementation::AlignmentGlyphBounds,
/**
* Midpoint between leftmost and rightmost cursor position and vertical
* line position is at origin.
* @see @ref Alignment::LineCenterGlyphBounds,
* @ref Alignment::LineCenterIntegral
*/ */
LineCenter = Implementation::AlignmentLine|Implementation::AlignmentCenter, LineCenter = Implementation::AlignmentLine|Implementation::AlignmentCenter,
/** Text end and line is at origin */ /**
* Midpoint between leftmost and rightmost cursor position and vertical
* line position is at origin, with the horizontal offset rounded to whole
* units.
* @see @ref Alignment::LineCenter,
* @ref Alignment::LineCenterGlyphBoundsIntegral
*/
LineCenterIntegral = LineCenter|Implementation::AlignmentIntegral,
/**
* Horizontal center of the glyph bounding rectangle and vertical line
* position is at origin.
* @see @ref Alignment::LineCenter,
* @ref Alignment::LineCenterGlyphBoundsIntegral
* @m_since_latest
*/
LineCenterGlyphBounds = LineCenter|Implementation::AlignmentGlyphBounds,
/**
* Horizontal center of the glyph bounding rectangle and vertical line
* position is at origin, with the horizontal offset rounded to whole
* units.
* @see @ref Alignment::LineCenterGlyphBounds,
* @ref Alignment::LineCenterIntegral
* @m_since_latest
*/
LineCenterGlyphBoundsIntegral = LineCenterGlyphBounds|Implementation::AlignmentIntegral,
/**
* Rightmost cursor position and vertical line position is at origin.
* @see @ref Alignment::LineRightGlyphBounds
*/
LineRight = Implementation::AlignmentLine|Implementation::AlignmentRight, LineRight = Implementation::AlignmentLine|Implementation::AlignmentRight,
/** /**
* Text start and vertical middle is at origin * Right side of the glyph bounding rectangle and vertical line position is
* * at origin.
* @see @ref Alignment::MiddleLeftIntegral * @see @ref Alignment::LineRight
* @m_since_latest
*/
LineRightGlyphBounds = LineRight|Implementation::AlignmentGlyphBounds,
/**
* Leftmost cursor position and bottommost line descent is at origin.
* @see @ref Alignment::BottomLeftGlyphBounds
* @m_since_latest
*/
BottomLeft = Implementation::AlignmentBottom|Implementation::AlignmentLeft,
/**
* Bottom left corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::BottomLeft
* @m_since_latest
*/
BottomLeftGlyphBounds = BottomLeft|Implementation::AlignmentGlyphBounds,
/**
* Midpoint between leftmost and rightmost cursor position and bottommost
* line decent is at origin.
* @see @ref Alignment::BottomCenterGlyphBounds,
* @ref Alignment::BottomCenterIntegral
* @m_since_latest
*/
BottomCenter = Implementation::AlignmentBottom|Implementation::AlignmentCenter,
/**
* Midpoint between leftmost and rightmost cursor position and bottommost
* line descent is at origin, with the horizontal offset rounded to whole
* units.
* @see @ref Alignment::BottomCenter,
* @ref Alignment::BottomCenterGlyphBoundsIntegral
* @m_since_latest
*/
BottomCenterIntegral = BottomCenter|Implementation::AlignmentIntegral,
/**
* Horizontal center and bottom side of the glyph bounding rectangle is at
* origin.
* @see @ref Alignment::BottomCenter,
* @ref Alignment::BottomCenterGlyphBoundsIntegral
* @m_since_latest
*/
BottomCenterGlyphBounds = BottomCenter|Implementation::AlignmentGlyphBounds,
/**
* Horizontal center and bottom side of the glyph bounding rectangle is at
* origin, with the horizontal offset rounded to whole units.
* @see @ref Alignment::BottomCenterGlyphBounds,
* @ref Alignment::BottomCenterIntegral
* @m_since_latest
*/
BottomCenterGlyphBoundsIntegral = BottomCenterGlyphBounds|Implementation::AlignmentIntegral,
/**
* Rightmost cursor position and bottommost line descent is at origin.
* @see @ref Alignment::BottomRightGlyphBounds
* @m_since_latest
*/
BottomRight = Implementation::AlignmentBottom|Implementation::AlignmentRight,
/**
* Bottom right corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::BottomRight
* @m_since_latest
*/
BottomRightGlyphBounds = BottomRight|Implementation::AlignmentGlyphBounds,
/**
* Leftmost cursor position and a midpoint between topmost line ascent and
* bottommost line descent is at origin.
* @see @ref Alignment::MiddleLeftGlyphBounds,
* @ref Alignment::MiddleLeftIntegral
*/ */
MiddleLeft = Implementation::AlignmentMiddle|Implementation::AlignmentLeft, MiddleLeft = Implementation::AlignmentMiddle|Implementation::AlignmentLeft,
/** /**
* Text center and vertical middle is at origin * Leftmost cursor position and a midpoint between topmost line ascent and
* * bottommost line descent is at origin, with the vertical offset rounded
* @see @ref Alignment::MiddleRightIntegral * to whole units.
* @see @ref Alignment::MiddleLeft,
* @ref Alignment::MiddleLeftGlyphBoundsIntegral
*/
MiddleLeftIntegral = MiddleLeft|Implementation::AlignmentIntegral,
/**
* Left side and vertical center of the glyph bounding rectangle is at
* origin.
* @see @ref Alignment::MiddleLeft,
* @ref Alignment::MiddleLeftGlyphBoundsIntegral
* @m_since_latest
*/
MiddleLeftGlyphBounds = MiddleLeft|Implementation::AlignmentGlyphBounds,
/**
* Left side and vertical center of the glyph bounding rectangle is at
* origin, with the vertical offset rounded to whole units.
* @see @ref Alignment::MiddleLeftGlyphBounds,
* @ref Alignment::MiddleLeftIntegral
* @m_since_latest
*/
MiddleLeftGlyphBoundsIntegral = MiddleLeftGlyphBounds|Implementation::AlignmentIntegral,
/**
* Midpoint between leftmost and rightmost cursor position and a midpoint
* between topmost line ascent and bottommost line descent is at origin.
* @see @ref Alignment::MiddleCenterGlyphBounds,
* @ref Alignment::MiddleRightIntegral
*/ */
MiddleCenter = Implementation::AlignmentMiddle|Implementation::AlignmentCenter, MiddleCenter = Implementation::AlignmentMiddle|Implementation::AlignmentCenter,
/** /**
* Text end and vertical middle is at origin * Midpoint between leftmost and rightmost cursor position and a midpoint
* * between topmost line ascent and bottommost line descent is at origin,
* @see @ref Alignment::MiddleRightIntegral * with both the horizontal and vertical offset rounded to whole units.
* @see @ref Alignment::MiddleCenter,
* @ref Alignment::MiddleCenterGlyphBoundsIntegral
*/
MiddleCenterIntegral = MiddleCenter|Implementation::AlignmentIntegral,
/**
* Horizontal and vertical center of the glyph bounding rectangle is at
* origin.
* @see @ref Alignment::MiddleCenter,
* @ref Alignment::MiddleCenterGlyphBoundsIntegral
* @m_since_latest
*/
MiddleCenterGlyphBounds = MiddleCenter|Implementation::AlignmentGlyphBounds,
/**
* Horizontal and vertical center of the glyph bounding rectangle is at
* origin, with both the horizontal and vertical offset rounded to whole
* units.
* @see @ref Alignment::MiddleCenterGlyphBounds,
* @ref Alignment::MiddleCenterIntegral
* @m_since_latest
*/
MiddleCenterGlyphBoundsIntegral = MiddleCenterGlyphBounds|Implementation::AlignmentIntegral,
/**
* Rightmost cursor position and a midpoint between topmost line ascent and
* bottommost line descent is at origin.
* @see @ref Alignment::MiddleRightGlyphBounds,
* @ref Alignment::MiddleRightIntegral
*/ */
MiddleRight = Implementation::AlignmentMiddle|Implementation::AlignmentRight, MiddleRight = Implementation::AlignmentMiddle|Implementation::AlignmentRight,
/** Text start and top is at origin */ /**
TopLeft = Implementation::AlignmentTop|Implementation::AlignmentLeft, * Rightmost cursor position and a midpoint between topmost line ascent and
* bottommost line descent is at origin, with the vertical offset rounded
* to whole units.
* @see @ref Alignment::MiddleRight,
* @ref Alignment::MiddleRightGlyphBoundsIntegral
*/
MiddleRightIntegral = MiddleRight|Implementation::AlignmentIntegral,
/** /**
* Text center and top is at origin * Right side and vertical center of the glyph bounding rectangle is at
* * origin.
* @see @ref Alignment::TopCenterIntegral * @see @ref Alignment::MiddleRight,
* @ref Alignment::MiddleRightGlyphBoundsIntegral
* @m_since_latest
*/ */
TopCenter = Implementation::AlignmentTop|Implementation::AlignmentCenter, MiddleRightGlyphBounds = MiddleRight|Implementation::AlignmentGlyphBounds,
/** Text end and top is at origin */ /**
TopRight = Implementation::AlignmentTop|Implementation::AlignmentRight, * Right side and vertical center of the glyph bounding rectangle is at
* origin, with the vertical offset rounded to whole units.
* @see @ref Alignment::MiddleRightGlyphBounds,
* @ref Alignment::MiddleRightIntegral
* @m_since_latest
*/
MiddleRightGlyphBoundsIntegral = MiddleRightGlyphBounds|Implementation::AlignmentIntegral,
/** /**
* Text center and line is at origin and alignment offset is integral * Leftmost cursor position and topmost line ascent is at origin.
* * @see @ref Alignment::TopLeftGlyphBounds
* @see @ref Alignment::LineCenter
*/ */
LineCenterIntegral = Implementation::AlignmentLine|Implementation::AlignmentCenter|Implementation::AlignmentIntegral, TopLeft = Implementation::AlignmentTop|Implementation::AlignmentLeft,
/** /**
* Text start and vertical middle is at origin and alignment offset is integral * Top left corner of the glyph bounding rectangle is at origin.
* * @see @ref Alignment::TopLeft
* @see @ref Alignment::MiddleLeft * @m_since_latest
*/ */
MiddleLeftIntegral = Implementation::AlignmentMiddle|Implementation::AlignmentLeft|Implementation::AlignmentIntegral, TopLeftGlyphBounds = TopLeft|Implementation::AlignmentGlyphBounds,
/** /**
* Text center and vertical middle is at origin and alignment offset is integral * Midpoint between leftmost and rightmost cursor position and topmost line
* * ascent is at origin.
* @see @ref Alignment::MiddleCenter * @see @ref Alignment::TopCenterGlyphBounds,
* @ref Alignment::TopCenterIntegral
*/ */
MiddleCenterIntegral = Implementation::AlignmentMiddle|Implementation::AlignmentCenter|Implementation::AlignmentIntegral, TopCenter = Implementation::AlignmentTop|Implementation::AlignmentCenter,
/** /**
* Text end and vertical middle is at origin and alignment offset is integral * Midpoint between leftmost and rightmost cursor position and topmost line
* * ascent is at origin, with the horizontal offset rounded to whole units.
* @see @ref Alignment::MiddleRight * @see @ref Alignment::TopCenter,
* @ref Alignment::TopCenterGlyphBoundsIntegral
* @m_since_latest
*/
TopCenterIntegral = TopCenter|Implementation::AlignmentIntegral,
/**
* Horizontal center and top side of the glyph bounding rectangle is at
* origin.
* @see @ref Alignment::TopCenter,
* @ref Alignment::TopCenterGlyphBoundsIntegral
* @m_since_latest
*/ */
MiddleRightIntegral = Implementation::AlignmentMiddle|Implementation::AlignmentRight|Implementation::AlignmentIntegral, TopCenterGlyphBounds = TopCenter|Implementation::AlignmentGlyphBounds,
/** /**
* Text center and line is at origin and alignment offset is integral * Horizontal center and top side of the glyph bounding rectangle is at
* * origin, with the horizontal offset rounded to whole units.
* @see @ref Alignment::TopCenter * @see @ref Alignment::TopCenterGlyphBounds,
* @ref Alignment::TopCenterIntegral
* @m_since_latest * @m_since_latest
*/ */
TopCenterIntegral = Implementation::AlignmentTop|Implementation::AlignmentCenter|Implementation::AlignmentIntegral, TopCenterGlyphBoundsIntegral = TopCenterGlyphBounds|Implementation::AlignmentIntegral,
/**
* Rightmost cursor position and topmost line ascent is at origin.
* @see @ref Alignment::TopRightGlyphBounds
*/
TopRight = Implementation::AlignmentTop|Implementation::AlignmentRight,
/**
* Top right corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::TopRight
* @m_since_latest
*/
TopRightGlyphBounds = TopRight|Implementation::AlignmentGlyphBounds,
}; };
}} }}

61
src/Magnum/Text/Renderer.cpp

@ -137,8 +137,15 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
issue arises. */ issue arises. */
CORRADE_INTERNAL_ASSERT(vertices.size() + shaper->glyphCount()*4 <= vertices.capacity()); CORRADE_INTERNAL_ASSERT(vertices.size() + shaper->glyphCount()*4 <= vertices.capacity());
/* Bounds of rendered line */ /* Bounds of rendered line. If `Alignment::*GlyphBounds` is used, it's
filled with actual bounds of each glyph, otherwise with
ascent/descent and actual cursor range. */
Range2D lineRectangle; Range2D lineRectangle;
/** @todo this assumes horizontal direction, update when vertical text
is possible & testable */
if(!(UnsignedByte(alignment) & Implementation::AlignmentGlyphBounds))
lineRectangle = {linePosition + Vector2::yAxis(font.descent()*scale),
linePosition + Vector2::yAxis(font.ascent()*scale)};
/* Create quads for all glyphs */ /* Create quads for all glyphs */
Vector2 cursorPosition(linePosition); Vector2 cursorPosition(linePosition);
@ -174,34 +181,46 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
{quadPosition.bottomRight(), quadTextureCoordinates.bottomRight()} {quadPosition.bottomRight(), quadTextureCoordinates.bottomRight()}
}); });
/* Extend the line rectangle with current quad bounds. If the
original is zero size, it gets replaced. */
lineRectangle = Math::join(lineRectangle, quadPosition);
/* Advance cursor position to next character, again scaled */ /* Advance cursor position to next character, again scaled */
cursorPosition += glyphs[i].advance*scale; cursorPosition += glyphs[i].advance*scale;
/* Extend the line rectangle with current glyph bounds if
`Alignment::*GlyphBounds` is used, otherwise just expand with
the cursor range. */
if(UnsignedByte(alignment) & Implementation::AlignmentGlyphBounds) {
/* If the original is zero size, it gets replaced */
lineRectangle = Math::join(lineRectangle, quadPosition);
} else {
/** @todo this assumes left-to-right direction, update when
when vertical text is possible & testable */
lineRectangle.max() = Math::max(lineRectangle.max(), cursorPosition);
}
} }
/** @todo What about top-down text? */ /** @todo What about top-down text? */
/* Horizontally align the rendered line */ /* Horizontally align the rendered line. As we have the `lineRectangle`
already appropriate based on presence of `Alignment::*GlyphBounds`,
we don't need to special-case it here in any way. */
Float alignmentOffsetX = 0.0f; Float alignmentOffsetX = 0.0f;
if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter) if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentLeft)
alignmentOffsetX = -lineRectangle.left();
else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter) {
alignmentOffsetX = -lineRectangle.centerX(); alignmentOffsetX = -lineRectangle.centerX();
/* Integer alignment */
if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
alignmentOffsetX = Math::round(alignmentOffsetX);
}
else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight) else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight)
alignmentOffsetX = -lineRectangle.right(); alignmentOffsetX = -lineRectangle.right();
/* Integer alignment */
if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
alignmentOffsetX = Math::round(alignmentOffsetX);
/* Align positions and bounds on current line */ /* Align positions and bounds on current line */
lineRectangle = lineRectangle.translated(Vector2::xAxis(alignmentOffsetX)); lineRectangle = lineRectangle.translated(Vector2::xAxis(alignmentOffsetX));
for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it) for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it)
it->position.x() += alignmentOffsetX; it->position.x() += alignmentOffsetX;
/* Extend the rectangle with final line bounds, similarly to what was /* Extend the rectangle with final line bounds. This is again the same
done for each glyph above */ code path for both with and without `Alignment::*GlyphBounds`. */
rectangle = Math::join(rectangle, lineRectangle); rectangle = Math::join(rectangle, lineRectangle);
/* Move to next line */ /* Move to next line */
@ -210,17 +229,21 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
lastLineLastVertex = vertices.size(), lastLineLastVertex = vertices.size(),
pos != std::string::npos); pos != std::string::npos);
/* Vertically align the rendered text */ /* Vertically align the rendered text. Again, as we had the input rects
already appropriate based on presence of `Alignment::*GlyphBounds`, we
don't need to special-case it here in any way either. */
Float alignmentOffsetY = 0.0f; Float alignmentOffsetY = 0.0f;
if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle) if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentBottom)
alignmentOffsetY = -rectangle.bottom();
else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle) {
alignmentOffsetY = -rectangle.centerY(); alignmentOffsetY = -rectangle.centerY();
/* Integer alignment */
if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
alignmentOffsetY = Math::round(alignmentOffsetY);
}
else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentTop) else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentTop)
alignmentOffsetY = -rectangle.top(); alignmentOffsetY = -rectangle.top();
/* Integer alignment */
if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
alignmentOffsetY = Math::round(alignmentOffsetY);
/* Align positions and bounds */ /* Align positions and bounds */
rectangle = rectangle.translated(Vector2::yAxis(alignmentOffsetY)); rectangle = rectangle.translated(Vector2::yAxis(alignmentOffsetY));
for(auto& v: vertices) v.position.y() += alignmentOffsetY; for(auto& v: vertices) v.position.y() += alignmentOffsetY;

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

@ -57,30 +57,64 @@ struct RendererGLTest: GL::OpenGLTester {
}; };
const struct { const struct {
const char* name; TestSuite::TestCaseDescriptionSourceLocation name;
Alignment alignment; Alignment alignment;
Vector2 offset; Vector2 offset;
} RenderDataData[]{ } RenderDataData[]{
/* Not testing all combinations, just making sure that each horizontal, /* Not testing all combinations, just making sure that each horizontal,
vertical and integer variant is covered */ vertical, glyph bounds and integer variant is covered */
{"line left", Alignment::LineLeft, {"line left", Alignment::LineLeft,
/* This is the default (0) value, thus should result in no shift */
{}}, {}},
/** @todo for all these, the initial glyph offset is first subtracted and {"line left, glyph bounds", Alignment::LineLeftGlyphBounds,
only then the shift by either half or full size is performed, does that /* The first glyph has X offset of 2.5, which is subtracted */
make sense? why is not done for the LineLeft case, then? */ {-2.5f, 0.0f}},
{"top center, integral", Alignment::TopCenterIntegral,
/* The Y shift is rounded to whole units */
{-8.0f, -11.0f}},
{"top left", Alignment::TopLeft, {"top left", Alignment::TopLeft,
{0.0f, -10.5f}}, /* Ascent is 4.5, scaled by 0.5 */
{0.0f, -2.25f}},
{"top left, glyph bounds", Alignment::TopLeftGlyphBounds,
/* Largest Y value is 10.5f */
{-2.5f, -10.5f}},
{"top right", Alignment::TopRight, {"top right", Alignment::TopRight,
/* Advances were 1, 2, 3, so 6 in total, ascent is 4.5; scaled by
0.5 */
{-3.0f, -2.25f}},
{"top right, glyph bounds", Alignment::TopRightGlyphBounds,
/* Basically subtracting the largest vertex value */
{-12.5f, -10.5f}}, {-12.5f, -10.5f}},
{"top center", Alignment::TopCenter,
/* Advances were 1, 2, 3, so 6 in total, center is 3, scaled by 0.5 */
{-1.5f, -2.25f}},
{"top center, integral", Alignment::TopCenterIntegral,
/* The Y shift isn't whole units but only X is rounded here */
{-2.0f, -2.25f}},
{"top center, glyph bounds", Alignment::TopCenterGlyphBounds,
{-7.5f, -10.5f}},
{"top center, glyph bounds, integral", Alignment::TopCenterGlyphBoundsIntegral,
/* The Y shift isn't whole units but only X is rounded here */
{-8.0f, -10.5f}},
{"middle left, glyph bounds", Alignment::MiddleLeftGlyphBounds,
{-2.5f, -7.125f}},
{"middle left, glyph bounds, integral", Alignment::MiddleLeftGlyphBoundsIntegral,
/* The X shift isn't whole units but only Y is rounded here */
{-2.5f, -7.0f}},
{"middle center", Alignment::MiddleCenter, {"middle center", Alignment::MiddleCenter,
{-1.5f, -0.5f}},
{"middle center, integral", Alignment::MiddleCenterIntegral,
/* Rounding happens on both X and Y in this case */
{-2.0f, -1.0f}},
{"middle center, glyph bounds", Alignment::MiddleCenterGlyphBounds,
/* Half size of the bounds quad */ /* Half size of the bounds quad */
{-7.5f, -7.125f}}, {-7.5f, -7.125f}},
{"middle center, integral", Alignment::MiddleCenterIntegral, {"middle center, glyph bounds, integral", Alignment::MiddleCenterGlyphBoundsIntegral,
/* The X shift is rounded to whole units */
{-8.0f, -7.0f}}, {-8.0f, -7.0f}},
{"bottom left", Alignment::BottomLeft,
/* Descent is -2.5; scaled by 0.5 */
{0.0f, 1.25f}},
{"bottom right", Alignment::BottomRight,
{-3.0f, 1.25f}},
{"bottom right, glyph bounds", Alignment::BottomRightGlyphBounds,
{-12.5f, -3.75f}},
}; };
const struct { const struct {
@ -91,34 +125,68 @@ const struct {
Vector2 offset0, offset1, offset2; Vector2 offset0, offset1, offset2;
} MultilineData[]{ } MultilineData[]{
{"line left", Alignment::LineLeft, {"line left", Alignment::LineLeft,
{0.0f, -0.0f},
{0.0f, -4.0f},
{0.0f, -12.0f}},
{"line left, glyph bounds", Alignment::LineLeftGlyphBounds,
{0.0f, 0.0f}, {0.0f, 0.0f},
{0.0f, -4.0f}, {0.0f, -4.0f},
{0.0f, -12.0f}}, {0.0f, -12.0f}},
{"middle left", Alignment::MiddleLeft, {"middle left", Alignment::MiddleLeft,
{0.0f, 6.0f},
{0.0f, 2.0f},
{0.0f, -6.0f}},
{"middle left, glyph bounds", Alignment::MiddleLeftGlyphBounds,
{0.0f, 5.5f}, {0.0f, 5.5f},
{0.0f, 1.5f}, {0.0f, 1.5f},
{0.0f, -6.5f}}, {0.0f, -6.5f}},
{"middle left, integral", Alignment::MiddleLeftIntegral, {"middle left, glyph bounds, integral", Alignment::MiddleLeftGlyphBoundsIntegral,
{0.0f, 6.0f}, {0.0f, 6.0f},
{0.0f, 2.0f}, {0.0f, 2.0f},
{0.0f, -6.0f}}, {0.0f, -6.0f}},
{"middle center", Alignment::MiddleCenter, {"middle center", Alignment::MiddleCenter,
/* The advance for the rightmost glyph is one unit larger than the
actual bounds which makes it different */
{-4.0f, 6.0f},
{-2.0f, 2.0f},
{-3.0f, -6.0f}},
{"middle center, integral", Alignment::MiddleCenterIntegral,
{-4.0f, 6.0f},
{-2.0f, 2.0f},
{-3.0f, -6.0f}},
{"middle center, glyph bounds", Alignment::MiddleCenterGlyphBounds,
{-3.5f, 5.5f}, {-3.5f, 5.5f},
{-1.5f, 1.5f}, {-1.5f, 1.5f},
{-2.5f, -6.5f}}, {-2.5f, -6.5f}},
{"middle center, integral", Alignment::MiddleCenterIntegral, {"middle center, glyph bounds, integral", Alignment::MiddleCenterGlyphBoundsIntegral,
{-4.0f, 6.0f}, {-4.0f, 6.0f},
{-2.0f, 2.0f}, {-2.0f, 2.0f},
{-3.0f, -6.0f}}, {-3.0f, -6.0f}},
{"top right", Alignment::TopRight, {"top right", Alignment::TopRight,
{-8.0f, -0.5f},
{-4.0f, -4.5f},
{-6.0f, -12.5f}},
{"top right, glyph bounds", Alignment::TopRightGlyphBounds,
{-7.0f, -1.0f}, {-7.0f, -1.0f},
{-3.0f, -5.0f}, {-3.0f, -5.0f},
{-5.0f, -13.0f}}, {-5.0f, -13.0f}},
{"top center", Alignment::TopCenter, {"top center", Alignment::TopCenter,
/* The advance for the rightmost glyph is one unit larger than the
actual bounds which makes it different */
{-4.0f, -0.5f},
{-2.0f, -4.5f},
{-3.0f, -12.5f}},
{"top center, integral", Alignment::TopCenterIntegral,
/* The Y shift isn't whole units but only X (which is already whole
units) would be rounded here */
{-4.0f, -0.5f},
{-2.0f, -4.5f},
{-3.0f, -12.5f}},
{"top center, glyph bounds", Alignment::TopCenterGlyphBounds,
{-3.5f, -1.0f}, {-3.5f, -1.0f},
{-1.5f, -5.0f}, {-1.5f, -5.0f},
{-2.5f, -13.0f}}, {-2.5f, -13.0f}},
{"top center, integral", Alignment::TopCenterIntegral, {"top center, integral", Alignment::TopCenterGlyphBoundsIntegral,
{-4.0f, -1.0f}, {-4.0f, -1.0f},
{-2.0f, -5.0f}, {-2.0f, -5.0f},
{-3.0f, -13.0f}}, {-3.0f, -13.0f}},
@ -172,7 +240,8 @@ struct TestFont: AbstractFont {
Properties doOpenFile(Containers::StringView, Float size) override { Properties doOpenFile(Containers::StringView, Float size) override {
_opened = true; _opened = true;
return {size, 0.45f, -0.25f, 0.75f, 10}; /* Line height isn't used for anything here so can be arbitrary */
return {size, 4.5f, -2.5f, 10000.0f, 10};
} }
UnsignedInt doGlyphId(char32_t) override { return 0; } UnsignedInt doGlyphId(char32_t) override { return 0; }
@ -259,8 +328,12 @@ void RendererGLTest::renderData() {
Vector2{ 9.0f, 4.0f} + data.offset Vector2{ 9.0f, 4.0f} + data.offset
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
/* Bounds */ /* Bounds. Different depending on whether or not GlyphBounds alignment is
CORRADE_COMPARE(bounds, (Range2D{{2.5f, 3.75f}, {12.5f, 10.5f}}.translated(data.offset))); used. */
if(UnsignedByte(data.alignment) & Implementation::AlignmentGlyphBounds)
CORRADE_COMPARE(bounds, (Range2D{{2.5f, 3.75f}, {12.5f, 10.5f}}.translated(data.offset)));
else
CORRADE_COMPARE(bounds, (Range2D{{0.0f, -1.25f}, {3.0f, 2.25f}}.translated(data.offset)));
/* Texture coordinates. First glyph is bottom, second top left, third top /* Texture coordinates. First glyph is bottom, second top left, third top
right. right.
@ -318,12 +391,10 @@ void RendererGLTest::renderMesh() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
/* Alignment offset */ /* Alignment offset */
/** @todo same as in render(), figure out if the initial offset makes const Vector2 offset{-1.5f, -0.5f};
sense or not */
const Vector2 offset{-7.5f, -7.125f};
/* Bounds */ /* Bounds */
CORRADE_COMPARE(bounds, (Range2D{{2.5f, 3.75f}, {12.5f, 10.5f}}.translated(offset))); CORRADE_COMPARE(bounds, (Range2D{{0.0f, -1.25f}, {3.0f, 2.25f}}.translated(offset)));
/** @todo How to verify this on ES? */ /** @todo How to verify this on ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -446,12 +517,10 @@ void RendererGLTest::mutableText() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
/* Alignment offset */ /* Alignment offset */
/** @todo same as in render(), figure out if the initial offset makes const Vector2 offset{-1.5f, -0.5f};
sense or not */
const Vector2 offset{-7.5f, -7.125f};
/* Updated bounds and mesh vertex count */ /* Updated bounds and mesh vertex count */
CORRADE_COMPARE(renderer.rectangle(), (Range2D{{2.5f, 3.75f}, {12.5f, 10.5f} }.translated(offset))); CORRADE_COMPARE(renderer.rectangle(), (Range2D{{0.0f, -1.25f}, {3.0f, 2.25f}}.translated(offset)));
CORRADE_COMPARE(renderer.mesh().count(), 3*6); CORRADE_COMPARE(renderer.mesh().count(), 3*6);
/** @todo How to verify this on ES? */ /** @todo How to verify this on ES? */
@ -503,9 +572,9 @@ void RendererGLTest::multiline() {
Properties doOpenFile(Containers::StringView, Float size) override { Properties doOpenFile(Containers::StringView, Float size) override {
_opened = true; _opened = true;
/* The ascent and descent values shouldn't be used for anything /* Compared to the glyph bounds, which are from 0 to 2, this is
here and so can be completely arbitrary */ shifted by one unit, thus by 0.5 in the output */
return {size, -10000.0f, 1000.0f, 8.0f, 10}; return {size, 1.0f, -1.0f, 8.0f, 10};
} }
UnsignedInt doGlyphId(char32_t) override { return 0; } UnsignedInt doGlyphId(char32_t) override { return 0; }
@ -539,8 +608,12 @@ void RendererGLTest::multiline() {
CORRADE_COMPARE(font.size(), 0.5f); CORRADE_COMPARE(font.size(), 0.5f);
CORRADE_COMPARE(font.lineHeight(), 8.0f); CORRADE_COMPARE(font.lineHeight(), 8.0f);
/* Bounds */ /* Bounds. The advance for the rightmost glyph is one unit larger than the
CORRADE_COMPARE(rectangle, Range2D({0.0f, -12.0f}, {7.0f, 1.0f}).translated(data.offset0)); actual bounds so it's different on X between the two variants */
if(UnsignedByte(data.alignment) & Implementation::AlignmentGlyphBounds)
CORRADE_COMPARE(rectangle, Range2D({0.0f, -12.0f}, {7.0f, 1.0f}).translated(data.offset0));
else
CORRADE_COMPARE(rectangle, Range2D({0.0f, -12.5f}, {8.0f, 0.5f}).translated(data.offset0));
/* Vertices /* Vertices
[a] [b] [c] [d] [a] [b] [c] [d]

Loading…
Cancel
Save