Correct line metric style for the first/last lines

Bug: skia:13767
Change-Id: Ib94df2bd1ceb981e442ce5ee2f568a51f2fbc000
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/587676
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/modules/skparagraph/samples/SampleParagraph.cpp b/modules/skparagraph/samples/SampleParagraph.cpp
index 6e95088..d341d6b 100644
--- a/modules/skparagraph/samples/SampleParagraph.cpp
+++ b/modules/skparagraph/samples/SampleParagraph.cpp
@@ -3818,6 +3818,100 @@
     using INHERITED = Sample;
 };
 
+class ParagraphViewLast : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("ParagraphViewLast"); }
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+        auto fontCollection = getFontCollection();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+        ParagraphStyle paragraph_style;
+        paragraph_style.setTextDirection(TextDirection::kLtr);
+        TextStyle text_style;
+        text_style.setColor(SK_ColorBLACK);
+        text_style.setFontFamilies({SkString("Roboto")});
+        text_style.setFontSize(14.0);
+        SkPaint paint;
+        paint.setColor(SK_ColorBLUE);
+        //text_style.setBackgroundColor(paint);
+        TextStyle text_style1;
+        text_style1.setColor(SK_ColorBLACK);
+        text_style1.setFontFamilies({SkString("Roboto")});
+        text_style1.setFontSize(30);
+        text_style1.setHeight(3.0);
+        text_style1.setHeightOverride(true);
+        //paint.setColor(SK_ColorRED);
+        text_style1.setDecorationStyle(TextDecorationStyle::kSolid);
+        text_style1.setDecorationColor(SK_ColorRED);
+        //text_style1.setBackgroundColor(paint);
+        StrutStyle strut_style;
+        strut_style.setFontSize(30);
+        strut_style.setHeight(3.0);
+        strut_style.setHeightOverride(true);
+        strut_style.setFontFamilies({SkString("Roboto")});
+
+        auto draw = [&](const char* text, bool test = false) {
+            if (test) {
+                paragraph_style.setTextHeightBehavior(TextHeightBehavior::kDisableAll);
+                strut_style.setStrutEnabled(true);
+                paragraph_style.setStrutStyle(strut_style);
+            } else {
+                paragraph_style.setTextHeightBehavior(TextHeightBehavior::kAll);
+                strut_style.setStrutEnabled(false);
+                paragraph_style.setStrutStyle(strut_style);
+            }
+            ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+            if (test) {
+                if (text[0] == 'u') {
+                    text_style1.setDecoration(TextDecoration::kUnderline);
+                } else if (text[0] == 'o') {
+                    text_style1.setDecoration(TextDecoration::kOverline);
+                    text_style1.setDecorationColor(SK_ColorGREEN);
+                } else if (text[0] == 's') {
+                    text_style1.setDecoration(TextDecoration::kLineThrough);
+                } else {
+                    text_style1.setDecoration(TextDecoration::kNoDecoration);
+                }
+                builder.pushStyle(text_style1);
+            } else {
+                builder.pushStyle(text_style);
+            }
+            builder.addText(text);
+            builder.pop();
+            auto paragraph = builder.Build();
+            paragraph->layout(width());
+            paragraph->paint(canvas, 0, 0);
+            if (test) {
+                /*
+                auto boxes = paragraph->getRectsForRange(0, 12, RectHeightStyle::kMax, RectWidthStyle::kTight);
+                for (auto& box : boxes) {
+                    SkPaint paint;
+                    paint.setColor(SK_ColorGREEN);
+                    paint.setStyle(SkPaint::kStroke_Style);
+                    paint.setAntiAlias(true);
+                    paint.setStrokeWidth(2);
+                    canvas->drawRect(box.rect, paint);
+                }
+                */
+            }
+            canvas->translate(0, paragraph->getHeight());
+        };
+
+        draw("+++++++++++++++++++");
+        draw("AAA\nBBB\nCCC", true);
+        draw("===================");
+        draw("underline\nBBB\nCCC", true);
+        draw("===================");
+        draw("strike\nBBB\nCCC", true);
+        draw("===================");
+        draw("overline\nBBB\nCCC", true);
+        draw("===================");
+    }
+private:
+    using INHERITED = Sample;
+};
+
 }  // namespace
 
 //////////////////////////////////////////////////////////////////////////////
@@ -3885,4 +3979,4 @@
 DEF_SAMPLE(return new ParagraphView64();)
 DEF_SAMPLE(return new ParagraphView65();)
 DEF_SAMPLE(return new ParagraphView66();)
-
+DEF_SAMPLE(return new ParagraphViewLast();)
diff --git a/modules/skparagraph/src/Decorations.cpp b/modules/skparagraph/src/Decorations.cpp
index 9d4c2e5..f9e5fa9 100644
--- a/modules/skparagraph/src/Decorations.cpp
+++ b/modules/skparagraph/src/Decorations.cpp
@@ -33,7 +33,10 @@
             continue;
         }
 
-        calculatePosition(decoration, context.run->correctAscent());
+        calculatePosition(decoration,
+                          decoration == TextDecoration::kOverline
+                          ? context.run->correctAscent() - context.run->ascent()
+                          : context.run->correctAscent());
 
         calculatePaint(textStyle);
 
