Reland "[skottie] More snug kTop Shaper alignment"

This reverts commit 80658204f417f90711900b2c761d04f9e835c928.

Reason for revert: <INSERT REASONING HERE>

Original change's description:
> Revert "[skottie] More snug kTop Shaper alignment"
> 
> This reverts commit 7ef3e145160fbe99223249b3cff74fb64df6e7e7.
> 
> Reason for revert: TSAN failures (HB?)
> 
> Original change's description:
> > [skottie] More snug kTop Shaper alignment
> > 
> > The current implementation relies on SkShaper ascent values for top
> > text box alignment, but the results are not as visually accurate as AE
> > (or Lottie).
> > 
> > Use the computed tight bounds instead.
> > 
> > Change-Id: I4447a834fe3cae398fc887766daa68802e7f50a5
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206684
> > Reviewed-by: Ben Wagner <bungeman@google.com>
> > Commit-Queue: Florin Malita <fmalita@chromium.org>
> 
> TBR=bungeman@google.com,fmalita@chromium.org
> 
> Change-Id: Icdcca2f6a7b33c8366c1118be2e842ff3978c8bd
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206911
> Reviewed-by: Florin Malita <fmalita@chromium.org>
> Commit-Queue: Florin Malita <fmalita@chromium.org>

TBR=bungeman@google.com,fmalita@chromium.org

Change-Id: I8b72a30b7c2d6d6918f768e59e5d7c987ed5c147
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/207300
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp
index 6c5804e..097d22e 100644
--- a/modules/skottie/src/SkottieAdapter.cpp
+++ b/modules/skottie/src/SkottieAdapter.cpp
@@ -470,6 +470,24 @@
     };
     const auto shape_result = Shaper::Shape(fText.fText, text_desc, fText.fBox);
 
+#if (0)
+    // Enable for text box debugging/visualization.
+    auto box_color = sksg::Color::Make(0xffff0000);
+    box_color->setStyle(SkPaint::kStroke_Style);
+    box_color->setStrokeWidth(1);
+    box_color->setAntiAlias(true);
+
+    auto bounds_color = sksg::Color::Make(0xff00ff00);
+    bounds_color->setStyle(SkPaint::kStroke_Style);
+    bounds_color->setStrokeWidth(1);
+    bounds_color->setAntiAlias(true);
+
+    fRoot->addChild(sksg::Draw::Make(sksg::Rect::Make(fText.fBox),
+                                     std::move(box_color)));
+    fRoot->addChild(sksg::Draw::Make(sksg::Rect::Make(shape_result.computeBounds()),
+                                     std::move(bounds_color)));
+#endif
+
     fTextNode->setBlob(shape_result.fBlob);
     fTextNode->setPosition(shape_result.fPos);
 
diff --git a/modules/skottie/src/SkottieShaper.cpp b/modules/skottie/src/SkottieShaper.cpp
index 782197f..d801455 100644
--- a/modules/skottie/src/SkottieShaper.cpp
+++ b/modules/skottie/src/SkottieShaper.cpp
@@ -73,9 +73,7 @@
         fMaxRunLeading = SkTMax(fMaxRunLeading, metrics.fLeading);
     }
 
