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();)