Souped-up SkTextBlob.

Refactored text blob backend for improved performance: instead of using
separate buffers for runs/positions/glyphs, everything is now packed in
a consolidated slab (including the SkTextBlob object itself!).

Benefits:

 * number of allocations per blob construction reduced from ~4 to 1
   (also minimizes internal fragmentation)
 * run record size reduced by 8 bytes

This takes the blob construction overhead down to negligible levels
(for the current Blink uncached textblob implementation).

Unfortunately, the code is much more finicky (run merging in
particular) -- hence the assert spree.

Multi-run blobs are vulnerable to realloc storms but this is not a
problem at the moment because Blink is using one-run blobs 99% of the
time. Will be addressed in the future.

R=mtklein@google.com, reed@google.com, robertphillips@google.com

Committed: https://skia.googlesource.com/skia/+/13645ea0ea87038ebd71be3bd6d53b313069a9e4

Author: fmalita@chromium.org

Review URL: https://codereview.chromium.org/581173003
diff --git a/include/core/SkTextBlob.h b/include/core/SkTextBlob.h
index 8e67723..8ee1d19 100644
--- a/include/core/SkTextBlob.h
+++ b/include/core/SkTextBlob.h
@@ -53,6 +53,8 @@
         kFull_Positioning         = 2  // Point positioning -- two scalars per glyph.
     };
 
+    class RunRecord;
+
     class RunIterator {
     public:
         RunIterator(const SkTextBlob* blob);
@@ -68,21 +70,16 @@
         GlyphPositioning positioning() const;
 
     private:
-        const SkTextBlob* fBlob;
-        int               fIndex;
+        const RunRecord* fCurrentRun;
+        int              fRemainingRuns;
+
+        SkDEBUGCODE(uint8_t* fStorageTop;)
     };
 
-    // A run is a sequence of glyphs sharing the same font metrics and positioning mode.
-    struct Run {
-        uint32_t         count;
-        uint32_t         glyphStart; // index into fGlyphBuffer
-        uint32_t         posStart;   // index into fPosBuffer
-        SkPoint          offset;     // run offset (unsued for fully positioned glyphs)
-        SkPaint          font;
-        GlyphPositioning positioning;
-    };
+    SkTextBlob(int runCount, const SkRect& bounds);
 
-    SkTextBlob(uint16_t* glyphs, SkScalar* pos, const SkTArray<Run>* runs, const SkRect& bounds);
+    virtual ~SkTextBlob();
+    virtual void internal_dispose() const SK_OVERRIDE;
 
     static unsigned ScalarsPerGlyph(GlyphPositioning pos);
 
@@ -90,14 +87,14 @@
     friend class SkTextBlobBuilder;
     friend class TextBlobTester;
 
-    const SkAutoTMalloc<uint16_t>       fGlyphBuffer;
-    const SkAutoTMalloc<SkScalar>       fPosBuffer;
+    const int        fRunCount;
+    const SkRect     fBounds;
+    mutable uint32_t fUniqueID;
 
-    // SkTArray required here for run font destruction.
-    SkAutoTDelete<const SkTArray<Run> > fRuns;
-    const SkRect                        fBounds;
+    SkDEBUGCODE(size_t fStorageSize;)
 
-    mutable uint32_t                    fUniqueID;
+    // The actual payload resides in externally-managed storage, following the object.
+    // (see the .cpp for more details)
 
     typedef SkRefCnt INHERITED;
 };
@@ -108,11 +105,7 @@
  */
 class SK_API SkTextBlobBuilder {
 public:
-    /**
-     *  @param runs The number of runs to be added, if known. This is a storage hint and
-     *              not a limit.
-     */
-    SkTextBlobBuilder(unsigned runs = 0);
+    SkTextBlobBuilder();
 
     ~SkTextBlobBuilder();
 
@@ -180,20 +173,23 @@
     const RunBuffer& allocRunPos(const SkPaint& font, int count, const SkRect* bounds = NULL);
 
 private:
+    void reserve(size_t size);
     void allocInternal(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
                        int count, SkPoint offset, const SkRect* bounds);
-    void ensureRun(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
-                   const SkPoint& offset);
+    bool mergeRun(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
+                  int count, SkPoint offset);
     void updateDeferredBounds();
 
-    SkTDArray<uint16_t>        fGlyphBuffer;
-    SkTDArray<SkScalar>        fPosBuffer;
-    SkTArray<SkTextBlob::Run>* fRuns;
+    SkAutoTMalloc<uint8_t> fStorage;
+    size_t                 fStorageSize;
+    size_t                 fStorageUsed;
 
-    SkRect                     fBounds;
-    bool                       fDeferredBounds;
+    SkRect                 fBounds;
+    int                    fRunCount;
+    bool                   fDeferredBounds;
+    size_t                 fLastRun; // index into fStorage
 
-    RunBuffer                  fCurrentRunBuffer;
+    RunBuffer              fCurrentRunBuffer;
 };
 
 #endif // SkTextBlob_DEFINED