Cache should work with INF values

Change-Id: I1ae8d95bb85d28fdce9e0cf270583f0224e4dfed
Bug: skia:9874
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/268938
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
Julia Lavrova 2020-02-05 10:17:53 -05:00 committed by Skia Commit-Bot
parent 2bc603196a
commit c0360582d2
14 changed files with 242 additions and 87 deletions

View File

@ -37,6 +37,17 @@ struct StrutStyle {
bool getHeightOverride() const { return fHeightOverride; }
void setHeightOverride(bool v) { fHeightOverride = v; }
bool operator==(const StrutStyle& rhs) const {
return this->fEnabled == rhs.fEnabled &&
this->fHeightOverride == rhs.fHeightOverride &&
this->fForceHeight == rhs.fForceHeight &&
nearlyEqual(this->fLeading, rhs.fLeading) &&
nearlyEqual(this->fHeight, rhs.fHeight) &&
nearlyEqual(this->fFontSize, rhs.fFontSize) &&
this->fFontStyle == rhs.fFontStyle &&
this->fFontFamilies == rhs.fFontFamilies;
}
private:
std::vector<SkString> fFontFamilies;

View File

@ -18,6 +18,21 @@
namespace skia {
namespace textlayout {
static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) {
if (SkScalarIsFinite(x)) {
return SkScalarNearlyZero(x, tolerance);
}
return false;
}
static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) {
if (SkScalarIsFinite(x) && SkScalarIsFinite(x)) {
return SkScalarNearlyEqual(x, y, tolerance);
}
// Inf == Inf, anything else is false
return x == y;
}
// Multiple decorations can be applied at once. Ex: Underline and overline is
// (0x1 | 0x2)
enum TextDecoration {
@ -100,7 +115,13 @@ struct FontFeature {
};
struct PlaceholderStyle {
PlaceholderStyle() { }
PlaceholderStyle()
: fWidth(0)
, fHeight(0)
, fAlignment(PlaceholderAlignment::kBaseline)
, fBaseline(TextBaseline::kAlphabetic)
, fBaselineOffset(0) {}
PlaceholderStyle(SkScalar width, SkScalar height, PlaceholderAlignment alignment,
TextBaseline baseline, SkScalar offset)
: fWidth(width)
@ -111,13 +132,10 @@ struct PlaceholderStyle {
bool equals(const PlaceholderStyle& other) const;
SkScalar fWidth = 0;
SkScalar fHeight = 0;
SkScalar fWidth;
SkScalar fHeight;
PlaceholderAlignment fAlignment;
TextBaseline fBaseline;
// Distance from the top edge of the rect to the baseline position. This
// baseline will be aligned against the alphabetic baseline of the surrounding
// text.
@ -126,7 +144,7 @@ struct PlaceholderStyle {
// small or negative values will cause the rect to be positioned underneath
// the line. When baseline == height, the bottom edge of the rect will rest on
// the alphabetic baseline.
SkScalar fBaselineOffset = 0;
SkScalar fBaselineOffset;
};
class TextStyle {

View File

@ -483,7 +483,7 @@ bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
run.fPositions[0] = { advanceX, 0 };
run.fOffsets[0] = {0, 0};
run.fClusterIndexes[0] = 0;
run.fPlaceholder = &placeholder.fStyle;
run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
advanceX += placeholder.fStyle.fWidth;
}
return true;

View File

@ -8,8 +8,12 @@ namespace textlayout {
namespace {
SkScalar relax(SkScalar a) {
// This rounding is done to match Flutter tests. Must be removed..
auto threshold = SkIntToScalar(1 << 12);
return SkScalarRoundToScalar(a * threshold)/threshold;
if (SkScalarIsFinite(a)) {
auto threshold = SkIntToScalar(1 << 12);
return SkScalarRoundToScalar(a * threshold)/threshold;
} else {
return a;
}
}
}
@ -31,19 +35,15 @@ class ParagraphCacheValue {
public:
ParagraphCacheValue(const ParagraphImpl* paragraph)
: fKey(ParagraphCacheKey(paragraph))
, fInternalState(paragraph->fState)
, fRuns(paragraph->fRuns)
, fClusters(paragraph->fClusters)
, fUnresolvedGlyphs(paragraph->fUnresolvedGlyphs){ }
, fClusters(paragraph->fClusters) { }
// Input == key
ParagraphCacheKey fKey;
// Shaped results:
InternalState fInternalState;
// Shaped results
SkTArray<Run, false> fRuns;
SkTArray<Cluster, true> fClusters;
size_t fUnresolvedGlyphs;
};
uint32_t ParagraphCache::KeyHash::mix(uint32_t hash, uint32_t data) const {
@ -56,21 +56,20 @@ uint32_t ParagraphCache::KeyHash::mix(uint32_t hash, uint32_t data) const {
uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const {
uint32_t hash = 0;
for (auto& ph : key.fPlaceholders) {
if (&ph == &key.fPlaceholders.back()) {
// Skip the last "dummy" placeholder
break;
if (ph.fRange.width() == 0) {
continue;
}
hash = mix(hash, SkGoodHash()(ph.fRange.start));
hash = mix(hash, SkGoodHash()(ph.fRange.end));
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fBaselineOffset)));
hash = mix(hash, SkGoodHash()(ph.fStyle.fBaseline));
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fHeight)));
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fWidth)));
hash = mix(hash, SkGoodHash()(ph.fStyle.fAlignment));
hash = mix(hash, SkGoodHash()(ph.fStyle.fBaseline));
if (ph.fStyle.fAlignment == PlaceholderAlignment::kBaseline) {
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fBaselineOffset)));
}
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fHeight)));
hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fWidth)));
}
for (auto& ts : key.fTextStyles) {
if (ts.fStyle.isPlaceholder()) {
continue;
@ -79,22 +78,34 @@ uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getWordSpacing())));
hash = mix(hash, SkGoodHash()(ts.fStyle.getLocale()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHeight())));
hash = mix(hash, SkGoodHash()(ts.fRange));
for (auto& ff : ts.fStyle.getFontFamilies()) {
hash = mix(hash, SkGoodHash()(ff));
}
for (auto& ff : ts.fStyle.getFontFeatures()) {
hash = mix(hash, SkGoodHash()(ff));
hash = mix(hash, SkGoodHash()(ff.fValue));
hash = mix(hash, SkGoodHash()(ff.fName));
}
hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(ts.fRange.start));
hash = mix(hash, SkGoodHash()(ts.fRange.end));
hash = mix(hash, SkGoodHash()(ts.fRange));
}
hash = mix(hash, SkGoodHash()(relax(key.fParagraphStyle.getHeight())));
hash = mix(hash, SkGoodHash()(key.fParagraphStyle.getTextDirection()));
auto& strutStyle = key.fParagraphStyle.getStrutStyle();
if (strutStyle.getStrutEnabled()) {
hash = mix(hash, SkGoodHash()(relax(strutStyle.getHeight())));
hash = mix(hash, SkGoodHash()(relax(strutStyle.getLeading())));
hash = mix(hash, SkGoodHash()(relax(strutStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(strutStyle.getHeightOverride()));
hash = mix(hash, SkGoodHash()(strutStyle.getFontStyle()));
hash = mix(hash, SkGoodHash()(strutStyle.getForceStrutHeight()));
for (auto& ff : strutStyle.getFontFamilies()) {
hash = mix(hash, SkGoodHash()(ff));
}
}
hash = mix(hash, SkGoodHash()(key.fText));
return hash;
}
@ -114,13 +125,17 @@ bool operator==(const ParagraphCacheKey& a, const ParagraphCacheKey& b) {
}
// There is no need to compare default paragraph styles - they are included into fTextStyles
if (!SkScalarNearlyEqual(a.fParagraphStyle.getHeight(), b.fParagraphStyle.getHeight())) {
if (!nearlyEqual(a.fParagraphStyle.getHeight(), b.fParagraphStyle.getHeight())) {
return false;
}
if (a.fParagraphStyle.getTextDirection() != b.fParagraphStyle.getTextDirection()) {
return false;
}
if (!(a.fParagraphStyle.getStrutStyle() == b.fParagraphStyle.getStrutStyle())) {
return false;
}
for (size_t i = 0; i < a.fTextStyles.size(); ++i) {
auto& tsa = a.fTextStyles[i];
auto& tsb = b.fTextStyles[i];
@ -137,9 +152,12 @@ bool operator==(const ParagraphCacheKey& a, const ParagraphCacheKey& b) {
return false;
}
}
for (size_t i = 0; i < a.fPlaceholders.size() - 1; ++i) {
for (size_t i = 0; i < a.fPlaceholders.size(); ++i) {
auto& tsa = a.fPlaceholders[i];
auto& tsb = b.fPlaceholders[i];
if (tsa.fRange.width() == 0 && tsb.fRange.width() == 0) {
continue;
}
if (!(tsa.fStyle.equals(tsb.fStyle))) {
return false;
}
@ -175,7 +193,6 @@ ParagraphCache::~ParagraphCache() { }
void ParagraphCache::updateFrom(const ParagraphImpl* paragraph, Entry* entry) {
entry->fValue->fInternalState = paragraph->state();
for (size_t i = 0; i < paragraph->fRuns.size(); ++i) {
auto& run = paragraph->fRuns[i];
if (run.fSpaced) {
@ -185,6 +202,7 @@ void ParagraphCache::updateFrom(const ParagraphImpl* paragraph, Entry* entry) {
}
void ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) {
paragraph->fRuns.reset();
paragraph->fRuns = entry->fValue->fRuns;
for (auto& run : paragraph->fRuns) {
@ -197,8 +215,7 @@ void ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) {
cluster.setMaster(paragraph);
}
paragraph->fState = entry->fValue->fInternalState;
paragraph->fUnresolvedGlyphs = entry->fValue->fUnresolvedGlyphs;
paragraph->fState = kMarked;
}
void ParagraphCache::printStatistics() {

View File

@ -414,7 +414,7 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
}
}
fLongestLine = SkTMax(fLongestLine, SkScalarNearlyZero(advance.fX) ? widthWithSpaces : advance.fX);
fLongestLine = SkTMax(fLongestLine, nearlyZero(advance.fX) ? widthWithSpaces : advance.fX);
});
fHeight = textWrapper.height();
fWidth = maxWidth;
@ -427,9 +427,17 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
void ParagraphImpl::formatLines(SkScalar maxWidth) {
auto effectiveAlign = fParagraphStyle.effective_align();
if (!SkScalarIsFinite(maxWidth) && effectiveAlign != TextAlign::kLeft) {
// Special case: clean all text in case of maxWidth == INF & align != left
// We had to go through shaping though because we need all the measurement numbers
fLines.reset();
return;
}
if (effectiveAlign == TextAlign::kJustify) {
this->resetRunShifts();
}
for (auto& line : fLines) {
if (&line == &fLines.back() && effectiveAlign == TextAlign::kJustify) {
effectiveAlign = line.assumedTextAlign();
@ -768,16 +776,16 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
bool mergedBoxes = false;
if (!results.empty() &&
lastRun != nullptr &&
lastRun->placeholder() == nullptr &&
context.run->placeholder() == nullptr &&
SkScalarNearlyEqual(lastRun->lineHeight(), context.run->lineHeight()) &&
lastRun->placeholderStyle() == nullptr &&
context.run->placeholderStyle() == nullptr &&
nearlyEqual(lastRun->lineHeight(), context.run->lineHeight()) &&
lastRun->font() == context.run->font())
{
auto& lastBox = results.back();
if (SkScalarNearlyEqual(lastBox.rect.fTop, clip.fTop) &&
SkScalarNearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
(SkScalarNearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
SkScalarNearlyEqual(lastBox.rect.fRight, clip.fLeft)))
if (nearlyEqual(lastBox.rect.fTop, clip.fTop) &&
nearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
(nearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
nearlyEqual(lastBox.rect.fRight, clip.fLeft)))
{
lastBox.rect.fLeft = SkTMin(lastBox.rect.fLeft, clip.fLeft);
lastBox.rect.fRight = SkTMax(lastBox.rect.fRight, clip.fRight);
@ -792,7 +800,7 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
results.emplace_back(
clip, context.run->leftToRight() ? TextDirection::kLtr : TextDirection::kRtl);
}
if (!SkScalarNearlyZero(trailingSpaces.width()) && !merge(trailingSpaces)) {
if (!nearlyZero(trailingSpaces.width()) && !merge(trailingSpaces)) {
results.emplace_back(trailingSpaces, paragraphTextDirection);
}
@ -843,7 +851,7 @@ std::vector<TextBox> ParagraphImpl::getRectsForPlaceholders() {
auto context =
line.measureTextInsideOneRun(textRange, run, runOffset, 0, true, false);
*width = context.clip.width();
if (run->placeholder() == nullptr) {
if (run->placeholderStyle() == nullptr) {
return true;
}
if (run->textRange().width() == 0) {
@ -1120,7 +1128,9 @@ void ParagraphImpl::computeEmptyMetrics() {
fEmptyMetrics.leading() * multiplier);
}
fStrutMetrics.updateLineMetrics(fEmptyMetrics);
if (fParagraphStyle.getStrutStyle().getStrutEnabled()) {
fStrutMetrics.updateLineMetrics(fEmptyMetrics);
}
}
void ParagraphImpl::updateText(size_t from, SkString text) {

View File

@ -135,6 +135,9 @@ public:
SkSpan<Block> styles() {
return SkSpan<Block>(fTextStyles.data(), fTextStyles.size());
}
SkSpan<Placeholder> placeholders() {
return SkSpan<Placeholder>(fPlaceholders.data(), fPlaceholders.size());
}
SkSpan<TextLine> lines() { return SkSpan<TextLine>(fLines.data(), fLines.size()); }
const ParagraphStyle& paragraphStyle() const { return fParagraphStyle; }
SkSpan<Cluster> clusters() { return SkSpan<Cluster>(fClusters.begin(), fClusters.size()); }

View File

@ -47,7 +47,7 @@ Run::Run(ParagraphImpl* master,
fOffsets[info.glyphCount] = { 0, 0};
fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
fEllipsis = false;
fPlaceholder = nullptr;
fPlaceholderIndex = std::numeric_limits<size_t>::max();
}
SkShaper::RunHandler::Buffer Run::newRunBuffer() {
@ -238,9 +238,11 @@ void Run::shift(const Cluster* cluster, SkScalar offset) {
void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
SkASSERT(isPlaceholder());
auto placeholderStyle = this->placeholderStyle();
// Difference between the placeholder baseline and the line bottom
SkScalar baselineAdjustment = 0;
switch (fPlaceholder->fBaseline) {
switch (placeholderStyle->fBaseline) {
case TextBaseline::kAlphabetic:
break;
@ -249,11 +251,11 @@ void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
break;
}
auto height = fPlaceholder->fHeight;
auto offset = fPlaceholder->fBaselineOffset;
auto height = placeholderStyle->fHeight;
auto offset = placeholderStyle->fBaselineOffset;
fFontMetrics.fLeading = 0;
switch (fPlaceholder->fAlignment) {
switch (placeholderStyle->fAlignment) {
case PlaceholderAlignment::kBaseline:
fFontMetrics.fAscent = baselineAdjustment - offset;
fFontMetrics.fDescent = baselineAdjustment + height - offset;
@ -339,6 +341,14 @@ SkScalar Run::positionX(size_t pos) const {
return posX(pos) + fShifts[pos] + fMaster->posShift(fIndex, pos);
}
PlaceholderStyle* Run::placeholderStyle() const {
if (isPlaceholder()) {
return &fMaster->placeholders()[fPlaceholderIndex].fStyle;
} else {
return nullptr;
}
}
Run* Cluster::run() const {
if (fRunIndex >= fMaster->runs().size()) {
return nullptr;

View File

@ -104,8 +104,8 @@ public:
bool leftToRight() const { return fBidiLevel % 2 == 0; }
size_t index() const { return fIndex; }
SkScalar lineHeight() const { return fHeightMultiplier; }
PlaceholderStyle* placeholder() const { return fPlaceholder; }
bool isPlaceholder() const { return fPlaceholder != nullptr; }
PlaceholderStyle* placeholderStyle() const;
bool isPlaceholder() const { return fPlaceholderIndex != std::numeric_limits<size_t>::max(); }
size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; }
SkScalar positionX(size_t pos) const;
@ -177,7 +177,7 @@ private:
SkFont fFont;
SkFontMetrics fFontMetrics;
SkScalar fHeightMultiplier;
PlaceholderStyle* fPlaceholder;
size_t fPlaceholderIndex;
bool fEllipsis;
size_t fIndex;
uint8_t fBidiLevel;

View File

@ -34,7 +34,7 @@ int compareRound(SkScalar a, SkScalar b) {
// It has to be relative to be useful
auto base = SkTMax(SkScalarAbs(a), SkScalarAbs(b));
auto diff = SkScalarAbs(a - b);
if (SkScalarNearlyZero(base) || diff / base < 0.001f) {
if (nearlyZero(base) || diff / base < 0.001f) {
return 0;
}
@ -223,7 +223,7 @@ void TextLine::paint(SkCanvas* textCanvas) {
this->iterateThroughVisualRuns(false,
[textCanvas, this]
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
if (run->placeholder() != nullptr) {
if (run->placeholderStyle() != nullptr) {
*runWidthInLine = run->advance().fX;
return true;
}
@ -300,7 +300,7 @@ SkRect TextLine::extendHeight(const ClipContext& context) const {
void TextLine::paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
if (context.run->placeholder() != nullptr) {
if (context.run->placeholderStyle() != nullptr) {
return;
}
@ -578,7 +578,7 @@ void TextLine::justify(SkScalar maxWidth) {
return true;
});
SkAssertResult(SkScalarNearlyEqual(shift, maxWidth - textLen));
SkAssertResult(nearlyEqual(shift, maxWidth - textLen));
SkASSERT(whitespacePatches == 0);
this->fWidthWithSpaces += ghostShift;
@ -644,7 +644,7 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
void commitRunBuffer(const RunInfo& info) override {
fRun->fAdvance.fX = info.fAdvance.fX;
fRun->fAdvance.fY = fRun->advance().fY;
fRun->fPlaceholder = nullptr;
fRun->fPlaceholderIndex = std::numeric_limits<size_t>::max();
fRun->fEllipsis = true;
}
@ -673,7 +673,7 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
bool limitToClusters) const {
ClipContext result = { run, 0, run->size(), 0, SkRect::MakeEmpty(), false };
if (run->placeholder() != nullptr || run->fEllipsis) {
if (run->placeholderStyle() != nullptr || run->fEllipsis) {
// Both ellipsis and placeholders can only be measured as one glyph
SkASSERT(textRange == run->textRange());
result.fTextShift = runOffsetInLine;
@ -931,7 +931,7 @@ LineMetrics TextLine::getMetrics() const {
this->iterateThroughVisualRuns(false,
[this, &result]
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
if (run->placeholder() != nullptr) {
if (run->placeholderStyle() != nullptr) {
*runWidthInLine = run->advance().fX;
return true;
}

View File

@ -108,10 +108,10 @@ bool TextStyle::equalsByFonts(const TextStyle& that) const {
fFontStyle == that.fFontStyle &&
fFontFamilies == that.fFontFamilies &&
fFontFeatures == that.fFontFeatures &&
SkScalarNearlyEqual(fLetterSpacing, that.fLetterSpacing) &&
SkScalarNearlyEqual(fWordSpacing, that.fWordSpacing) &&
SkScalarNearlyEqual(fHeight, that.fHeight) &&
SkScalarNearlyEqual(fFontSize, that.fFontSize) &&
nearlyEqual(fLetterSpacing, that.fLetterSpacing) &&
nearlyEqual(fWordSpacing, that.fWordSpacing) &&
nearlyEqual(fHeight, that.fHeight) &&
nearlyEqual(fFontSize, that.fFontSize) &&
fLocale == that.fLocale;
}
@ -180,12 +180,12 @@ void TextStyle::getFontMetrics(SkFontMetrics* metrics) const {
}
bool PlaceholderStyle::equals(const PlaceholderStyle& other) const {
return SkScalarNearlyEqual(fWidth, other.fWidth) &&
SkScalarNearlyEqual(fHeight, other.fHeight) &&
return nearlyEqual(fWidth, other.fWidth) &&
nearlyEqual(fHeight, other.fHeight) &&
fAlignment == other.fAlignment &&
fBaseline == other.fBaseline &&
(fAlignment != PlaceholderAlignment::kBaseline ||
SkScalarNearlyEqual(fBaselineOffset, other.fBaselineOffset));
nearlyEqual(fBaselineOffset, other.fBaselineOffset));
}
} // namespace textlayout

View File

@ -220,7 +220,7 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
continue;
}
lastRun = cluster->run();
if (lastRun->placeholder() != nullptr) {
if (lastRun->placeholderStyle() != nullptr) {
SkASSERT(lastRun->size() == 1);
// Update the placeholder metrics so we can get the placeholder positions later
// and the line metrics (to make sure the placeholder fits)

View File

@ -112,7 +112,7 @@ class TextWrapper {
if (fEnd.cluster() != nullptr &&
fEnd.cluster()->master() != nullptr &&
fEnd.cluster()->run() != nullptr &&
fEnd.cluster()->run()->placeholder() == nullptr &&
fEnd.cluster()->run()->placeholderStyle() == nullptr &&
fWidth > 0) {
fWidth -= (fEnd.cluster()->width() - fEnd.cluster()->trimmedWidth(fEnd.position()));
}

View File

@ -1869,27 +1869,61 @@ protected:
void onDrawContent(SkCanvas* canvas) override {
canvas->drawColor(SK_ColorWHITE);
const char* text = "Overflowing endorsement that has a large lengthy text and is a lot longer than expected";
/*
* Shell: ParagraphStyle: 1.000000 1
Shell: Strut enabled: 0 1.000000 14.000000 400 5 0
Shell: Font Families: 0
Shell: DefaultTextStyle: 16.000000 500 5 0
Shell: Font Families: 1 Roboto
Shell: Font Features: 0
Shell: TextStyle#0: [0:22) 16.000000 500 5 0
Shell: Font Families: 1 Roboto
Shell: Font Features: 0
Shell: TextStyle#1: [25:49) 16.000000 500 5 0
Shell: Font Families: 1 Roboto
Shell: Font Features: 0
Shell: Placeholder#0: [22:25) 32.000000 32.000000 32.000000 0 5
Shell: Placeholder#1: [49:52) 19.000000 41.000000 19.000000 0 4
Shell: Placeholder#2: [52:52) 0.000000 0.000000 0.000000 0 5
Shell: layout('Go to device settings and set up a passcode. ', 280.000000): 280.000000 * 38.000000
*/
auto fontCollection = getFontCollection();
//fontCollection->getParagraphCache()->turnOn(false);
const char* text1 = "Go to device settings ";
const char* text2 = "and set up a passcode.";
ParagraphStyle paragraph_style;
paragraph_style.setEllipsis(u"\u2026");
paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
StrutStyle strut_style;
strut_style.setStrutEnabled(false);
strut_style.setFontSize(14);
strut_style.setForceStrutHeight(false);
strut_style.setHeight(14);
paragraph_style.setStrutStyle(strut_style);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setFontFamilies({SkString("Google Sans")});
text_style.setFontSize(20);
builder.pushStyle(text_style);
builder.addText(text);
auto paragraph = builder.Build();
paragraph->layout(594.0f);
paragraph->paint(canvas, 0, 0);
canvas->translate(0, 200);
paragraph->layout(std::numeric_limits<SkScalar>::max());
paragraph->paint(canvas, 0, 0);
canvas->translate(0, 200);
paragraph->layout(787.0f);
paragraph->paint(canvas, 0, 0);
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(16);
PlaceholderStyle placeholder_style;
{
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(text1);
placeholder_style.fHeight = 32;
placeholder_style.fWidth = 32;
placeholder_style.fBaselineOffset = 32;
placeholder_style.fBaseline = TextBaseline::kAlphabetic;
placeholder_style.fAlignment = PlaceholderAlignment::kMiddle;
builder.addPlaceholder(placeholder_style);
builder.addText(text2);
placeholder_style.fHeight = 19;
placeholder_style.fWidth = 41;
placeholder_style.fBaselineOffset = 19;
placeholder_style.fBaseline = TextBaseline::kAlphabetic;
placeholder_style.fAlignment = PlaceholderAlignment::kTop;
builder.addPlaceholder(placeholder_style);
auto paragraph = builder.Build();
paragraph->layout(280);
paragraph->paint(canvas, 0, 0);
}
}
private:
@ -2089,5 +2123,5 @@ DEF_SAMPLE(return new ParagraphView22();)
DEF_SAMPLE(return new ParagraphView23();)
DEF_SAMPLE(return new ParagraphView24();)
DEF_SAMPLE(return new ParagraphView25();)
DEF_SAMPLE(return new ParagraphView26();)
DEF_SAMPLE(return new ParagraphView27();)
//DEF_SAMPLE(return new ParagraphView26();)
//DEF_SAMPLE(return new ParagraphView27();)

View File

@ -5223,3 +5223,55 @@ DEF_TEST(SkParagraph_MemoryLeak, reporter) {
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
};
DEF_TEST(SkParagraph_FormattingInfinity, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_FormattingInfinity.png");
const char* text = "Some text\nAnother line";
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorBLACK);
TextStyle textStyle;
textStyle.setForegroundColor(paint);
textStyle.setFontFamilies({ SkString("Roboto") });
ParagraphStyle paragraphStyle;
paragraphStyle.setTextStyle(textStyle);
auto draw = [&](const char* prefix, TextAlign textAlign) {
paragraphStyle.setTextAlign(textAlign);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.addText(text);
auto paragraph = builder.Build();
paragraph->layout(SK_ScalarInfinity);
paragraph->paint(canvas.get(), 0, 0);
canvas.get()->translate(0, 100);
};
draw("left", TextAlign::kLeft);
draw("right", TextAlign::kRight);
draw("center", TextAlign::kCenter);
draw("justify", TextAlign::kJustify);
};
DEF_TEST(SkParagraph_Infinity, reporter) {
SkASSERT(nearlyEqual(1, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(1, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(1, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarInfinity) == true);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity) == true);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNaN) == false);
};