[svg] Text object bounding box support

Implement onObjectBoundingBox() for SkSVGText.

Enables use of objectBoundingBox filters effects on text.

Bug: skia:10840
Change-Id: I84ab3df04683cb23073ba9ddc531abe4e5788e5e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/358476
Commit-Queue: Florin Malita <fmalita@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Tyler Denniston <tdenniston@google.com>
diff --git a/modules/svg/src/SkSVGText.cpp b/modules/svg/src/SkSVGText.cpp
index f81eb7c..a5f1609 100644
--- a/modules/svg/src/SkSVGText.cpp
+++ b/modules/svg/src/SkSVGText.cpp
@@ -20,6 +20,7 @@
 #include "modules/svg/include/SkSVGRenderContext.h"
 #include "modules/svg/include/SkSVGValue.h"
 #include "modules/svg/src/SkSVGTextPriv.h"
+#include "src/core/SkTextBlobPriv.h"
 #include "src/utils/SkUTF.h"
 
 namespace {
@@ -558,6 +559,41 @@
     this->onShapeText(ctx, &tctx, this->getXmlSpace());
 }
 
+SkRect SkSVGText::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
+    SkRect bounds = SkRect::MakeEmpty();
+
+    const SkSVGTextContext::ShapedTextCallback compute_bounds =
+        [&bounds](const SkSVGRenderContext& ctx, const sk_sp<SkTextBlob>& blob, const SkPaint*,
+                  const SkPaint*) {
+            if (!blob) {
+                return;
+            }
+
+            SkAutoSTArray<64, SkRect> glyphBounds;
+
+            SkTextBlobRunIterator it(blob.get());
+
+            for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
+                glyphBounds.reset(SkToInt(it.glyphCount()));
+                it.font().getBounds(it.glyphs(), it.glyphCount(), glyphBounds.get(), nullptr);
+
+                SkASSERT(it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning);
+                SkMatrix m;
+                for (uint32_t i = 0; i < it.glyphCount(); ++i) {
+                    m.setRSXform(it.xforms()[i]);
+                    bounds.join(m.mapRect(glyphBounds[i]));
+                }
+            }
+        };
+
+    {
+        SkSVGTextContext tctx(ctx, compute_bounds);
+        this->onShapeText(ctx, &tctx, this->getXmlSpace());
+    }
+
+    return bounds;
+}
+
 void SkSVGTextPath::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* parent_tctx,
                                  SkSVGXmlSpace xs) const {
     SkASSERT(parent_tctx);