Fix cluster reporting from shaper.

Bug: skia:8420
Change-Id: I7eea8c6b3af2153a1ac1189782fc6cbaaf9ee5c6
Reviewed-on: https://skia-review.googlesource.com/c/190821
Reviewed-by: Hal Canary <halcanary@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp
index 1daac2b..9c04b9f 100644
--- a/modules/skottie/src/SkottieAdapter.cpp
+++ b/modules/skottie/src/SkottieAdapter.cpp
@@ -331,7 +331,7 @@
             : fAlignFactor(AlignFactor(align)) {}
 
         Buffer newRunBuffer(const RunInfo& info, const SkFont& font, int glyphCount,
-                            int utf8textCount) override {
+                            SkSpan<const char> utf8) override {
             fPendingLineAdvance += info.fAdvance;
 
             auto& run = fPendingLineRuns.emplace_back(font, info, glyphCount);
@@ -340,10 +340,11 @@
                 run.fGlyphs   .data(),
                 run.fPositions.data(),
                 nullptr,
-                nullptr,
             };
         }
 
+        void commitRun() override { }
+
         void commitLine() override {
             SkScalar line_spacing = 0;
 
diff --git a/modules/skshaper/include/SkShaper.h b/modules/skshaper/include/SkShaper.h
index 06ffe68..a6fa72e 100644
--- a/modules/skshaper/include/SkShaper.h
+++ b/modules/skshaper/include/SkShaper.h
@@ -11,6 +11,7 @@
 #include <memory>
 
 #include "SkPoint.h"
+#include "SkSpan.h"
 #include "SkTextBlob.h"
 #include "SkTypeface.h"
 
@@ -41,14 +42,15 @@
         struct Buffer {
             SkGlyphID* glyphs;    // required
             SkPoint*   positions; // required
-            char*      utf8text;  // optional
             uint32_t*  clusters;  // optional
         };
 
         // Callback per glyph run.
         virtual Buffer newRunBuffer(const RunInfo&, const SkFont&, int glyphCount,
-                                    int utf8textCount) = 0;
+                                    SkSpan<const char> utf8) = 0;
 
+        // Called after run information is filled out.
+        virtual void commitRun() = 0;
         // Callback per line.
         virtual void commitLine() = 0;
     };
@@ -75,14 +77,19 @@
  */
 class SkTextBlobBuilderRunHandler final : public SkShaper::RunHandler {
 public:
+    SkTextBlobBuilderRunHandler(const char* utf8Text) : fUtf8Text(utf8Text) {}
     sk_sp<SkTextBlob> makeBlob();
 
-    SkShaper::RunHandler::Buffer newRunBuffer(const RunInfo&, const SkFont&, int, int) override;
-
+    SkShaper::RunHandler::Buffer newRunBuffer(const RunInfo&, const SkFont&, int, SkSpan<const char>) override;
+    void commitRun() override;
     void commitLine() override {}
 
 private:
     SkTextBlobBuilder fBuilder;
+    char const * const fUtf8Text;
+    uint32_t* fClusters;
+    int fClusterOffset;
+    int fGlyphCount;
 };
 
 #endif  // SkShaper_DEFINED
diff --git a/modules/skshaper/src/SkShaper.cpp b/modules/skshaper/src/SkShaper.cpp
index e08b7b3..1dd8c5c 100644
--- a/modules/skshaper/src/SkShaper.cpp
+++ b/modules/skshaper/src/SkShaper.cpp
@@ -6,21 +6,33 @@
  */
 
 #include "SkShaper.h"
-
+#include "SkSpan.h"
 #include "SkTextBlobPriv.h"
 
 SkShaper::RunHandler::Buffer SkTextBlobBuilderRunHandler::newRunBuffer(const RunInfo&,
                                                                        const SkFont& font,
                                                                        int glyphCount,
-                                                                       int textCount) {
+                                                                       SkSpan<const char> utf8) {
     const auto& runBuffer = SkTextBlobBuilderPriv::AllocRunTextPos(&fBuilder, font, glyphCount,
-                                                                   textCount, SkString());
+                                                                   utf8.size(), SkString());
+    if (runBuffer.utf8text && fUtf8Text) {
+        memcpy(runBuffer.utf8text, utf8.data(), utf8.size());
+    }
+    fClusters = runBuffer.clusters;
+    fGlyphCount = glyphCount;
+    fClusterOffset = utf8.data() - fUtf8Text;
+
     return { runBuffer.glyphs,
              runBuffer.points(),
-             runBuffer.utf8text,
              runBuffer.clusters };
 }
 
