[skottie] Path support for paragraph text

In addition to single line (point) text, AE also supports path layout
for paragraph text.

At a high level, the paragraph box top is mapped to the path (following
alignment rules), and each glyph is displaced along its path positioning
vector, post orientation.

The main difference compared to point text, is that the distance on path
is based on the fragment position relative to the paragraph left edge.

The paragraph box also plays a role in alignment: left/center/right
aligns with path start/mid/end.

This includes a tangential optimization: instead of validating cached
contour data in each PathInfo::getMatrix() call, we only check once at
a higher level (onSync) -- to avoid performing a shape vector comparison
for each fragment.

Change-Id: I2c31ce3b0a525a3cd2d4525abcf88d5fc943bb6e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457656
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
Florin Malita 2021-10-10 22:52:32 -04:00 committed by SkCQ
parent d616f0b949
commit 132d47c90d
4 changed files with 38 additions and 7 deletions

View File

@ -67,7 +67,7 @@ struct TextAdapter::PathInfo {
fPathPerpendicular = 0,
fPathReverse = 0;
SkM44 getMatrix(float distance, SkTextUtils::Align alignment) {
void updateContourData() {
const auto reverse = fPathReverse != 0;
if (fPath != fCurrentPath || reverse != fCurrentReversed) {
@ -88,6 +88,18 @@ struct TextAdapter::PathInfo {
// AE paths are always single-contour (no moves allowed).
SkASSERT(!iter.next());
}
}
float pathLength() const {
SkASSERT(fPath == fCurrentPath);
SkASSERT((fPathReverse != 0) == fCurrentReversed);
return fCurrentMeasure ? fCurrentMeasure->length() : 0;
}
SkM44 getMatrix(float distance, SkTextUtils::Align alignment) const {
SkASSERT(fPath == fCurrentPath);
SkASSERT((fPathReverse != 0) == fCurrentReversed);
if (!fCurrentMeasure) {
return SkM44();
@ -95,9 +107,6 @@ struct TextAdapter::PathInfo {
const auto path_len = fCurrentMeasure->length();
// Alignment adjustment, relative to path len.
distance += path_len * align_factor(alignment);
// First/last margin adjustment also depends on alignment.
switch (alignment) {
case SkTextUtils::Align::kLeft_Align: distance += fPathFMargin; break;
@ -526,6 +535,11 @@ void TextAdapter::onSync() {
return;
}
// Update the path contour measure, if needed.
if (fPathInfo) {
fPathInfo->updateContourData();
}
// Seed props from the current text value.
TextAnimator::ResolvedProps seed_props;
seed_props.fill_color = fText->fFillColor;
@ -662,10 +676,24 @@ SkM44 TextAdapter::fragmentMatrix(const TextAnimator::ResolvedProps& props,
return SkM44::Translate(pos.x, pos.y, pos.z);
}
// When using a text path, the horizontal component determines the position on path.
const auto path_distance = pos.x;
// "Align" the paragraph box left/center/right to path start/mid/end, respectively.
const auto align_offset =
align_factor(fText->fHAlign)*(fPathInfo->pathLength() - fText->fBox.width());
// Path positioning is based on the fragment position relative to the paragraph box
// upper-left corner:
//
// - the horizontal component determines the distance on path
//
// - the vertical component is post-applied after orienting on path
//
// Note: in point-text mode, the box adjustments have no effect as fBox is {0,0,0,0}.
//
const auto rel_pos = SkV2{pos.x, pos.y} - SkV2{fText->fBox.fLeft, fText->fBox.fTop};
const auto path_distance = rel_pos.x + align_offset;
return fPathInfo->getMatrix(path_distance, fText->fHAlign)
* SkM44::Translate(0, pos.y, pos.z);
* SkM44::Translate(0, rel_pos.y, pos.z);
}
void TextAdapter::pushPropsToFragment(const TextAnimator::ResolvedProps& props,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long