add bounds generation code to SkGlyphRun

Change-Id: I29c269bce86cb50fea57d44d7760b60326aa56a8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/396436
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 70190d1..243b4cb 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -39,7 +39,7 @@
         size_t len = strlen(gText);
         SkGlyphRunBuilder builder;
         SkPaint paint;
-        auto glyphRunList = builder.textToGlyphRunList(font, gText, len, {100, 100});
+        auto glyphRunList = builder.textToGlyphRunList(font, paint, gText, len, {100, 100});
         SkASSERT(!glyphRunList.empty());
         SkSurfaceProps props;
         if (canvas) { canvas->getProps(&props); }
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index e06094c..a8da995 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2278,12 +2278,13 @@
 
 void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                               const SkPaint& paint) {
-    const SkRect bounds = blob->bounds().makeOffset(x, y);
-    if (this->internalQuickReject(bounds, paint)) {
+    const SkRect blobBounds = blob->bounds().makeOffset(x, y);
+    if (this->internalQuickReject(blobBounds, paint)) {
         return;
     }
 
     auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(*blob, {x, y});
+    SkRect bounds = glyphRunList.sourceBounds();
     AutoLayerForImageFilter layer(this, paint, &bounds);
     this->topDevice()->drawGlyphRunList(glyphRunList, layer.paint());
 }
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index ef67573..c2fc270 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -448,7 +448,7 @@
                                                     const SkPaint& paint) {
     for (const SkGlyphRun& run : glyphRunList) {
         if (run.scaledRotations().empty()) {
-            this->drawGlyphRunList(SkGlyphRunList{run}, paint);
+            this->drawGlyphRunList(SkGlyphRunList{run, run.sourceBounds(paint)}, paint);
         } else {
             SkPoint origin = glyphRunList.origin();
             SkPoint sharedPos{0, 0};    // we're at the origin
@@ -477,7 +477,8 @@
                 SkPaint invertingPaint{paint};
                 invertingPaint.setShader(make_post_inverse_lm(paint.getShader(), glyphToLocal));
                 this->setLocalToDevice(originalLocalToDevice * SkM44(glyphToLocal));
-                this->drawGlyphRunList(SkGlyphRunList{glyphRun}, invertingPaint);
+                this->drawGlyphRunList(
+                        SkGlyphRunList{glyphRun, glyphRun.sourceBounds(paint)}, invertingPaint);
             }
             this->setLocalToDevice(originalLocalToDevice);
         }
diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp
index b95a0a7..a50dfdb 100644
--- a/src/core/SkGlyphRun.cpp
+++ b/src/core/SkGlyphRun.cpp
@@ -39,19 +39,87 @@
     , fClusters{that.fClusters}
     , fFont{font} {}
 
+SkRect SkGlyphRun::sourceBounds(const SkPaint& paint) const {
+    SkASSERT(this->runSize() > 0);
+    const SkRect fontBounds = SkFontPriv::GetFontBounds(fFont);
+
+    if (fontBounds.isEmpty()) {
+        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
+        // producing useful results in this case.
+        SkStrikeSpec strikeSpec = SkStrikeSpec::MakeCanonicalized(fFont, &paint);
+        SkBulkGlyphMetrics metrics{strikeSpec};
+        SkSpan<const SkGlyph*> glyphs = metrics.glyphs(this->glyphsIDs());
+        if (fScaledRotations.empty()) {
+            // No RSXForm data - glyphs x/y aligned.
+            auto scaleAndTranslateRect =
+                [scale = strikeSpec.strikeToSourceRatio()](const SkRect& in, const SkPoint& pos) {
+                    return SkRect::MakeLTRB(in.left()   * scale + pos.x(),
+                                            in.top()    * scale + pos.y(),
+                                            in.right()  * scale + pos.x(),
+                                            in.bottom() * scale + pos.y());
+                };
+
+            SkRect bounds = SkRect::MakeEmpty();
+            for (auto [pos, glyph] : SkMakeZip(this->positions(), glyphs)) {
+                if (SkRect r = glyph->rect(); !r.isEmpty()) {
+                    bounds.join(scaleAndTranslateRect(r, pos));
+                }
+            }
+            return bounds;
+        } else {
+            // RSXForm - glyphs can be any scale or rotation.
+            SkScalar scale = strikeSpec.strikeToSourceRatio();
+            SkRect bounds = SkRect::MakeEmpty();
+            for (auto [pos, scaleRotate, glyph] :
+                    SkMakeZip(this->positions(), fScaledRotations, glyphs)) {
+                if (!glyph->rect().isEmpty()) {
+                    SkMatrix xform = SkMatrix().setRSXform(
+                            SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
+                    xform.preScale(scale, scale);
+                    bounds.join(xform.mapRect(glyph->rect()));
+                }
+            }
+            return bounds;
+        }
+    }
+
+    // Use conservative bounds. All glyph have a box of fontBounds size.
+    if (fScaledRotations.empty()) {
+        SkRect bounds;
+        bounds.setBounds(this->positions().data(), this->positions().count());
+        bounds.fLeft   += fontBounds.left();
+        bounds.fTop    += fontBounds.top();
+        bounds.fRight  += fontBounds.right();
+        bounds.fBottom += fontBounds.bottom();
+        return bounds;
+    } else {
+        // RSXForm case glyphs can be any scale or rotation.
+        SkRect bounds;
+        bounds.setEmpty();
+        for (auto [pos, scaleRotate] : SkMakeZip(this->positions(), fScaledRotations)) {
+            const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
+            bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
+        }
+        return bounds;
+    }
+}
+
 // -- SkGlyphRunList -------------------------------------------------------------------------------
 SkGlyphRunList::SkGlyphRunList() = default;
 SkGlyphRunList::SkGlyphRunList(
         const SkTextBlob* blob,
+        SkRect bounds,
         SkPoint origin,
         SkSpan<const SkGlyphRun> glyphRunList)
         : fGlyphRuns{glyphRunList}
         , fOriginalTextBlob{blob}
+        , fSourceBounds{bounds}
         , fOrigin{origin} { }
 
-SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun)
+SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun, const SkRect& bounds)
         : fGlyphRuns{SkSpan<const SkGlyphRun>{&glyphRun, 1}}
         , fOriginalTextBlob{nullptr}