+void SkTextBlobBuilderRunHandler::commitRun() {
+    for (int i = 0; i < fGlyphCount; ++i) {
+        fClusters[i] -= fClusterOffset;
+    }
+}
+
 sk_sp<SkTextBlob> SkTextBlobBuilderRunHandler::makeBlob() {
     return fBuilder.make();
 }
diff --git a/modules/skshaper/src/SkShaper_harfbuzz.cpp b/modules/skshaper/src/SkShaper_harfbuzz.cpp
index 57f3df6..22a53a6 100644
--- a/modules/skshaper/src/SkShaper_harfbuzz.cpp
+++ b/modules/skshaper/src/SkShaper_harfbuzz.cpp
@@ -15,6 +15,7 @@
 #include "SkRefCnt.h"
 #include "SkScalar.h"
 #include "SkShaper.h"
+#include "SkSpan.h"
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkTArray.h"
@@ -455,18 +456,17 @@
     bool fUnsafeToBreak;
 };
 struct ShapedRun {
-    ShapedRun(const char* utf8Start, const char* utf8End, int numGlyphs, const SkFont& font,
-              UBiDiLevel level, std::unique_ptr<ShapedGlyph[]> glyphs)
-        : fUtf8Start(utf8Start), fUtf8End(utf8End), fNumGlyphs(numGlyphs), fFont(font)
-        , fLevel(level), fGlyphs(std::move(glyphs))
+    ShapedRun(SkSpan<const char> utf8, const SkFont& font, UBiDiLevel level,
+              std::unique_ptr<ShapedGlyph[]> glyphs, int numGlyphs)
+        : fUtf8(utf8), fFont(font), fLevel(level)
+        , fGlyphs(std::move(glyphs)), fNumGlyphs(numGlyphs)
     {}
 
-    const char* fUtf8Start;
-    const char* fUtf8End;
-    int fNumGlyphs;
+    SkSpan<const char> fUtf8;
     SkFont fFont;
     UBiDiLevel fLevel;
     std::unique_ptr<ShapedGlyph[]> fGlyphs;
+    int fNumGlyphs;
     SkVector fAdvance = { 0, 0 };
 };
 struct ShapedLine {
@@ -481,16 +481,12 @@
 static void append(SkShaper::RunHandler* handler, const SkShaper::RunHandler::RunInfo& runInfo,
                    const ShapedRun& run, int start, int end,
                    SkPoint* p) {
-    unsigned len = end - start;
+    const unsigned len = end - start;
 
-    const auto buffer = handler->newRunBuffer(runInfo, run.fFont, len, run.fUtf8End - run.fUtf8Start);
+    const auto buffer = handler->newRunBuffer(runInfo, run.fFont, len, run.fUtf8);
     SkASSERT(buffer.glyphs);
     SkASSERT(buffer.positions);
 
-    if (buffer.utf8text) {
-        memcpy(buffer.utf8text, run.fUtf8Start, run.fUtf8End - run.fUtf8Start);
-    }
-
     for (unsigned i = 0; i < len; i++) {
         // Glyphs are in logical order, but output ltr since PDF readers seem to expect that.
         const ShapedGlyph& glyph = run.fGlyphs[is_LTR(run.fLevel) ? start + i : end - 1 - i];
@@ -502,6 +498,7 @@
         p->fX += glyph.fAdvance.fX;
         p->fY += glyph.fAdvance.fY;
     }
+    handler->commitRun();
 }
 
 static void emit(const ShapedLine& line, SkShaper::RunHandler* handler,
@@ -747,7 +744,7 @@
         utf8Start = utf8End;
         utf8End = runSegmenter.endOfCurrentRun();
 
-        ShapedRun model(nullptr, nullptr, 0, SkFont(), 0, nullptr);
+        ShapedRun model(SkSpan<const char>(), SkFont(), 0, nullptr, 0);
         bool modelNeedsRegenerated = true;
         int modelOffset = 0;
 
@@ -805,7 +802,7 @@
                 }
             }
 
-            ShapedRun best(nullptr, nullptr, 0, SkFont(), 0, nullptr);
+            ShapedRun best(SkSpan<const char>(), SkFont(), 0, nullptr, 0);
             best.fAdvance = { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity };
             SkScalar widthLeft = width - line.fAdvance.fX;
 
@@ -817,15 +814,14 @@
 
                 // TODO: adjust breakIteratorCurrent by ignorable whitespace
                 ShapedRun candidate = modelText[breakIteratorCurrent + modelTextOffset].glyphLen
