Browse Source

Text: introduce Alignment::*Start and *End.

Based on the actual text direction (either explicitly set or detected),
these resolve to either *Left or *Right. For the Text::Renderer it's
done automatically inside (and there's no way to actually set the
direction from outside due to the API being ancient and limited), for
the align*() utils the alignment has to be explicitly resolved using a
new alignmentForDirection() utility.
pull/641/head
Vladimír Vondruš 2 years ago
parent
commit
bc33945d3d
  1. 5
      doc/changelog.dox
  2. 44
      src/Magnum/Text/Alignment.cpp
  3. 286
      src/Magnum/Text/Alignment.h
  4. 2
      src/Magnum/Text/CMakeLists.txt
  5. 36
      src/Magnum/Text/Renderer.cpp
  6. 10
      src/Magnum/Text/Renderer.h
  7. 82
      src/Magnum/Text/Test/AlignmentTest.cpp
  8. 2
      src/Magnum/Text/Test/CMakeLists.txt
  9. 108
      src/Magnum/Text/Test/RendererTest.cpp

5
doc/changelog.dox

@ -834,6 +834,11 @@ See also:
missing before, and @ref Text::Alignment::TopCenterIntegral, which is also missing before, and @ref Text::Alignment::TopCenterIntegral, which is also
needed in order to prevent aligning pixel-perfect fonts to fractional needed in order to prevent aligning pixel-perfect fonts to fractional
positions. positions.
- @ref Text::Alignment gained special `*Start` and `*End` values for
alignment based on text direction, and a new
@ref Text::alignmentForDirection() utility that resolves those to concrete
`*Left` and `*Right` values based @ref Text::LayoutDirection and
@ref Text::ShapeDirection
- Added a @ref Text::GlyphCache::GlyphCache(NoCreateT) and - Added a @ref Text::GlyphCache::GlyphCache(NoCreateT) and
@ref Text::DistanceFieldGlyphCache::DistanceFieldGlyphCache(NoCreateT) @ref Text::DistanceFieldGlyphCache::DistanceFieldGlyphCache(NoCreateT)
constructor allowing to construct the object without a GL context present constructor allowing to construct the object without a GL context present

44
src/Magnum/Text/Alignment.cpp