+        , fSourceBounds{bounds}
         , fOrigin{SkPoint::Make(0, 0)} {}
 
 uint64_t SkGlyphRunList::uniqueID() const {
@@ -90,9 +158,11 @@
 }
 
 const SkGlyphRunList& SkGlyphRunBuilder::textToGlyphRunList(
-        const SkFont& font, const void* bytes, size_t byteLength, SkPoint origin,
+        const SkFont& font, const SkPaint& paint,
+        const void* bytes, size_t byteLength, SkPoint origin,
         SkTextEncoding encoding) {
     auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
+    SkRect bounds = SkRect::MakeEmpty();
     if (!glyphIDs.empty()) {
         this->initialize(glyphIDs.size());
         SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
@@ -102,9 +172,10 @@
                            SkSpan<const char>{},
                            SkSpan<const uint32_t>{},
                            SkSpan<const SkVector>{});
+        bounds = fGlyphRunListStorage.front().sourceBounds(paint);
     }
 
-    return this->makeGlyphRunList(nullptr, origin);
+    return this->makeGlyphRunList(nullptr, bounds.makeOffset(origin), origin);
 }
 
 const SkGlyphRunList& SkGlyphRunBuilder::blobToGlyphRunList(
@@ -163,7 +234,7 @@
                 scaledRotations);
     }
 
-    return this->makeGlyphRunList(&blob, origin);
+    return this->makeGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin);
 }
 
 void SkGlyphRunBuilder::initialize(int totalRunSize) {
@@ -237,8 +308,8 @@
     }
 }
 