-                                    ? ShapedRun(utf8Start, utf8Start + breakIteratorCurrent,
-                                                modelText[breakIteratorCurrent + modelTextOffset].glyphLen - modelOffset,
-                                                *font->currentFont(),
-                                                bidi->currentLevel(),
-                                                std::unique_ptr<ShapedGlyph[]>())
+                                    ? ShapedRun(SkSpan<const char>(utf8Start, breakIteratorCurrent),
+                                                *font->currentFont(), bidi->currentLevel(),
+                                                std::unique_ptr<ShapedGlyph[]>(),
+                                                modelText[breakIteratorCurrent + modelTextOffset].glyphLen - modelOffset)
                                     : shape(utf8, utf8Bytes,
                                             utf8Start, utf8Start + breakIteratorCurrent,
                                             bidi, language, script, font);
-                if (!candidate.fUtf8Start) {
+                if (!candidate.fUtf8.data()) {
                     //report error
                     return point;
                 }
@@ -834,10 +830,10 @@
                 }
                 auto score = [widthLeft](const ShapedRun& run) -> SkScalar {
                     if (run.fAdvance.fX < widthLeft) {
-                        if (run.fUtf8Start == nullptr) {
+                        if (run.fUtf8.data() == nullptr) {
                             return SK_ScalarNegativeInfinity;
                         } else {
-                            return run.fUtf8End - run.fUtf8Start;
+                            return run.fUtf8.size();
                         }
                     } else {
                         return widthLeft - run.fAdvance.fX;
@@ -859,12 +855,12 @@
                     memcpy(best.fGlyphs.get(), model.fGlyphs.get() + modelOffset,
                            best.fNumGlyphs * sizeof(ShapedGlyph));
                     modelOffset += best.fNumGlyphs;
-                    modelTextOffset += best.fUtf8End - best.fUtf8Start;
+                    modelTextOffset += best.fUtf8.size();
                     modelTextAdvanceOffset += best.fAdvance;
                 } else {
                     modelNeedsRegenerated = true;
                 }
-                utf8Start = best.fUtf8End;
+                utf8Start = best.fUtf8.end();
                 line.fAdvance += best.fAdvance;
                 line.runs.emplace_back(std::move(best));
 
@@ -1115,7 +1111,7 @@
                                 const ScriptRunIterator* script,
                                 const FontRunIterator* font) const
 {
-    ShapedRun run(nullptr, nullptr, 0, SkFont(), 0, nullptr);
+    ShapedRun run(SkSpan<const char>(), SkFont(), 0, nullptr, 0);
 
     hb_buffer_t* buffer = fBuffer.get();
     SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer);
@@ -1173,9 +1169,9 @@
         return run;
     }
 
-    run = ShapedRun(utf8Start, utf8End, len, *font->currentFont(),
-                    bidi->currentLevel(),
-                    std::unique_ptr<ShapedGlyph[]>(new ShapedGlyph[len]));
+    run = ShapedRun(SkSpan<const char>(utf8Start, utf8runLength),
+                    *font->currentFont(), bidi->currentLevel(),
+                    std::unique_ptr<ShapedGlyph[]>(new ShapedGlyph[len]), len);
     int scaleX, scaleY;
     hb_font_get_scale(font->currentHBFont(), &scaleX, &scaleY);
     double textSizeY = run.fFont.getSize() / scaleY;
diff --git a/modules/skshaper/src/SkShaper_primitive.cpp b/modules/skshaper/src/SkShaper_primitive.cpp
index 8980932..4b371c0 100644
--- a/modules/skshaper/src/SkShaper_primitive.cpp
+++ b/modules/skshaper/src/SkShaper_primitive.cpp
@@ -58,15 +58,12 @@
         metrics.fDescent,
         metrics.fLeading,
     };
-    const auto buffer = handler->newRunBuffer(info, font, glyphCount, textBytes);
+    const auto buffer = handler->newRunBuffer(info, font, glyphCount,
+                                              SkSpan<const char>(utf8text, textBytes));
     SkAssertResult(font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, buffer.glyphs,
                                      glyphCount) == glyphCount);
     font.getPos(buffer.glyphs, glyphCount, buffer.positions, point);
 
-    if (buffer.utf8text) {
-        memcpy(buffer.utf8text, utf8text, textBytes);
-    }
-
     if (buffer.clusters) {
         const char* txtPtr = utf8text;
         for (int i = 0; i < glyphCount; ++i) {
@@ -76,7 +73,7 @@
             SkASSERT(txtPtr <= utf8text + textBytes);
         }
     }
-
+    handler->commitRun();
     handler->commitLine();
 
     return point + SkVector::Make(0, metrics.fDescent + metrics.fLeading);