@ -25,8 +25,11 @@
#include "Alignment.h" #include "Alignment.h"
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h> #include <Corrade/Utility/Debug.h>
#include "Magnum/Text/Direction.h"
namespace Magnum { namespace Text { namespace Magnum { namespace Text {
Debug& operator<<(Debug& debug, const Alignment value) { Debug& operator<<(Debug& debug, const Alignment value) {
@ -43,6 +46,10 @@ Debug& operator<<(Debug& debug, const Alignment value) {
_c(LineCenterGlyphBoundsIntegral) _c(LineCenterGlyphBoundsIntegral)
_c(LineRight) _c(LineRight)
_c(LineRightGlyphBounds) _c(LineRightGlyphBounds)
_c(LineStart)
_c(LineStartGlyphBounds)
_c(LineEnd)
_c(LineEndGlyphBounds)
_c(BottomLeft) _c(BottomLeft)
_c(BottomLeftGlyphBounds) _c(BottomLeftGlyphBounds)
_c(BottomCenter) _c(BottomCenter)
@ -51,6 +58,10 @@ Debug& operator<<(Debug& debug, const Alignment value) {
_c(BottomCenterGlyphBoundsIntegral) _c(BottomCenterGlyphBoundsIntegral)
_c(BottomRight) _c(BottomRight)
_c(BottomRightGlyphBounds) _c(BottomRightGlyphBounds)
_c(BottomStart)
_c(BottomStartGlyphBounds)
_c(BottomEnd)
_c(BottomEndGlyphBounds)
_c(MiddleLeft) _c(MiddleLeft)
_c(MiddleLeftIntegral) _c(MiddleLeftIntegral)
_c(MiddleLeftGlyphBounds) _c(MiddleLeftGlyphBounds)
@ -63,6 +74,14 @@ Debug& operator<<(Debug& debug, const Alignment value) {
_c(MiddleRightIntegral) _c(MiddleRightIntegral)
_c(MiddleRightGlyphBounds) _c(MiddleRightGlyphBounds)
_c(MiddleRightGlyphBoundsIntegral) _c(MiddleRightGlyphBoundsIntegral)
_c(MiddleStart)
_c(MiddleStartIntegral)
_c(MiddleStartGlyphBounds)
_c(MiddleStartGlyphBoundsIntegral)
_c(MiddleEnd)
_c(MiddleEndIntegral)
_c(MiddleEndGlyphBounds)
_c(MiddleEndGlyphBoundsIntegral)
_c(TopLeft) _c(TopLeft)
_c(TopLeftGlyphBounds) _c(TopLeftGlyphBounds)
_c(TopCenter) _c(TopCenter)
@ -71,6 +90,10 @@ Debug& operator<<(Debug& debug, const Alignment value) {
_c(TopCenterGlyphBoundsIntegral) _c(TopCenterGlyphBoundsIntegral)
_c(TopRight) _c(TopRight)
_c(TopRightGlyphBounds) _c(TopRightGlyphBounds)
_c(TopStart)
_c(TopStartGlyphBounds)
_c(TopEnd)
_c(TopEndGlyphBounds)
#undef _c #undef _c
/* LCOV_EXCL_STOP */ /* LCOV_EXCL_STOP */
} }
@ -78,4 +101,25 @@ Debug& operator<<(Debug& debug, const Alignment value) {
return debug << "(" << Debug::nospace << Debug::hex << UnsignedByte(value) << Debug::nospace << ")"; return debug << "(" << Debug::nospace << Debug::hex << UnsignedByte(value) << Debug::nospace << ")";
} }
Alignment alignmentForDirection(const Alignment alignment, const LayoutDirection layoutDirection, const ShapeDirection shapeDirection) {
CORRADE_ASSERT(layoutDirection == LayoutDirection::HorizontalTopToBottom,
"Text::alignmentForDirection(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << layoutDirection, {});
CORRADE_ASSERT(shapeDirection != ShapeDirection::TopToBottom &&
shapeDirection != ShapeDirection::BottomToTop,
"Text::alignmentForDirection():" << shapeDirection << "is not supported yet, sorry", {});
const UnsignedByte horizontal = UnsignedByte(alignment) & Implementation::AlignmentHorizontal;
const UnsignedByte exceptHorizontal = UnsignedByte(alignment) & ~Implementation::AlignmentHorizontal;
if(horizontal == Implementation::AlignmentStart)
return Alignment((shapeDirection == ShapeDirection::RightToLeft ?
Implementation::AlignmentRight :
Implementation::AlignmentLeft)|exceptHorizontal);
if(horizontal == Implementation::AlignmentEnd)
return Alignment((shapeDirection == ShapeDirection::RightToLeft ?
Implementation::AlignmentLeft :
Implementation::AlignmentRight)|exceptHorizontal);
return alignment;
}
}} }}

286
src/Magnum/Text/Alignment.h

@ -26,10 +26,11 @@
*/ */
/** @file /** @file
* @brief Enum @ref Magnum::Text::Alignment * @brief Enum @ref Magnum::Text::Alignment, function @ref Magnum::Text::alignmentForDirection()
*/ */
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
#include "Magnum/Text/Text.h"
#include "Magnum/Text/visibility.h" #include "Magnum/Text/visibility.h"
namespace Magnum { namespace Text { namespace Magnum { namespace Text {
@ -42,16 +43,20 @@ namespace Implementation {
AlignmentLeft = 0, AlignmentLeft = 0,
AlignmentCenter = 1 << 0, AlignmentCenter = 1 << 0,
AlignmentRight = 2 << 0, AlignmentRight = 2 << 0,
AlignmentHorizontal = AlignmentLeft|AlignmentCenter|AlignmentRight, /* Start and End is Left or Right based on ShapeDirection, and possibly
also Top / Bottom eventually for vertical text */
AlignmentStart = 3 << 0,
AlignmentEnd = 4 << 0,
AlignmentHorizontal = AlignmentLeft|AlignmentCenter|AlignmentRight|AlignmentStart|AlignmentEnd,
AlignmentLine = 0, AlignmentLine = 0,
AlignmentBottom = 1 << 2, AlignmentBottom = 1 << 4,
AlignmentMiddle = 2 << 2, AlignmentMiddle = 2 << 4,
AlignmentTop = 3 << 2, AlignmentTop = 3 << 4,
AlignmentVertical = AlignmentLine|AlignmentBottom|AlignmentMiddle|AlignmentTop, AlignmentVertical = AlignmentLine|AlignmentBottom|AlignmentMiddle|AlignmentTop,
AlignmentIntegral = 1 << 4, AlignmentIntegral = 1 << 6,
AlignmentGlyphBounds = 1 << 5 AlignmentGlyphBounds = 1 << 7
}; };
} }
@ -71,19 +76,31 @@ positioned on whole pixels as well in order to avoid making them blurry. These
are only needed for `*Middle` and `*Center` alignments as they may result in are only needed for `*Middle` and `*Center` alignments as they may result in
the bounding rectangle to have odd dimensions, which would then result in the bounding rectangle to have odd dimensions, which would then result in
half-pixel shifts when divided by 2. half-pixel shifts when divided by 2.
@see @ref Renderer::render(), @ref Renderer::Renderer()
The `*Start` and `*End` values behave the same as `*Left` and `*Right`,
respectively, if @ref ShapeDirection::LeftToRight is passed to
@ref AbstractShaper::setDirection(), or if it's
@ref ShapeDirection::Unspecified and the particular font plugin doesn't detect
@ref ShapeDirection::RightToLeft when shaping. If
@ref ShapeDirection::RightToLeft is set (or detected for
@ref ShapeDirection::Unspecified), they're swapped, i.e. `*Start` becomes
`*Right` and `*End` becomes `*Left`.
@see @ref Renderer::render(), @ref Renderer::Renderer(),
@see @ref alignmentForDirection()
*/ */
enum class Alignment: UnsignedByte { enum class Alignment: UnsignedByte {
/** /**
* Leftmost cursor position and vertical line position is at origin. * Leftmost cursor position and vertical line position is at origin.
* @see @ref Alignment::LineLeftGlyphBounds * @see @ref Alignment::LineLeftGlyphBounds, @ref Alignment::LineStart,
* @ref Alignment::LineEnd
*/ */
LineLeft = Implementation::AlignmentLine|Implementation::AlignmentLeft, LineLeft = Implementation::AlignmentLine|Implementation::AlignmentLeft,
/** /**
* Left side of the glyph bounding rectangle and vertical line position is * Left side of the glyph bounding rectangle and vertical line position is
* at origin. * at origin.
* @see @ref Alignment::LineLeft * @see @ref Alignment::LineLeft, @ref Alignment::LineStartGlyphBounds,
* @ref Alignment::LineEndGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
LineLeftGlyphBounds = LineLeft|Implementation::AlignmentGlyphBounds, LineLeftGlyphBounds = LineLeft|Implementation::AlignmentGlyphBounds,
@ -126,28 +143,68 @@ enum class Alignment: UnsignedByte {
/** /**
* Rightmost cursor position and vertical line position is at origin. * Rightmost cursor position and vertical line position is at origin.
* @see @ref Alignment::LineRightGlyphBounds * @see @ref Alignment::LineRightGlyphBounds, @ref Alignment::LineEnd,
* @ref Alignment::LineStart
* @see @ref alignmentForDirection()
*/ */
LineRight = Implementation::AlignmentLine|Implementation::AlignmentRight, LineRight = Implementation::AlignmentLine|Implementation::AlignmentRight,
/** /**
* Right side of the glyph bounding rectangle and vertical line position is * Right side of the glyph bounding rectangle and vertical line position is
* at origin. * at origin.
* @see @ref Alignment::LineRight * @see @ref Alignment::LineRight, @ref Alignment::LineEndGlyphBounds,
* @ref Alignment::LineStartGlyphBounds
* @see @ref alignmentForDirection()
* @m_since_latest * @m_since_latest
*/ */
LineRightGlyphBounds = LineRight|Implementation::AlignmentGlyphBounds, LineRightGlyphBounds = LineRight|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::LineRight for @ref ShapeDirection::RightToLeft,
* @ref Alignment::LineLeft otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
LineStart = Implementation::AlignmentLine|Implementation::AlignmentStart,
/**
* @ref Alignment::LineRightGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::LineLeftGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
LineStartGlyphBounds = LineStart|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::LineLeft for @ref ShapeDirection::RightToLeft,
* @ref Alignment::LineRight otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
LineEnd = Implementation::AlignmentLine|Implementation::AlignmentEnd,
/**
* @ref Alignment::LineLeftGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::LineRightGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
LineEndGlyphBounds = LineEnd|Implementation::AlignmentGlyphBounds,
/** /**
* Leftmost cursor position and bottommost line descent is at origin. * Leftmost cursor position and bottommost line descent is at origin.
* @see @ref Alignment::BottomLeftGlyphBounds * @see @ref Alignment::BottomLeftGlyphBounds, @ref Alignment::BottomStart,
* @ref Alignment::BottomEnd
* @m_since_latest * @m_since_latest
*/ */
BottomLeft = Implementation::AlignmentBottom|Implementation::AlignmentLeft, BottomLeft = Implementation::AlignmentBottom|Implementation::AlignmentLeft,
/** /**
* Bottom left corner of the glyph bounding rectangle is at origin. * Bottom left corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::BottomLeft * @see @ref Alignment::BottomLeft, @ref Alignment::BottomStartGlyphBounds,
* @ref Alignment::BottomEndGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
BottomLeftGlyphBounds = BottomLeft|Implementation::AlignmentGlyphBounds, BottomLeftGlyphBounds = BottomLeft|Implementation::AlignmentGlyphBounds,
@ -191,23 +248,60 @@ enum class Alignment: UnsignedByte {
/** /**
* Rightmost cursor position and bottommost line descent is at origin. * Rightmost cursor position and bottommost line descent is at origin.
* @see @ref Alignment::BottomRightGlyphBounds * @see @ref Alignment::BottomRightGlyphBounds, @ref Alignment::BottomEnd,
* @ref Alignment::BottomStart
* @m_since_latest * @m_since_latest
*/ */
BottomRight = Implementation::AlignmentBottom|Implementation::AlignmentRight, BottomRight = Implementation::AlignmentBottom|Implementation::AlignmentRight,
/** /**
* Bottom right corner of the glyph bounding rectangle is at origin. * Bottom right corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::BottomRight * @see @ref Alignment::BottomRight, @ref Alignment::BottomEndGlyphBounds,
* @ref Alignment::BottomStartGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
BottomRightGlyphBounds = BottomRight|Implementation::AlignmentGlyphBounds, BottomRightGlyphBounds = BottomRight|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::BottomRight for @ref ShapeDirection::RightToLeft,
* @ref Alignment::BottomLeft otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
BottomStart = Implementation::AlignmentBottom|Implementation::AlignmentStart,
/**
* @ref Alignment::BottomRightGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::BottomLeftGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
BottomStartGlyphBounds = BottomStart|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::BottomLeft for @ref ShapeDirection::RightToLeft,
* @ref Alignment::BottomRight otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
BottomEnd = Implementation::AlignmentBottom|Implementation::AlignmentEnd,
/**
* @ref Alignment::BottomLeftGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::BottomRightGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
BottomEndGlyphBounds = BottomEnd|Implementation::AlignmentGlyphBounds,
/** /**
* Leftmost cursor position and a midpoint between topmost line ascent and * Leftmost cursor position and a midpoint between topmost line ascent and
* bottommost line descent is at origin. * bottommost line descent is at origin.
* @see @ref Alignment::MiddleLeftGlyphBounds, * @see @ref Alignment::MiddleLeftGlyphBounds,
* @ref Alignment::MiddleLeftIntegral * @ref Alignment::MiddleLeftIntegral, @ref Alignment::MiddleStart,
* @ref Alignment::MiddleEnd
*/ */
MiddleLeft = Implementation::AlignmentMiddle|Implementation::AlignmentLeft, MiddleLeft = Implementation::AlignmentMiddle|Implementation::AlignmentLeft,
@ -216,7 +310,9 @@ enum class Alignment: UnsignedByte {
* bottommost line descent is at origin, with the vertical offset rounded * bottommost line descent is at origin, with the vertical offset rounded
* to whole units. * to whole units.
* @see @ref Alignment::MiddleLeft, * @see @ref Alignment::MiddleLeft,
* @ref Alignment::MiddleLeftGlyphBoundsIntegral * @ref Alignment::MiddleLeftGlyphBoundsIntegral,
* @ref Alignment::MiddleStartIntegral,
* @ref Alignment::MiddleEndIntegral
*/ */
MiddleLeftIntegral = MiddleLeft|Implementation::AlignmentIntegral, MiddleLeftIntegral = MiddleLeft|Implementation::AlignmentIntegral,
@ -224,7 +320,9 @@ enum class Alignment: UnsignedByte {
* Left side and vertical center of the glyph bounding rectangle is at * Left side and vertical center of the glyph bounding rectangle is at
* origin. * origin.
* @see @ref Alignment::MiddleLeft, * @see @ref Alignment::MiddleLeft,
* @ref Alignment::MiddleLeftGlyphBoundsIntegral * @ref Alignment::MiddleLeftGlyphBoundsIntegral,
* @ref Alignment::MiddleStartGlyphBounds,
* @ref Alignment::MiddleEndGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
MiddleLeftGlyphBounds = MiddleLeft|Implementation::AlignmentGlyphBounds, MiddleLeftGlyphBounds = MiddleLeft|Implementation::AlignmentGlyphBounds,
@ -233,7 +331,9 @@ enum class Alignment: UnsignedByte {
* Left side and vertical center of the glyph bounding rectangle is at * Left side and vertical center of the glyph bounding rectangle is at
* origin, with the vertical offset rounded to whole units. * origin, with the vertical offset rounded to whole units.
* @see @ref Alignment::MiddleLeftGlyphBounds, * @see @ref Alignment::MiddleLeftGlyphBounds,
* @ref Alignment::MiddleLeftIntegral * @ref Alignment::MiddleLeftIntegral,
* @ref Alignment::MiddleStartGlyphBoundsIntegral,
* @ref Alignment::MiddleEndGlyphBoundsIntegral
* @m_since_latest * @m_since_latest
*/ */
MiddleLeftGlyphBoundsIntegral = MiddleLeftGlyphBounds|Implementation::AlignmentIntegral, MiddleLeftGlyphBoundsIntegral = MiddleLeftGlyphBounds|Implementation::AlignmentIntegral,
@ -278,7 +378,8 @@ enum class Alignment: UnsignedByte {
* Rightmost cursor position and a midpoint between topmost line ascent and * Rightmost cursor position and a midpoint between topmost line ascent and
* bottommost line descent is at origin. * bottommost line descent is at origin.
* @see @ref Alignment::MiddleRightGlyphBounds, * @see @ref Alignment::MiddleRightGlyphBounds,
* @ref Alignment::MiddleRightIntegral * @ref Alignment::MiddleRightIntegral, @ref Alignment::MiddleEnd,
* @ref Alignment::MiddleStart
*/ */
MiddleRight = Implementation::AlignmentMiddle|Implementation::AlignmentRight, MiddleRight = Implementation::AlignmentMiddle|Implementation::AlignmentRight,
@ -287,7 +388,9 @@ enum class Alignment: UnsignedByte {
* bottommost line descent is at origin, with the vertical offset rounded * bottommost line descent is at origin, with the vertical offset rounded
* to whole units. * to whole units.
* @see @ref Alignment::MiddleRight, * @see @ref Alignment::MiddleRight,
* @ref Alignment::MiddleRightGlyphBoundsIntegral * @ref Alignment::MiddleRightGlyphBoundsIntegral,
* @ref Alignment::MiddleEndIntegral,
* @ref Alignment::MiddleStartIntegral
*/ */
MiddleRightIntegral = MiddleRight|Implementation::AlignmentIntegral, MiddleRightIntegral = MiddleRight|Implementation::AlignmentIntegral,
@ -295,7 +398,9 @@ enum class Alignment: UnsignedByte {
* Right side and vertical center of the glyph bounding rectangle is at * Right side and vertical center of the glyph bounding rectangle is at
* origin. * origin.
* @see @ref Alignment::MiddleRight, * @see @ref Alignment::MiddleRight,
* @ref Alignment::MiddleRightGlyphBoundsIntegral * @ref Alignment::MiddleRightGlyphBoundsIntegral,
* @ref Alignment::MiddleEndGlyphBounds,
* @ref Alignment::MiddleStartGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
MiddleRightGlyphBounds = MiddleRight|Implementation::AlignmentGlyphBounds, MiddleRightGlyphBounds = MiddleRight|Implementation::AlignmentGlyphBounds,
@ -304,20 +409,94 @@ enum class Alignment: UnsignedByte {
* Right side and vertical center of the glyph bounding rectangle is at * Right side and vertical center of the glyph bounding rectangle is at
* origin, with the vertical offset rounded to whole units. * origin, with the vertical offset rounded to whole units.
* @see @ref Alignment::MiddleRightGlyphBounds, * @see @ref Alignment::MiddleRightGlyphBounds,
* @ref Alignment::MiddleRightIntegral * @ref Alignment::MiddleRightIntegral,
* @ref Alignment::MiddleEndGlyphBoundsIntegral,
* @ref Alignment::MiddleStartGlyphBoundsIntegral
* @m_since_latest * @m_since_latest
*/ */
MiddleRightGlyphBoundsIntegral = MiddleRightGlyphBounds|Implementation::AlignmentIntegral, MiddleRightGlyphBoundsIntegral = MiddleRightGlyphBounds|Implementation::AlignmentIntegral,
/**
* @ref Alignment::MiddleRight for @ref ShapeDirection::RightToLeft,
* @ref Alignment::MiddleLeft otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleStart = Implementation::AlignmentMiddle|Implementation::AlignmentStart,
/**
* @ref Alignment::MiddleRightIntegral for
* @ref ShapeDirection::RightToLeft, @ref Alignment::MiddleLeftIntegral
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleStartIntegral = MiddleStart|Implementation::AlignmentIntegral,
/**
* @ref Alignment::MiddleRightGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::MiddleLeftGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleStartGlyphBounds = MiddleStart|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::MiddleRightGlyphBoundsIntegral for
* @ref ShapeDirection::RightToLeft,
* @ref Alignment::MiddleLeftGlyphBoundsIntegral otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleStartGlyphBoundsIntegral = MiddleStartGlyphBounds|Implementation::AlignmentIntegral,
/**
* @ref Alignment::MiddleLeft for @ref ShapeDirection::RightToLeft,
* @ref Alignment::MiddleRight otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleEnd = Implementation::AlignmentMiddle|Implementation::AlignmentEnd,
/**
* @ref Alignment::MiddleLeftIntegral for
* @ref ShapeDirection::RightToLeft, @ref Alignment::MiddleRightIntegral
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleEndIntegral = MiddleEnd|Implementation::AlignmentIntegral,
/**
* @ref Alignment::MiddleLeftGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::MiddleRightGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleEndGlyphBounds = MiddleEnd|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::MiddleLeftGlyphBoundsIntegral for
* @ref ShapeDirection::RightToLeft,
* @ref Alignment::MiddleRightGlyphBoundsIntegral otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
MiddleEndGlyphBoundsIntegral = MiddleEndGlyphBounds|Implementation::AlignmentIntegral,
/** /**
* Leftmost cursor position and topmost line ascent is at origin. * Leftmost cursor position and topmost line ascent is at origin.
* @see @ref Alignment::TopLeftGlyphBounds * @see @ref Alignment::TopLeftGlyphBounds, @ref Alignment::TopStart,
* @ref Alignment::TopEnd
*/ */
TopLeft = Implementation::AlignmentTop|Implementation::AlignmentLeft, TopLeft = Implementation::AlignmentTop|Implementation::AlignmentLeft,
/** /**
* Top left corner of the glyph bounding rectangle is at origin. * Top left corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::TopLeft * @see @ref Alignment::TopLeft, @ref Alignment::TopStartGlyphBounds,
* @ref Alignment::TopEndGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
TopLeftGlyphBounds = TopLeft|Implementation::AlignmentGlyphBounds, TopLeftGlyphBounds = TopLeft|Implementation::AlignmentGlyphBounds,
@ -359,21 +538,72 @@ enum class Alignment: UnsignedByte {
/** /**
* Rightmost cursor position and topmost line ascent is at origin. * Rightmost cursor position and topmost line ascent is at origin.
* @see @ref Alignment::TopRightGlyphBounds * @see @ref Alignment::TopRightGlyphBounds, @ref Alignment::TopEnd,
* @ref Alignment::TopStart
*/ */
TopRight = Implementation::AlignmentTop|Implementation::AlignmentRight, TopRight = Implementation::AlignmentTop|Implementation::AlignmentRight,
/** /**
* Top right corner of the glyph bounding rectangle is at origin. * Top right corner of the glyph bounding rectangle is at origin.
* @see @ref Alignment::TopRight * @see @ref Alignment::TopRight, @ref Alignment::TopEndGlyphBounds,
* @ref Alignment::TopStartGlyphBounds
* @m_since_latest * @m_since_latest
*/ */
TopRightGlyphBounds = TopRight|Implementation::AlignmentGlyphBounds, TopRightGlyphBounds = TopRight|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::TopRight for @ref ShapeDirection::RightToLeft,
* @ref Alignment::TopLeft otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
TopStart = Implementation::AlignmentTop|Implementation::AlignmentStart,
/**
* @ref Alignment::TopRightGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::TopLeftGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
TopStartGlyphBounds = TopStart|Implementation::AlignmentGlyphBounds,
/**
* @ref Alignment::TopLeft for @ref ShapeDirection::RightToLeft,
* @ref Alignment::TopRight otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
TopEnd = Implementation::AlignmentTop|Implementation::AlignmentEnd,
/**
* @ref Alignment::TopLeftGlyphBounds for
* @ref ShapeDirection::RightToLeft, @ref Alignment::TopRightGlyphBounds
* otherwise.
* @see @ref alignmentForDirection()
* @m_since_latest
*/
TopEndGlyphBounds = TopEnd|Implementation::AlignmentGlyphBounds,
}; };
/** @debugoperatorenum{Alignment} */ /** @debugoperatorenum{Alignment} */
MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& debug, Alignment value); MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& debug, Alignment value);
/**
@brief Alignment for layout and shape direction
@m_since_latest
The @p layoutDirection is currently expected to always be
@ref LayoutDirection::HorizontalTopToBottom and @p shapeDirection never
@ref ShapeDirection::TopToBottom or @ref ShapeDirection::BottomToTop. Then, if
@p alignment is `*Start` or `*End`, it's converted to `*Left` or `*Right`,
respectively, if @p shapeDirection is @ref ShapeDirection::LeftToRight or
@ref ShapeDirection::Unspecified, and `*Right` or `*Left`, respectively, if
@p shapeDirection is @ref ShapeDirection::RightToLeft.
*/
MAGNUM_TEXT_EXPORT Alignment alignmentForDirection(Alignment alignment, LayoutDirection layoutDirection, ShapeDirection shapeDirection);
}} }}
#endif #endif

2
src/Magnum/Text/CMakeLists.txt

@ -31,7 +31,6 @@ find_package(Corrade REQUIRED PluginManager)
# Files shared between main library and unit test library # Files shared between main library and unit test library
set(MagnumText_SRCS set(MagnumText_SRCS
Alignment.cpp
Direction.cpp) Direction.cpp)
# Files compiled with different flags for main library and unit test library # Files compiled with different flags for main library and unit test library
@ -40,6 +39,7 @@ set(MagnumText_GracefulAssert_SRCS
AbstractFontConverter.cpp AbstractFontConverter.cpp
AbstractGlyphCache.cpp AbstractGlyphCache.cpp
AbstractShaper.cpp AbstractShaper.cpp
Alignment.cpp
Feature.cpp Feature.cpp
Renderer.cpp Renderer.cpp
Script.cpp) Script.cpp)

36
src/Magnum/Text/Renderer.cpp

@ -188,6 +188,10 @@ Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache, const Float scale,
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, {});
CORRADE_ASSERT(
(UnsignedByte(alignment) & Implementation::AlignmentHorizontal) != Implementation::AlignmentStart &&
(UnsignedByte(alignment) & Implementation::AlignmentHorizontal) != Implementation::AlignmentEnd,
"Text::alignRenderedLine():" << alignment << "has to be resolved to *Left / *Right before being passed to this function", {});
#ifdef CORRADE_NO_ASSERT #ifdef CORRADE_NO_ASSERT
static_cast<void>(direction); /** @todo drop once implemented */ static_cast<void>(direction); /** @todo drop once implemented */
#endif #endif
@ -218,6 +222,10 @@ Range2D alignRenderedLine(const Range2D& lineRectangle, const LayoutDirection di
Range2D alignRenderedBlock(const Range2D& blockRectangle, const LayoutDirection direction, const Alignment alignment, const Containers::StridedArrayView1D<Vector2>& positions) { Range2D alignRenderedBlock(const Range2D& blockRectangle, const LayoutDirection direction, const Alignment alignment, const Containers::StridedArrayView1D<Vector2>& positions) {
CORRADE_ASSERT(direction == LayoutDirection::HorizontalTopToBottom, CORRADE_ASSERT(direction == LayoutDirection::HorizontalTopToBottom,
"Text::alignRenderedBlock(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << direction, {}); "Text::alignRenderedBlock(): only" << LayoutDirection::HorizontalTopToBottom << "is supported right now, got" << direction, {});
CORRADE_ASSERT(
(UnsignedByte(alignment) & Implementation::AlignmentHorizontal) != Implementation::AlignmentStart &&
(UnsignedByte(alignment) & Implementation::AlignmentHorizontal) != Implementation::AlignmentEnd,
"Text::alignRenderedBlock():" << alignment << "has to be resolved to *Left / *Right before being passed to this function", {});
#ifdef CORRADE_NO_ASSERT #ifdef CORRADE_NO_ASSERT
static_cast<void>(direction); /** @todo drop once implemented */ static_cast<void>(direction); /** @todo drop once implemented */
#endif #endif
@ -335,6 +343,12 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
/** @todo even with reusing a shaper this is all horrific, rework!! */ /** @todo even with reusing a shaper this is all horrific, rework!! */
Containers::Pointer<AbstractShaper> shaper = font.createShaper(); Containers::Pointer<AbstractShaper> shaper = font.createShaper();
/* Start/End alignment resolved based on what the shaper detects for the
first line. Not great, but can't do much better with this old limited
API. */
/** @todo rework all this, again */
Containers::Optional<Alignment> resolvedAlignment;
/* Render each line separately and align it horizontally */ /* Render each line separately and align it horizontally */
std::size_t pos, prevPos = 0; std::size_t pos, prevPos = 0;
do { do {
@ -404,14 +418,30 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
lineVertices.slice(&Vertex::position), lineVertices.slice(&Vertex::position),
lineVertices.slice(&Vertex::textureCoordinates)); lineVertices.slice(&Vertex::textureCoordinates));
/* Resolve the alignment based on what the shaper detected (if
anything). Assume there are no font plugins that would produce
vertical shape direction by default. */
/** @todo drop all this once the shaper instance is configurable from
outside */
if(!resolvedAlignment) {
const ShapeDirection shapeDirection = shaper->direction();
CORRADE_INTERNAL_ASSERT(
shapeDirection != ShapeDirection::TopToBottom &&
shapeDirection != ShapeDirection::BottomToTop);
resolvedAlignment = alignmentForDirection(alignment,
/** @todo direction hardcoded here */
LayoutDirection::HorizontalTopToBottom,
shapeDirection);
}
/* Horizontally align the line, using either of the rectangles based on /* Horizontally align the line, using either of the rectangles based on
which alignment is desired */ which alignment is desired */
const Range2D alignedLineRectangle = alignRenderedLine( const Range2D alignedLineRectangle = alignRenderedLine(
UnsignedByte(alignment) & Implementation::AlignmentGlyphBounds ? UnsignedByte(*resolvedAlignment) & Implementation::AlignmentGlyphBounds ?
lineQuadRectangle : lineRectangle, lineQuadRectangle : lineRectangle,
/** @todo direction hardcoded here */ /** @todo direction hardcoded here */
LayoutDirection::HorizontalTopToBottom, LayoutDirection::HorizontalTopToBottom,
alignment, *resolvedAlignment,
lineVertices.slice(&Vertex::position)); lineVertices.slice(&Vertex::position));
/* Extend the rectangle with final line bounds */ /* Extend the rectangle with final line bounds */
@ -426,7 +456,7 @@ std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& fo
rectangle, rectangle,
/** @todo direction hardcoded here */ /** @todo direction hardcoded here */
LayoutDirection::HorizontalTopToBottom, LayoutDirection::HorizontalTopToBottom,
alignment, *resolvedAlignment,
Containers::stridedArrayView(vertices).slice(&Vertex::position)); Containers::stridedArrayView(vertices).slice(&Vertex::position));
return std::make_tuple(Utility::move(vertices), alignedRectangle); return std::make_tuple(Utility::move(vertices), alignedRectangle);

10
src/Magnum/Text/Renderer.h

@ -212,6 +212,11 @@ MAGNUM_TEXT_EXPORT Range2D renderGlyphQuadsInto(const AbstractGlyphCache& cache,
the alignment. the alignment.
@m_since_latest @m_since_latest
Expects that @p alignment isn't `*Start` or `*End`, those values have to be
resolved to `*Left` or `*Right` based on desired or detected
@ref ShapeDirection using @ref alignmentForDirection() before being passed to
this function.
If @p alignment isn't `*GlyphBounds`, this function should get glyph If @p alignment isn't `*GlyphBounds`, this function should get glyph
@p positions for the whole line coming from @ref renderLineGlyphPositionsInto() @p positions for the whole line coming from @ref renderLineGlyphPositionsInto()
and @p lineRectangle being all rectangles returned by that function combined and @p lineRectangle being all rectangles returned by that function combined
@ -244,6 +249,11 @@ MAGNUM_TEXT_EXPORT Range2D alignRenderedLine(const Range2D& lineRectangle, Layou
advance based on the alignment. advance based on the alignment.
@m_since_latest @m_since_latest
Expects that @p alignment isn't `*Start` or `*End`, those values have to be
resolved to `*Left` or `*Right` based on desired or detected
@ref ShapeDirection using @ref alignmentForDirection() before being passed to
this function.
This function should get glyph or vertex @p positions for all lines as aligned This function should get glyph or vertex @p positions for all lines as aligned
by calls to @ref alignRenderedLine(), and @p blockRectangle being all line by calls to @ref alignRenderedLine(), and @p blockRectangle being all line
rectangles returned by that function combined together with @ref Math::join(). rectangles returned by that function combined together with @ref Math::join().

82
src/Magnum/Text/Test/AlignmentTest.cpp

@ -24,10 +24,13 @@
*/ */
#include <sstream> #include <sstream>
#include <Corrade/Containers/StringStl.h> /** @todo remove once Debug is stream-free */
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is stream-free */ #include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is stream-free */
#include "Magnum/Text/Alignment.h" #include "Magnum/Text/Alignment.h"
#include "Magnum/Text/Direction.h"
namespace Magnum { namespace Text { namespace Test { namespace { namespace Magnum { namespace Text { namespace Test { namespace {
@ -35,10 +38,16 @@ struct AlignmentTest: TestSuite::Tester {
explicit AlignmentTest(); explicit AlignmentTest();
void debug(); void debug();
void forDirection();
void forDirectionInvalid();
}; };
AlignmentTest::AlignmentTest() { AlignmentTest::AlignmentTest() {
addTests({&AlignmentTest::debug}); addTests({&AlignmentTest::debug,
&AlignmentTest::forDirection,
&AlignmentTest::forDirectionInvalid});
} }
void AlignmentTest::debug() { void AlignmentTest::debug() {
@ -47,6 +56,77 @@ void AlignmentTest::debug() {
CORRADE_COMPARE(out.str(), "Text::Alignment::MiddleRightGlyphBounds Text::Alignment(0xab)\n"); CORRADE_COMPARE(out.str(), "Text::Alignment::MiddleRightGlyphBounds Text::Alignment(0xab)\n");
} }
void AlignmentTest::forDirection() {
/* For alignment that's neither Start nor End it's just a passthrough */
CORRADE_COMPARE(alignmentForDirection(
Alignment::BottomRightGlyphBounds,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::RightToLeft
), Alignment::BottomRightGlyphBounds);
CORRADE_COMPARE(alignmentForDirection(
Alignment::MiddleLeftIntegral,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::RightToLeft
), Alignment::MiddleLeftIntegral);
CORRADE_COMPARE(alignmentForDirection(
Alignment::TopCenter,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::LeftToRight
), Alignment::TopCenter);
/* For Start / End it resolves based on ShapeDirection, keeping all extra
bits as well */
CORRADE_COMPARE(alignmentForDirection(
Alignment::TopStart,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::LeftToRight
), Alignment::TopLeft);
CORRADE_COMPARE(alignmentForDirection(
Alignment::MiddleEndIntegral,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::LeftToRight
), Alignment::MiddleRightIntegral);
CORRADE_COMPARE(alignmentForDirection(
Alignment::MiddleStartGlyphBoundsIntegral,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::RightToLeft
), Alignment::MiddleRightGlyphBoundsIntegral);
CORRADE_COMPARE(alignmentForDirection(
Alignment::LineEndGlyphBounds,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::RightToLeft
), Alignment::LineLeftGlyphBounds);
/* Unspecified ShapeDirection behaves same as LeftToRight */
CORRADE_COMPARE(alignmentForDirection(
Alignment::BottomStart,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::Unspecified
), Alignment::BottomLeft);
CORRADE_COMPARE(alignmentForDirection(
Alignment::TopEndGlyphBounds,
LayoutDirection::HorizontalTopToBottom,
ShapeDirection::Unspecified
), Alignment::TopRightGlyphBounds);
}
void AlignmentTest::forDirectionInvalid() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
/* It should blow up also for alignments that don't use Start or End, for
consistency */
alignmentForDirection(Alignment::BottomCenter, LayoutDirection::VerticalRightToLeft, ShapeDirection::Unspecified);
alignmentForDirection(Alignment::MiddleCenterIntegral, LayoutDirection::HorizontalTopToBottom, ShapeDirection::TopToBottom);
alignmentForDirection(Alignment::MiddleCenterIntegral, LayoutDirection::HorizontalTopToBottom, ShapeDirection::BottomToTop);
CORRADE_COMPARE_AS(out.str(),
"Text::alignmentForDirection(): only Text::LayoutDirection::HorizontalTopToBottom is supported right now, got Text::LayoutDirection::VerticalRightToLeft\n"
"Text::alignmentForDirection(): Text::ShapeDirection::TopToBottom is not supported yet, sorry\n"
"Text::alignmentForDirection(): Text::ShapeDirection::BottomToTop is not supported yet, sorry\n",
TestSuite::Compare::String);
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::AlignmentTest) CORRADE_TEST_MAIN(Magnum::Text::Test::AlignmentTest)

2
src/Magnum/Text/Test/CMakeLists.txt

@ -77,7 +77,7 @@ endif()
corrade_add_test(TextAbstractShaperTest AbstractShaperTest.cpp corrade_add_test(TextAbstractShaperTest AbstractShaperTest.cpp
LIBRARIES MagnumTextTestLib) LIBRARIES MagnumTextTestLib)
corrade_add_test(TextAlignmentTest AlignmentTest.cpp LIBRARIES MagnumText) corrade_add_test(TextAlignmentTest AlignmentTest.cpp LIBRARIES MagnumTextTestLib)
corrade_add_test(TextDirectionTest DirectionTest.cpp LIBRARIES MagnumText) corrade_add_test(TextDirectionTest DirectionTest.cpp LIBRARIES MagnumText)
corrade_add_test(TextFeatureTest FeatureTest.cpp LIBRARIES MagnumTextTestLib) corrade_add_test(TextFeatureTest FeatureTest.cpp LIBRARIES MagnumTextTestLib)
corrade_add_test(TextRendererTest RendererTest.cpp LIBRARIES MagnumTextTestLib) corrade_add_test(TextRendererTest RendererTest.cpp LIBRARIES MagnumTextTestLib)

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

@ -122,62 +122,106 @@ const struct {
const struct { const struct {
TestSuite::TestCaseDescriptionSourceLocation name; TestSuite::TestCaseDescriptionSourceLocation name;
Alignment alignment; Alignment alignment;
ShapeDirection shapeDirection;
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, glyph bounds and integer variant is covered */ vertical, glyph bounds and integer variant is covered */
{"line left", Alignment::LineLeft, {"line left",
Alignment::LineLeft, ShapeDirection::Unspecified,
/* This is the default (0) value, thus should result in no shift */ /* This is the default (0) value, thus should result in no shift */
{}}, {}},
{"line left, glyph bounds", Alignment::LineLeftGlyphBounds, {"line left, glyph bounds",
Alignment::LineLeftGlyphBounds, ShapeDirection::Unspecified,
/* The first glyph has X offset of 2.5, which is subtracted */ /* The first glyph has X offset of 2.5, which is subtracted */
{-2.5f, 0.0f}}, {-2.5f, 0.0f}},
{"top left", Alignment::TopLeft, {"top left",
Alignment::TopLeft, ShapeDirection::Unspecified,
/* Ascent is 4.5, scaled by 0.5 */ /* Ascent is 4.5, scaled by 0.5 */
{0.0f, -2.25f}}, {0.0f, -2.25f}},
{"top left, glyph bounds", Alignment::TopLeftGlyphBounds, {"top left, glyph bounds",
Alignment::TopLeftGlyphBounds, ShapeDirection::Unspecified,
/* Largest Y value is 10.5f */ /* Largest Y value is 10.5f */
{-2.5f, -10.5f}}, {-2.5f, -10.5f}},
{"top right", Alignment::TopRight, {"top right",
Alignment::TopRight, ShapeDirection::Unspecified,
/* Advances were 1, 2, 3, so 6 in total, ascent is 4.5; scaled by /* Advances were 1, 2, 3, so 6 in total, ascent is 4.5; scaled by
0.5 */ 0.5 */
{-3.0f, -2.25f}}, {-3.0f, -2.25f}},
{"top right, glyph bounds", Alignment::TopRightGlyphBounds, {"top right, glyph bounds",
Alignment::TopRightGlyphBounds, ShapeDirection::Unspecified,
/* Basically subtracting the largest vertex value */ /* Basically subtracting the largest vertex value */
{-12.5f, -10.5f}}, {-12.5f, -10.5f}},
{"top center", Alignment::TopCenter, {"top center",
Alignment::TopCenter, ShapeDirection::Unspecified,
/* Advances were 1, 2, 3, so 6 in total, center is 3, scaled by 0.5 */ /* Advances were 1, 2, 3, so 6 in total, center is 3, scaled by 0.5 */
{-1.5f, -2.25f}}, {-1.5f, -2.25f}},
{"top center, integral", Alignment::TopCenterIntegral, {"top center, integral",
Alignment::TopCenterIntegral, ShapeDirection::Unspecified,
/* The Y shift isn't whole units but only X is rounded here */ /* The Y shift isn't whole units but only X is rounded here */
{-2.0f, -2.25f}}, {-2.0f, -2.25f}},
{"top center, glyph bounds", Alignment::TopCenterGlyphBounds, {"top center, glyph bounds",
Alignment::TopCenterGlyphBounds, ShapeDirection::Unspecified,
{-7.5f, -10.5f}}, {-7.5f, -10.5f}},
{"top center, glyph bounds, integral", Alignment::TopCenterGlyphBoundsIntegral, {"top center, glyph bounds, integral",
Alignment::TopCenterGlyphBoundsIntegral, ShapeDirection::Unspecified,
/* The Y shift isn't whole units but only X is rounded here */ /* The Y shift isn't whole units but only X is rounded here */
{-8.0f, -10.5f}}, {-8.0f, -10.5f}},
{"middle left, glyph bounds", Alignment::MiddleLeftGlyphBounds, {"middle left, glyph bounds",
Alignment::MiddleLeftGlyphBounds, ShapeDirection::Unspecified,
{-2.5f, -7.125f}}, {-2.5f, -7.125f}},
{"middle left, glyph bounds, integral", Alignment::MiddleLeftGlyphBoundsIntegral, {"middle left, glyph bounds, integral",
Alignment::MiddleLeftGlyphBoundsIntegral, ShapeDirection::Unspecified,
/* The X shift isn't whole units but only Y is rounded here */ /* The X shift isn't whole units but only Y is rounded here */
{-2.5f, -7.0f}}, {-2.5f, -7.0f}},
{"middle center", Alignment::MiddleCenter, {"middle center",
Alignment::MiddleCenter, ShapeDirection::Unspecified,
{-1.5f, -0.5f}}, {-1.5f, -0.5f}},
{"middle center, integral", Alignment::MiddleCenterIntegral, {"middle center, integral",
Alignment::MiddleCenterIntegral, ShapeDirection::Unspecified,
/* Rounding happens on both X and Y in this case */ /* Rounding happens on both X and Y in this case */
{-2.0f, -1.0f}}, {-2.0f, -1.0f}},
{"middle center, glyph bounds", Alignment::MiddleCenterGlyphBounds, {"middle center, glyph bounds",
Alignment::MiddleCenterGlyphBounds, ShapeDirection::Unspecified,
/* Half size of the bounds quad */ /* Half size of the bounds quad */
{-7.5f, -7.125f}}, {-7.5f, -7.125f}},
{"middle center, glyph bounds, integral", Alignment::MiddleCenterGlyphBoundsIntegral, {"middle center, glyph bounds, integral",
Alignment::MiddleCenterGlyphBoundsIntegral, ShapeDirection::Unspecified,
{-8.0f, -7.0f}}, {-8.0f, -7.0f}},
{"bottom left", Alignment::BottomLeft, {"bottom left",
Alignment::BottomLeft, ShapeDirection::Unspecified,
/* Descent is -2.5; scaled by 0.5 */ /* Descent is -2.5; scaled by 0.5 */
{0.0f, 1.25f}}, {0.0f, 1.25f}},
{"bottom right", Alignment::BottomRight, {"bottom right",
Alignment::BottomRight, ShapeDirection::Unspecified,
{-3.0f, 1.25f}}, {-3.0f, 1.25f}},
{"bottom right, glyph bounds", Alignment::BottomRightGlyphBounds, {"bottom right, glyph bounds",
Alignment::BottomRightGlyphBounds, ShapeDirection::Unspecified,
{-12.5f, -3.75f}}, {-12.5f, -3.75f}},
{"line start, direction unspecified",
Alignment::LineStart, ShapeDirection::Unspecified,
{}}, /* Same as line left, thus no shift */
{"bottom start, LTR",
Alignment::BottomStart, ShapeDirection::LeftToRight,
{0.0f, 1.25f}}, /* Same as bottom left */
{"line end, RTL",
Alignment::LineEnd, ShapeDirection::RightToLeft,
{}}, /* Again same as line left, thus no shift */
{"line end, direction unspecified",
Alignment::LineEnd, ShapeDirection::Unspecified,
{-3.0f, 0.0f}}, /* Same as line right */
{"top end, LTR",
Alignment::TopEnd, ShapeDirection::LeftToRight,
{-3.0f, -2.25f}}, /* Same as top right */
{"line start, RTL",
Alignment::LineStart, ShapeDirection::RightToLeft,
{-3.0f, 0.0f}}, /* Same as line right */
{"line left, RTL",
Alignment::LineLeft, ShapeDirection::RightToLeft,
{}}, /* Line left with no change */
{"middle center, RTL",
Alignment::MiddleCenter, ShapeDirection::RightToLeft,
{-1.5f, -0.5f}}, /* Middle center with no change */
}; };
const struct { const struct {
@ -303,12 +347,16 @@ RendererTest::RendererTest() {
} }
struct TestShaper: AbstractShaper { struct TestShaper: AbstractShaper {
using AbstractShaper::AbstractShaper; explicit TestShaper(AbstractFont& font, ShapeDirection direction): AbstractShaper{font}, _direction{direction} {}
UnsignedInt doShape(Containers::StringView text, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override { UnsignedInt doShape(Containers::StringView text, UnsignedInt, UnsignedInt, Containers::ArrayView<const FeatureRange>) override {
return text.size(); return text.size();
} }
ShapeDirection doDirection() const override {
return _direction;
}
void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override { void doGlyphIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& ids) const override {
for(UnsignedInt i = 0; i != ids.size(); ++i) { for(UnsignedInt i = 0; i != ids.size(); ++i) {
/* It just rotates between the three glyphs */ /* It just rotates between the three glyphs */
@ -325,9 +373,12 @@ struct TestShaper: AbstractShaper {
/* Offset Y and advance X is getting larger with every glyph, /* Offset Y and advance X is getting larger with every glyph,
advance Y is flipping its sign with every glyph */ advance Y is flipping its sign with every glyph */
offsets[i] = Vector2::yAxis(i + 1); offsets[i] = Vector2::yAxis(i + 1);
/* This is always to the right, independent of ShapeDirection */
advances[i] = {Float(i + 1), i % 2 ? -0.5f : +0.5f}; advances[i] = {Float(i + 1), i % 2 ? -0.5f : +0.5f};
} }
} }
ShapeDirection _direction;
}; };
struct TestFont: AbstractFont { struct TestFont: AbstractFont {
@ -350,9 +401,11 @@ struct TestFont: AbstractFont {
Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } Vector2 doGlyphAdvance(UnsignedInt) override { return {}; }
Containers::Pointer<AbstractShaper> doCreateShaper() override { Containers::Pointer<AbstractShaper> doCreateShaper() override {
return Containers::pointer<TestShaper>(*this); return Containers::pointer<TestShaper>(*this, direction);
} }
ShapeDirection direction = ShapeDirection::Unspecified;
bool _opened = false; bool _opened = false;
}; };
@ -817,7 +870,11 @@ void RendererTest::alignLineInvalidDirection() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
alignRenderedLine({}, LayoutDirection::VerticalRightToLeft, Alignment::LineLeft, nullptr); alignRenderedLine({}, LayoutDirection::VerticalRightToLeft, Alignment::LineLeft, nullptr);
CORRADE_COMPARE(out.str(), "Text::alignRenderedLine(): only Text::LayoutDirection::HorizontalTopToBottom is supported right now, got Text::LayoutDirection::VerticalRightToLeft\n"); alignRenderedLine({}, LayoutDirection::HorizontalTopToBottom, Alignment::BottomEnd, nullptr);
CORRADE_COMPARE_AS(out.str(),
"Text::alignRenderedLine(): only Text::LayoutDirection::HorizontalTopToBottom is supported right now, got Text::LayoutDirection::VerticalRightToLeft\n"
"Text::alignRenderedLine(): Text::Alignment::BottomEnd has to be resolved to *Left / *Right before being passed to this function\n",
TestSuite::Compare::String);
} }
void RendererTest::alignBlock() { void RendererTest::alignBlock() {
@ -847,7 +904,11 @@ void RendererTest::alignBlockInvalidDirection() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
alignRenderedBlock({}, LayoutDirection::VerticalRightToLeft, Alignment::LineLeft, nullptr); alignRenderedBlock({}, LayoutDirection::VerticalRightToLeft, Alignment::LineLeft, nullptr);
CORRADE_COMPARE(out.str(), "Text::alignRenderedBlock(): only Text::LayoutDirection::HorizontalTopToBottom is supported right now, got Text::LayoutDirection::VerticalRightToLeft\n"); alignRenderedBlock({}, LayoutDirection::HorizontalTopToBottom, Alignment::LineStartGlyphBounds, nullptr);
CORRADE_COMPARE_AS(out.str(),
"Text::alignRenderedBlock(): only Text::LayoutDirection::HorizontalTopToBottom is supported right now, got Text::LayoutDirection::VerticalRightToLeft\n"
"Text::alignRenderedBlock(): Text::Alignment::LineStartGlyphBounds has to be resolved to *Left / *Right before being passed to this function\n",
TestSuite::Compare::String);
} }
template<class T> void RendererTest::glyphQuadIndices() { template<class T> void RendererTest::glyphQuadIndices() {
@ -909,6 +970,7 @@ void RendererTest::renderData() {
setTestCaseDescription(data.name); setTestCaseDescription(data.name);
TestFont font; TestFont font;
font.direction = data.shapeDirection;
font.openFile({}, 0.5f); font.openFile({}, 0.5f);
DummyGlyphCache cache = testGlyphCache(font); DummyGlyphCache cache = testGlyphCache(font);

Loading…
Cancel
Save