-    void commitRunInfo() override {
-        fCurrentPosition.fY -= fMaxRunAscent;
-    }
+    void commitRunInfo() override {}
 
     Buffer runBuffer(const RunInfo& info) override {
         int glyphCount = SkTFitsIn<int>(info.glyphCount) ? info.glyphCount : INT_MAX;
@@ -102,11 +100,6 @@
 
     void commitLine() override {
         fOffset += { 0, fMaxRunDescent + fMaxRunLeading - fMaxRunAscent };
-
-        // Grab the first line ascent for vertical alignment.
-        if (SkScalarIsNaN(fFirstLineAscent)) {
-            fFirstLineAscent = fMaxRunAscent;
-        }
     }
 
     Shaper::Result makeBlob() {
@@ -114,12 +107,14 @@
 
         SkPoint pos {fBox.x(), fBox.y()};
 
+        // By default, first line is vertical-aligned on a baseline of 0.
+        // Perform additional adjustments based on VAlign.
         switch (fDesc.fVAlign) {
-        case Shaper::VAlign::kTop:
-            // Nothing to do (SkShaper default behavior).
-            break;
+        case Shaper::VAlign::kTop: {
+            pos.offset(0, -ComputeBlobBounds(blob).fTop);
+        } break;
         case Shaper::VAlign::kTopBaseline:
-            pos.offset(0, fFirstLineAscent);
+            // Default behavior.
             break;
         case Shaper::VAlign::kCenter: {
             const auto bounds = ComputeBlobBounds(blob).makeOffset(pos.x(), pos.y());
@@ -185,7 +180,6 @@
     SkTextBlobBuilder         fBuilder;
     std::unique_ptr<SkShaper> fShaper;
 
-    SkScalar fFirstLineAscent = SK_ScalarNaN;
     SkScalar fMaxRunAscent;
     SkScalar fMaxRunDescent;
     SkScalar fMaxRunLeading;
diff --git a/modules/skottie/src/SkottieShaper.h b/modules/skottie/src/SkottieShaper.h
index 7005fe9..816141d 100644
--- a/modules/skottie/src/SkottieShaper.h
+++ b/modules/skottie/src/SkottieShaper.h
@@ -30,7 +30,7 @@
     };
 
     enum class VAlign : uint8_t {
-        // Align the first line ascent with the text box top.
+        // Align the first line visual top with the text box top.
         kTop,
         // Align the first line baseline with the text box top.
         kTopBaseline,
diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp
index 4b550c7..34a4073 100644
--- a/modules/skottie/src/SkottieTest.cpp
+++ b/modules/skottie/src/SkottieTest.cpp
@@ -222,7 +222,7 @@
     REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f);
 }
 
-DEF_TEST(Skottie_Shaper, reporter) {
+DEF_TEST(Skottie_Shaper_HAlign, reporter) {
     auto typeface = SkTypeface::MakeDefault();
     REPORTER_ASSERT(reporter, typeface);
 
@@ -281,3 +281,64 @@
         }
     }
 }
+
+DEF_TEST(Skottie_Shaper_VAlign, reporter) {
+    auto typeface = SkTypeface::MakeDefault();
+    REPORTER_ASSERT(reporter, typeface);
+
+    static constexpr struct {
+        SkScalar text_size,
+                 tolerance;
+    } kTestSizes[] = {
+        // These gross tolerances are required for the test to pass on NativeFonts bots.
+        // Might be worth investigating why we need so much slack.
+        {  5, 2.0f },
+        { 10, 2.0f },
+        { 15, 2.4f },
+        { 25, 4.4f },
+    };
+
+    struct {
+        skottie::Shaper::VAlign align;
+        SkScalar                topFactor;
+    } kTestAligns[] = {
+        { skottie::Shaper::VAlign::kTop   , 0.0f },
+        { skottie::Shaper::VAlign::kCenter, 0.5f },
+        // TODO: any way to test kTopBaseline?
+    };
+
+    const SkString text("Foo, bar.\rBaz.");
+    const auto text_box = SkRect::MakeXYWH(100, 100, 1000, 1000); // large-enough to avoid breaks.
+
+
+    for (const auto& tsize : kTestSizes) {
+        for (const auto& talign : kTestAligns) {
+            const skottie::Shaper::TextDesc desc = {
+                typeface,
+                tsize.text_size,
+                SkTextUtils::Align::kCenter_Align,
+                talign.align,
+            };
+
+            const auto shape_result = skottie::Shaper::Shape(text, desc, text_box);
+            REPORTER_ASSERT(reporter, shape_result.fBlob);
+
+            const auto shape_bounds = shape_result.computeBounds();
+            REPORTER_ASSERT(reporter, !shape_bounds.isEmpty());
+
+            const auto v_diff = text_box.height() - shape_bounds.height();
+
+            const auto expected_t = text_box.top() + v_diff * talign.topFactor;
+            REPORTER_ASSERT(reporter,
+                            std::fabs(shape_bounds.top() - expected_t) < tsize.tolerance,
+                            "%f %f %f %f %d", shape_bounds.top(), expected_t, tsize.tolerance,
+                                              tsize.text_size, SkToU32(talign.align));
+
+            const auto expected_b = text_box.bottom() - v_diff * (1 - talign.topFactor);
+            REPORTER_ASSERT(reporter,
+                            std::fabs(shape_bounds.bottom() - expected_b) < tsize.tolerance,
+                            "%f %f %f %f %d", shape_bounds.bottom(), expected_b, tsize.tolerance,
+                                              tsize.text_size, SkToU32(talign.align));
+        }
+    }
+}