@@ -165,7 +168,7 @@
           fPosition -= ascent;
           break;
       case TextDecoration::kOverline:
-          fPosition = 0;
+          fPosition = - ascent;
         break;
       case TextDecoration::kLineThrough: {
           fPosition = (fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutPositionIsValid_Flag)
diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp
index 327112c..c2cdd28 100644
--- a/modules/skparagraph/src/TextLine.cpp
+++ b/modules/skparagraph/src/TextLine.cpp
@@ -458,7 +458,7 @@
     SkAutoCanvasRestore acr(canvas, true);
     canvas->translate(x + this->offset().fX, y + this->offset().fY + style.getBaselineShift());
     Decorations decorations;
-    SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5);
+    SkScalar correctedBaseline = SkScalarFloorToScalar(-this->sizes().rawAscent() + style.getBaselineShift() + 0.5);
     decorations.paint(canvas, style, context, correctedBaseline);
 }
 
@@ -909,10 +909,21 @@
                                                    StyleType styleType,
                                                    const RunStyleVisitor& visitor) const {
 
+    auto correctContext = [&](TextRange textRange, SkScalar textOffsetInRun) -> ClipContext {
+        auto result = this->measureTextInsideOneRun(
+                                        textRange, run, runOffset, textOffsetInRun, false, true);
+        if (styleType == StyleType::kDecorations) {
+            result.clip.fTop = this->sizes().runTop(run, LineMetricStyle::CSS);
+            result.clip.fBottom =
+                    result.clip.fTop +
+                    run->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS);
+        }
+        return result;
+    };
+
     if (run->fEllipsis) {
         // Extra efforts to get the ellipsis text style
-        ClipContext clipContext = this->measureTextInsideOneRun(run->textRange(), run, runOffset,
-                                                                0, false, true);
+        ClipContext clipContext = correctContext(run->textRange(), 0.0f);
         TextRange testRange(run->fClusterStart, run->fClusterStart + run->textRange().width());
         for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
            auto block = fOwner->styles().begin() + index;
@@ -926,8 +937,7 @@
     }
 
     if (styleType == StyleType::kNone) {
-        ClipContext clipContext = this->measureTextInsideOneRun(textRange, run, runOffset,
-                                                                0, false, true);
+        ClipContext clipContext = correctContext(textRange, 0.0f);
         if (clipContext.clip.height() > 0) {
             visitor(textRange, TextStyle(), clipContext);
             return clipContext.clip.width();
@@ -986,9 +996,7 @@
 
         // We have the style and the text
         auto runStyleTextRange = TextRange(start, start + size);
-        // Measure the text
-        ClipContext clipContext = this->measureTextInsideOneRun(runStyleTextRange, run, runOffset,
-                                                                textOffsetInRun, false, true);
+        ClipContext clipContext = correctContext(runStyleTextRange, textOffsetInRun);
         textOffsetInRun += clipContext.clip.width();
         if (clipContext.clip.height() == 0) {
             continue;
@@ -1106,11 +1114,11 @@
     return result;
 }
 
-bool TextLine::isFirstLine() {
+bool TextLine::isFirstLine() const {
     return this == &fOwner->lines().front();
 }
 
-bool TextLine::isLastLine() {
+bool TextLine::isLastLine() const {
     return this == &fOwner->lines().back();
 }
 
diff --git a/modules/skparagraph/src/TextLine.h b/modules/skparagraph/src/TextLine.h
index c74fe46..3f15853 100644
--- a/modules/skparagraph/src/TextLine.h
+++ b/modules/skparagraph/src/TextLine.h
@@ -98,8 +98,8 @@
     void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; }
     InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; }
 
-    bool isFirstLine();
-    bool isLastLine();
+    bool isFirstLine() const;
+    bool isLastLine() const;
     void getRectsForRange(TextRange textRange, RectHeightStyle rectHeightStyle, RectWidthStyle rectWidthStyle, std::vector<TextBox>& boxes);
     void getRectsForPlaceholders(std::vector<TextBox>& boxes);
     PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx);
@@ -118,6 +118,9 @@
     SkScalar metricsWithoutMultiplier(TextHeightBehavior correction);
     void shiftVertically(SkScalar shift) { fOffset.fY += shift; }
 
+    void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; }
+    void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; }
+
     bool endsWithHardLineBreak() const;
 
 private:
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index a94ce21..d17eeeb 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -484,6 +484,17 @@
         fHeight += fEndLine.metrics().height();
         parent->lines().back().setMaxRunMetrics(maxRunMetrics);
     }
+
+    if (parent->lines().empty()) {
+        return;
+    }
+    // Correct line metric styles for the first and for the last lines if needed
+    if (disableFirstAscent) {
+        parent->lines().front().setAscentStyle(LineMetricStyle::Typographic);
+    }
+    if (disableLastDescent) {
+        parent->lines().back().setDescentStyle(LineMetricStyle::Typographic);
+    }
 }
 
 }  // namespace textlayout