-const SkGlyphRunList& SkGlyphRunBuilder::makeGlyphRunList(const SkTextBlob* blob, SkPoint origin) {
-
+const SkGlyphRunList& SkGlyphRunBuilder::makeGlyphRunList(
+        const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) {
     fGlyphRunList.~SkGlyphRunList();
-    return *new (&fGlyphRunList) SkGlyphRunList{blob, origin, SkSpan(fGlyphRunListStorage)};
+    return *new (&fGlyphRunList) SkGlyphRunList{blob, bounds, origin, SkSpan(fGlyphRunListStorage)};
 }
diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h
index 45a3f66..c972031 100644
--- a/src/core/SkGlyphRun.h
+++ b/src/core/SkGlyphRun.h
@@ -44,6 +44,7 @@
     SkSpan<const uint32_t> clusters() const { return fClusters; }
     SkSpan<const char> text() const { return fText; }
     SkSpan<const SkVector> scaledRotations() const { return fScaledRotations; }
+    SkRect sourceBounds(const SkPaint& paint) const;
 
 private:
     // GlyphIDs and positions.
@@ -66,10 +67,11 @@
     // Blob maybe null.
     SkGlyphRunList(
             const SkTextBlob* blob,
+            SkRect bounds,
             SkPoint origin,
             SkSpan<const SkGlyphRun> glyphRunList);
 
-    SkGlyphRunList(const SkGlyphRun& glyphRun);
+    SkGlyphRunList(const SkGlyphRun& glyphRun, const SkRect& bounds);
 
     uint64_t uniqueID() const;
     bool anyRunsLCD() const;
@@ -93,6 +95,7 @@
     }
 
     SkPoint origin() const { return fOrigin; }
+    SkRect sourceBounds() const { return fSourceBounds; }
     const SkTextBlob* blob() const { return fOriginalTextBlob; }
 
     auto begin() -> decltype(fGlyphRuns.begin())               { return fGlyphRuns.begin();  }
@@ -106,13 +109,15 @@
 private:
     // The text blob is needed to hookup the call back that the SkTextBlob destructor calls. It
     // should be used for nothing else
-    const SkTextBlob*  fOriginalTextBlob{nullptr};
-    SkPoint fOrigin = {0, 0};
+    const SkTextBlob* fOriginalTextBlob{nullptr};
+    const SkRect fSourceBounds{SkRect::MakeEmpty()};
+    const SkPoint fOrigin = {0, 0};
 };
 
 class SkGlyphRunBuilder {
 public:
     const SkGlyphRunList& textToGlyphRunList(const SkFont& font,
+                                             const SkPaint& paint,
                                              const void* bytes,
                                              size_t byteLength,
                                              SkPoint origin,
@@ -135,10 +140,8 @@
             SkSpan<const uint32_t> clusters,
             SkSpan<const SkVector> scaledRotations);
 
-    const SkGlyphRunList& makeGlyphRunList(const SkTextBlob* blob, SkPoint origin);
-
-    SkPoint* simplifyTextBlobIgnoringRSXForm(const SkTextBlobRunIterator& it,
-                                             SkPoint* positionsCursor);
+    const SkGlyphRunList& makeGlyphRunList(
+            const SkTextBlob* blob, const SkRect& bounds, SkPoint origin);
 
     int fMaxTotalRunSize{0};
     SkAutoTMalloc<SkPoint> fPositions;
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index dcaf5e3..2553c95 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -497,7 +497,7 @@
     drawMatrix.preTranslate(x, y);
     auto drawOrigin = SkPoint::Make(x, y);
     SkGlyphRunBuilder builder;
-    auto glyphRunList = builder.textToGlyphRunList(font, text, textLen, drawOrigin);
+    auto glyphRunList = builder.textToGlyphRunList(font, skPaint, text, textLen, drawOrigin);
     if (glyphRunList.empty()) {
         return nullptr;
     }