layout/relayout with different widths and justification
Change-Id: I2f6d9121a97d3dc12981e0aaf420c56a8f7e05eb
Bug: skia:9851, skia:9875
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/271536
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp
index 954ee4d..ee1a386 100644
--- a/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/modules/skparagraph/src/ParagraphImpl.cpp
@@ -131,8 +131,17 @@
this->fRunShifts.reset();
this->fClusters.reset();
} else if (fState >= kLineBroken && (fOldWidth != floorWidth || fOldHeight != fHeight)) {
- // We can use the results from SkShaper but have to break lines again
+ // We can use the results from SkShaper but have to do EVERYTHING ELSE again
+ this->fClusters.reset();
+ this->fRunShifts.reset();
+ this->resetRunShifts();
fState = kShaped;
+
+ this->buildClusterTable();
+ fState = kClusterized;
+
+ this->markLineBreaks(); // Just because it's on cluster table
+ fState = kMarked;
}
if (fState < kShaped) {
@@ -165,6 +174,7 @@
return;
}
if (fState < kShaped) {
+ this->resetRunShifts();
fState = kShaped;
} else {
layout(floorWidth);
@@ -434,9 +444,6 @@
fLines.reset();
return;
}
- if (effectiveAlign == TextAlign::kJustify) {
- this->resetRunShifts();
- }
for (auto& line : fLines) {
if (&line == &fLines.back() && effectiveAlign == TextAlign::kJustify) {
@@ -1073,9 +1080,17 @@
// TODO: Cache this information
void ParagraphImpl::resetRunShifts() {
- fRunShifts.resize(fRuns.size());
+
+ if (fRunShifts.empty()) {
+ fRunShifts.resize(fRuns.size());
+ }
+
for (size_t i = 0; i < fRuns.size(); ++i) {
- fRunShifts[i].fShifts.push_back_n(fRuns[i].size() + 1, 0.0);
+ auto& run = fRuns[i];
+ auto& shifts = fRunShifts[i];
+ run.resetShifts();
+ shifts.fShifts.reset();
+ shifts.fShifts.push_back_n(run.size() + 1, 0.0);
}
}
diff --git a/modules/skparagraph/src/Run.cpp b/modules/skparagraph/src/Run.cpp
index 4214099..9bcdf90 100644
--- a/modules/skparagraph/src/Run.cpp
+++ b/modules/skparagraph/src/Run.cpp
@@ -334,7 +334,7 @@
// Find the width until the pos and return the min between trimmedWidth and the width(pos)
// We don't have to take in account cluster shift since it's the same for 0 and for pos
auto& run = fMaster->run(fRunIndex);
- return std::min(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
+ return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
}
SkScalar Run::positionX(size_t pos) const {
diff --git a/modules/skparagraph/src/Run.h b/modules/skparagraph/src/Run.h
index 4aa93c6..8ca7755 100644
--- a/modules/skparagraph/src/Run.h
+++ b/modules/skparagraph/src/Run.h
@@ -163,6 +163,11 @@
void commit();
SkRect getBounds(size_t pos) const { return fBounds[pos]; }
+
+ void resetShifts() {
+ for (auto& r: fShifts) { r = 0; }
+ fSpaced = false;
+ }
private:
friend class ParagraphImpl;
friend class TextLine;
diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp
index 262f888..4cf587b 100644
--- a/modules/skparagraph/src/TextLine.cpp
+++ b/modules/skparagraph/src/TextLine.cpp
@@ -136,7 +136,7 @@
}
clusterShift += cluster->width();
for (auto i = cluster->startPos(); i < cluster->endPos(); ++i) {
- auto posX = run->posX(i);
+ auto posX = run->positionX(i);
auto posY = run->posY(i);
auto bounds = run->getBounds(i);
bounds.offset(posX + runShift, posY);
@@ -724,6 +724,9 @@
result.clip.offset(textStartInLine, 0);
if (compareRound(result.clip.fRight, fAdvance.fX) > 0 && !includeGhostSpaces) {
+ // There are few cases when we need it.
+ // The most important one: we measure the text with spaces at the end
+ // and we should ignore these spaces
result.clippingNeeded = true;
result.clip.fRight = fAdvance.fX;
}
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index c9aec45..134c876 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -47,7 +47,13 @@
if (nextWordLength > maxWidth) {
// If the word is too long we can break it right now and hope it's enough
fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextWordLength);
- fTooLongWord = true;
+ if (fClusters.endPos() - fClusters.startPos() > 1 ||
+ fWords.empty()) {
+ fTooLongWord = true;
+ } else {
+ // Even if the word is too long there is a very little space on this line.
+ // let's deal with it on the next line.
+ }
}
if (cluster->width() > maxWidth) {
@@ -255,6 +261,7 @@
needEllipsis && !fHardLineBreak);
softLineMaxIntrinsicWidth += widthWithSpaces;
+
fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
if (fHardLineBreak) {
softLineMaxIntrinsicWidth = 0;
@@ -283,6 +290,7 @@
}
// We finished formatting the text but we need to scan the rest for some numbers
+ // TODO: make it a case of a normal flow
if (fEndLine.endCluster() != nullptr) {
auto lastWordLength = 0.0f;
auto cluster = fEndLine.endCluster();
@@ -291,7 +299,6 @@
if (cluster->isHardBreak()) {
fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
softLineMaxIntrinsicWidth = 0;
-
fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
lastWordLength = 0;
} else if (cluster->isWhitespaces()) {
diff --git a/samplecode/SampleParagraph.cpp b/samplecode/SampleParagraph.cpp
index 9bccc41..2dcc11e 100644
--- a/samplecode/SampleParagraph.cpp
+++ b/samplecode/SampleParagraph.cpp
@@ -2097,6 +2097,117 @@
typedef Sample INHERITED;
};
+class ParagraphView28 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph28"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const char* text = "AAAAA BBBBB CCCCC DDDDD EEEEE FFFFF GGGGG HHHHH IIIII JJJJJ KKKKK LLLLL MMMMM NNNNN OOOOO PPPPP QQQQQ";
+
+ canvas->drawColor(SK_ColorWHITE);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kJustify);
+ auto collection = getFontCollection();
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ auto s = 186;
+ paragraph->layout(360 - s);
+ paragraph->paint(canvas, 0, 0);
+ /*
+ paragraph->layout(360);
+ paragraph->paint(canvas, 0, 0);
+ canvas->translate(0, 400);
+ paragraph->layout(354.333);
+ paragraph->paint(canvas, 0, 0);
+ */
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView29 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph29"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const char* text = "PESTO";
+ canvas->drawColor(SK_ColorWHITE);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(1);
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kCenter);
+ auto collection = getFontCollection();
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(48);
+ text_style.setFontStyle(SkFontStyle::Bold());
+ text_style.setLetterSpacing(3);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ auto w = width() / 2;
+ paragraph->layout(w);
+ paragraph->paint(canvas, 0, 0);
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, width() / 2, paragraph->getHeight()), paint);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView30 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph30"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const char* text = "test text with space at end ";
+ canvas->drawColor(SK_ColorWHITE);
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kCenter);
+ auto collection = getFontCollection();
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(48);
+
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+
+ auto width = this->width();
+ paragraph->layout(width);
+ SkDebugf("%f: %f %f\n", width, paragraph->getMinIntrinsicWidth(), paragraph->getMaxIntrinsicWidth());
+ paragraph->paint(canvas, 0, 0);
+ auto boxes = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ for (auto& b : boxes) {
+ SkDebugf("box[%f:%f * %f:%f] %s\n",
+ b.rect.fLeft, b.rect.fRight, b.rect.fTop, b.rect.fBottom,
+ b.direction == TextDirection::kRtl ? "rtl" : "ltr");
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
//////////////////////////////////////////////////////////////////////////////
DEF_SAMPLE(return new ParagraphView1();)
@@ -2123,5 +2234,8 @@
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();)
+DEF_SAMPLE(return new ParagraphView28();)
+DEF_SAMPLE(return new ParagraphView29();)
+DEF_SAMPLE(return new ParagraphView30();)