Reland "[skottie] Max lines text auto-sizing constraint"

This relands commit 4d6d9e3f89.

Original change's description:
> [skottie] Max lines text auto-sizing constraint
>
> Introduce a new text property ("xl"), to limit the number of lines when
> auto-sizing.
>
> This is a Skottie extension, pending UI/controls in Bodymovin.
>
> Change-Id: Id0f1e633e1b324a97b227d6b187cd540990796a7
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/518498
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
> Commit-Queue: Florin Malita <fmalita@google.com>

Change-Id: Ib9d28366332866d8b787f89fa1dc13132c02b435
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/518699
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
This commit is contained in:
Florin Malita 2022-03-09 14:46:52 -05:00 committed by SkCQ
parent bad94bc85a
commit 762f8fbcad
8 changed files with 50 additions and 24 deletions

View File

@ -46,6 +46,7 @@ struct TextPropertyValue {
fLineHeight = 0,
fLineShift = 0,
fAscent = 0;
size_t fMaxLines = 0; // when auto-sizing
SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align;
Shaper::VAlign fVAlign = Shaper::VAlign::kTop;
Shaper::ResizePolicy fResize = Shaper::ResizePolicy::kNone;

View File

@ -22,6 +22,7 @@ bool TextPropertyValue::operator==(const TextPropertyValue& other) const {
&& fLineHeight == other.fLineHeight
&& fLineShift == other.fLineShift
&& fAscent == other.fAscent
&& fMaxLines == other.fMaxLines
&& fHAlign == other.fHAlign
&& fVAlign == other.fVAlign
&& fResize == other.fResize

View File

@ -321,6 +321,7 @@ DEF_TEST(Skottie_Properties, reporter) {
120,
12,
0,
0,
SkTextUtils::kLeft_Align,
Shaper::VAlign::kTopBaseline,
Shaper::ResizePolicy::kNone,
@ -480,7 +481,6 @@ DEF_TEST(Skottie_Shaper_HAlign, reporter) {
Shaper::LinebreakPolicy::kExplicit,
Shaper::Direction::kLTR,
Shaper::Capitalization::kNone,
Shaper::Flags::kNone
};
const auto shape_result = Shaper::Shape(text, desc, text_point,
@ -551,7 +551,6 @@ DEF_TEST(Skottie_Shaper_VAlign, reporter) {
Shaper::LinebreakPolicy::kParagraph,
Shaper::Direction::kLTR,
Shaper::Capitalization::kNone,
Shaper::Flags::kNone
};
const auto shape_result = Shaper::Shape(text, desc, text_box, SkFontMgr::RefDefault());
@ -592,7 +591,6 @@ DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) {
Shaper::LinebreakPolicy::kParagraph,
Shaper::Direction::kLTR,
Shaper::Capitalization::kNone,
Shaper::Flags::kNone
};
const SkString text("Foo bar baz");
@ -685,7 +683,6 @@ DEF_TEST(Skottie_Shaper_ExplicitFontMgr, reporter) {
Shaper::LinebreakPolicy::kParagraph,
Shaper::Direction::kLTR,
Shaper::Capitalization::kNone,
Shaper::Flags::kNone
};
const auto text_box = SkRect::MakeWH(100, 100);

View File

@ -385,6 +385,22 @@ Shaper::Result ShapeImpl(const SkString& txt, const Shaper::TextDesc& desc,
return blobMaker.finalize(shaped_size);
}
bool result_fits(const Shaper::Result& res, const SkSize& res_size,
const SkRect& box, const Shaper::TextDesc& desc) {
// optional max line count constraint
if (desc.fMaxLines) {
const auto line_count = res.fFragments.empty()
? 0
: res.fFragments.back().fLineIndex + 1;
if (line_count > desc.fMaxLines) {
return false;
}
}
// geometric constraint
return res_size.width() <= box.width() && res_size.height() <= box.height();
}
Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc,
const SkRect& box, const sk_sp<SkFontMgr>& fontmgr) {
Shaper::Result best_result;
@ -419,7 +435,7 @@ Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc
auto res = ShapeImpl(txt, desc, box, fontmgr, &res_size);
const auto prev_scale = try_scale;
if (res_size.width() > box.width() || res_size.height() > box.height()) {
if (!result_fits(res, res_size, box, desc)) {
out_scale = try_scale;
try_scale = (in_scale == min_scale)
// initial in_scale not found yet - search exponentially
@ -498,7 +514,7 @@ Shaper::Result Shaper::Shape(const SkString& orig_txt, const TextDesc& desc, con
SkSize size;
auto result = ShapeImpl(txt, desc, box, fontmgr, &size);
return (size.width() <= box.width() && size.height() <= box.height())
return result_fits(result, size, box, desc)
? result
: ShapeToFit(txt, desc, box, fontmgr);
}

View File

@ -108,19 +108,20 @@ public:
struct TextDesc {
const sk_sp<SkTypeface>& fTypeface;
SkScalar fTextSize,
fMinTextSize,
fMaxTextSize,
fLineHeight,
fLineShift,
fAscent;
SkTextUtils::Align fHAlign;
VAlign fVAlign;
ResizePolicy fResize;
LinebreakPolicy fLinebreak;
Direction fDirection;
Capitalization fCapitalization;
uint32_t fFlags;
SkScalar fTextSize = 0,
fMinTextSize = 0, // when auto-sizing
fMaxTextSize = 0, // when auto-sizing
fLineHeight = 0,
fLineShift = 0,
fAscent = 0;
SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align;
VAlign fVAlign = Shaper::VAlign::kTop;
ResizePolicy fResize = Shaper::ResizePolicy::kNone;
LinebreakPolicy fLinebreak = Shaper::LinebreakPolicy::kExplicit;
Direction fDirection = Shaper::Direction::kLTR ;
Capitalization fCapitalization = Shaper::Capitalization::kNone;
size_t fMaxLines = 0; // when auto-sizing, 0 -> no max
uint32_t fFlags = 0;
};
// Performs text layout along an infinite horizontal line, starting at |textPoint|.

View File

@ -440,10 +440,17 @@ void TextAdapter::setText(const TextValue& txt) {
uint32_t TextAdapter::shaperFlags() const {
uint32_t flags = Shaper::Flags::kNone;
// We need granular fragments (as opposed to consolidated blobs) when animating, or when
// positioning on a path.
if (!fAnimators.empty() || fPathInfo) flags |= Shaper::Flags::kFragmentGlyphs;
if (fRequiresAnchorPoint) flags |= Shaper::Flags::kTrackFragmentAdvanceAscent;
// We need granular fragments (as opposed to consolidated blobs):
// - when animating
// - when positioning on a path
// - when clamping the number or lines (for accurate line count)
if (!fAnimators.empty() || fPathInfo || fText->fMaxLines) {
flags |= Shaper::Flags::kFragmentGlyphs;
}
if (fRequiresAnchorPoint) {
flags |= Shaper::Flags::kTrackFragmentAdvanceAscent;
}
return flags;
}
@ -463,6 +470,7 @@ void TextAdapter::reshape() {
fText->fLineBreak,
fText->fDirection,
fText->fCapitalization,
fText->fMaxLines,
this->shaperFlags(),
};
const auto shape_result = Shaper::Shape(fText->fText, text_desc, fText->fBox, fFontMgr);

View File

@ -74,9 +74,10 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder,
ParseDefault<size_t>((*jtxt)["sk_rs"], 0)),
SK_ARRAY_COUNT(gResizeMap) - 1)];
// Optional min/max font size (used when aute-resizing)
// Optional min/max font size and line count (used when aute-resizing)
v->fMinTextSize = ParseDefault<SkScalar>((*jtxt)["mf"], 0.0f);
v->fMaxTextSize = ParseDefault<SkScalar>((*jtxt)["xf"], std::numeric_limits<float>::max());
v->fMaxLines = ParseDefault<size_t> ((*jtxt)["xl"], 0);
// At the moment, BM uses the paragraph box to discriminate point mode vs. paragraph mode.
v->fLineBreak = v->fBox.isEmpty()

File diff suppressed because one or more lines are too long