1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2014 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextBlobRunIterator.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkReadBuffer.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSafeMath.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTypeface.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkWriteBuffer.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include <limits>
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "text/GrTextBlobCache.h"
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// TODO(fmalita): replace with SkFont.
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass RunFont : SkNoncopyable {
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    RunFont(const SkPaint& paint)
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fSize(paint.getTextSize())
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fScaleX(paint.getTextScaleX())
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTypeface(SkSafeRef(paint.getTypeface()))
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fSkewX(paint.getTextSkewX())
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fAlign(paint.getTextAlign())
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fHinting(paint.getHinting())
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fFlags(paint.getFlags() & kFlagsMask) { }
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void applyToPaint(SkPaint* paint) const {
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTypeface(fTypeface);
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTextSize(fSize);
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTextScaleX(fScaleX);
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTextSkewX(fSkewX);
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool operator==(const RunFont& other) const {
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fTypeface == other.fTypeface
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fSize == other.fSize
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fScaleX == other.fScaleX
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fSkewX == other.fSkewX
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fAlign == other.fAlign
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fHinting == other.fHinting
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            && fFlags == other.fFlags;
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool operator!=(const RunFont& other) const {
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return !(*this == other);
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t flags() const { return fFlags; }
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const static uint32_t kFlagsMask =
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kAntiAlias_Flag          |
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kFakeBoldText_Flag       |
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kLinearText_Flag         |
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kSubpixelText_Flag       |
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kDevKernText_Flag        |
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kLCDRenderText_Flag      |
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kEmbeddedBitmapText_Flag |
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kAutoHinting_Flag        |
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kVerticalText_Flag       |
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kGenA8FromLCD_Flag;
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar                 fSize;
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar                 fScaleX;
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkTypeface>        fTypeface;
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar                 fSkewX;
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t                 fAlign : 2;
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t                 fHinting : 2;
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t                 fFlags : 16;
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typedef SkNoncopyable INHERITED;
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstruct RunFontStorageEquivalent {
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar fSize, fScaleX;
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void*    fTypeface;
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar fSkewX;
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fFlags;
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} // anonymous namespace
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Textblob data is laid out into externally-managed storage as follows:
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    -----------------------------------------------------------------------------
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//   | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    -----------------------------------------------------------------------------
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  Each run record describes a text blob run, and can be used to determine the (implicit)
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  location of the following record.
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Extended Textblob runs have more data after the Pos[] array:
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    -------------------------------------------------------------------------
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    -------------------------------------------------------------------------
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// To determine the length of the extended run data, the TextSize must be read.
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Extended Textblob runs may be mixed with non-extended runs.
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstruct RunRecordStorageEquivalent {
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    RunFont  fFont;
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint  fOffset;
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fCount;
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fFlags;
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(unsigned fMagic;)
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass SkTextBlob::RunRecord {
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    RunRecord(uint32_t count, uint32_t textSize,  const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fFont(font)
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fCount(count)
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fOffset(offset)
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fFlags(pos) {
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDEBUGCODE(fMagic = kRunRecordMagic);
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (textSize > 0) {
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fFlags |= kExtended_Flag;
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *this->textSizePtr() = textSize;
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t glyphCount() const {
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fCount;
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkPoint& offset() const {
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fOffset;
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const RunFont& font() const {
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fFont;
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphPositioning positioning() const {
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint16_t* glyphBuffer() const {
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Glyphs are stored immediately following the record.
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar* posBuffer() const {
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Position scalars follow the (aligned) glyph buffer.
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                           SkAlign4(fCount * sizeof(uint16_t)));
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t* clusterBuffer() const {
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // clusters follow the textSize.
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return isExtended() ? 1 + this->textSizePtr() : nullptr;
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    char* textBuffer() const {
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return isExtended()
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : nullptr;
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static size_t StorageSize(uint32_t glyphCount, uint32_t textSize,
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              SkTextBlob::GlyphPositioning positioning,
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              SkSafeMath* safe) {
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot               posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // RunRecord object + (aligned) glyph buffer + position buffer
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto size = sizeof(SkTextBlob::RunRecord);
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             size = safe->add(size, safe->alignUp(glyphSize, 4));
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             size = safe->add(size, posSize);
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (textSize) {  // Extended run.
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             size = safe->add(size, sizeof(uint32_t));
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             size = safe->add(size, textSize);
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return safe->alignUp(size, sizeof(void*));
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const RunRecord* First(const SkTextBlob* blob) {
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The first record (if present) is stored following the blob object.
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return reinterpret_cast<const RunRecord*>(blob + 1);
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const RunRecord* Next(const RunRecord* run) {
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void validate(const uint8_t* storageTop) const {
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(kRunRecordMagic == fMagic);
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 <= (SkScalar*)NextUnchecked(this));
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (isExtended()) {
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(textSize() > 0);
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      "runrecord_should_stay_packed");
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    friend class SkTextBlobBuilder;
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    enum Flags {
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        kLast_Flag        = 0x04, // set for the last blob run
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        kExtended_Flag    = 0x08, // set for runs with text/cluster info
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const RunRecord* NextUnchecked(const RunRecord* run) {
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkSafeMath safe;
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto res = reinterpret_cast<const RunRecord*>(
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   reinterpret_cast<const uint8_t*>(run)
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(safe);
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return res;
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static size_t PosCount(uint32_t glyphCount,
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           SkTextBlob::GlyphPositioning positioning,
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           SkSafeMath* safe) {
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t* textSizePtr() const {
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // textSize follows the position buffer.
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(isExtended());
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkSafeMath safe;
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(safe);
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return res;
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void grow(uint32_t count) {
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar* initialPosBuffer = posBuffer();
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t initialCount = fCount;
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCount += count;
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Move the initial pos scalars to their new location.
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // memmove, as the buffers may overlap
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        memmove(posBuffer(), initialPosBuffer, copySize);
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool isExtended() const {
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fFlags & kExtended_Flag;
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    RunFont          fFont;
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t         fCount;
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint          fOffset;
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t         fFlags;
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(unsigned fMagic;)
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic int32_t gNextID = 1;
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic int32_t next_id() {
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int32_t id;
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    do {
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        id = sk_atomic_inc(&gNextID);
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } while (id == SK_InvalidGenID);
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return id;
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlob::SkTextBlob(const SkRect& bounds)
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : fBounds(bounds)
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fUniqueID(next_id())
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fCacheID(SK_InvalidUniqueID) {}
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlob::~SkTextBlob() {
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SK_InvalidUniqueID != fCacheID.load()) {
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrTextBlobCache::PostPurgeBlobMessage(fUniqueID, fCacheID);
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const auto* run = RunRecord::First(this);
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    do {
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const auto* nextRun = RunRecord::Next(run);
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        run->~RunRecord();
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        run = nextRun;
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } while (run);
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotunion PositioningAndExtended {
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int32_t intValue;
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct {
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTextBlob::GlyphPositioning positioning;
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint8_t  extended;
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint16_t padding;
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} // namespace
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotunsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // GlyphPositioning values are directly mapped to scalars-per-glyph.
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(pos <= 2);
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return pos;
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkTextBlobRunIterator::done() const {
356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return !fCurrentRun;
357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlobRunIterator::next() {
360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!this->done()) {
363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint32_t SkTextBlobRunIterator::glyphCount() const {
369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->glyphCount();
371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst uint16_t* SkTextBlobRunIterator::glyphs() const {
374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->glyphBuffer();
376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst SkScalar* SkTextBlobRunIterator::pos() const {
379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->posBuffer();
381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst SkPoint& SkTextBlobRunIterator::offset() const {
384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->offset();
386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->positioning();
391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fCurrentRun->font().applyToPaint(paint);
397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint32_t* SkTextBlobRunIterator::clusters() const {
400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->clusterBuffer();
402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint32_t SkTextBlobRunIterator::textSize() const {
404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->textSize();
406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotchar* SkTextBlobRunIterator::text() const {
408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!this->done());
409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRun->textBuffer();
410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkTextBlobRunIterator::isLCD() const {
414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlobBuilder::SkTextBlobBuilder()
418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : fStorageSize(0)
419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fStorageUsed(0)
420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fRunCount(0)
421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fDeferredBounds(false)
422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fLastRun(0) {
423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fBounds.setEmpty();
424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkTextBlobBuilder::~SkTextBlobBuilder() {
427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (nullptr != fStorage.get()) {
428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We are abandoning runs and must destruct the associated font data.
429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The easiest way to accomplish that is to use the blob destructor.
430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->make();
431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect bounds;
436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint;
437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run.font().applyToPaint(&paint);
438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkTextBlob::kDefault_Positioning == run.positioning()) {
440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return bounds.makeOffset(run.offset().x(), run.offset().y());
442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.getTextWidths(run.glyphBuffer(),
446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        run.glyphCount() * sizeof(uint16_t),
447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        nullptr,
448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        glyphBounds.get());
449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             SkTextBlob::kHorizontal_Positioning == run.positioning());
452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // kFull_Positioning       => [ x, y, x, y... ]
453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // kHorizontal_Positioning => [ x, x, x... ]
454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //                            (const y applied by runBounds.offset(run->offset()) later)
455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkScalar horizontalConstY = 0;
456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkScalar* glyphPosX = run.posBuffer();
457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      glyphPosX + 1 : &horizontalConstY;
459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                   posXInc : 0;
462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.setEmpty();
464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (unsigned i = 0; i < run.glyphCount(); ++i) {
465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        glyphPosX += posXInc;
467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        glyphPosY += posYInc;
468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return bounds.makeOffset(run.offset().x(), run.offset().y());
473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(run.glyphCount() > 0);
477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             SkTextBlob::kHorizontal_Positioning == run.positioning());
479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint;
481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run.font().applyToPaint(&paint);
482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkRect fontBounds = paint.getFontBounds();
483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fontBounds.isEmpty()) {
484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // producing useful results in this case.
486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return TightRunBounds(run);
487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Compute the glyph position bbox.
490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect bounds;
491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (run.positioning()) {
492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    case SkTextBlob::kHorizontal_Positioning: {
493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar* glyphPos = run.posBuffer();
494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar minX = *glyphPos;
497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar maxX = *glyphPos;
498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (unsigned i = 1; i < run.glyphCount(); ++i) {
499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar x = glyphPos[i];
500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            minX = SkMinScalar(x, minX);
501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            maxX = SkMaxScalar(x, maxX);
502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bounds.setLTRB(minX, 0, maxX, 0);
505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } break;
506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    case SkTextBlob::kFull_Positioning: {
507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bounds.setBounds(glyphPosPts, run.glyphCount());
511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } break;
512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    default:
513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("unsupported positioning mode");
514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Expand by typeface glyph bounds.
517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.fLeft   += fontBounds.left();
518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.fTop    += fontBounds.top();
519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.fRight  += fontBounds.right();
520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.fBottom += fontBounds.bottom();
521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Offset by run position.
523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return bounds.makeOffset(run.offset().x(), run.offset().y());
524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlobBuilder::updateDeferredBounds() {
527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!fDeferredBounds || fRunCount > 0);
528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fDeferredBounds) {
530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fLastRun >= sizeof(SkTextBlob));
534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                          fLastRun);
536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // FIXME: we should also use conservative bounds for kDefault_Positioning.
538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       TightRunBounds(*run) : ConservativeRunBounds(*run);
540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fBounds.join(runBounds);
541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fDeferredBounds = false;
542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlobBuilder::reserve(size_t size) {
545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSafeMath safe;
546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We don't currently pre-allocate, but maybe someday...
548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (0 == fRunCount) {
553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == fStorage.get());
554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(0 == fStorageSize);
555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(0 == fStorageUsed);
556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the first allocation also includes blob storage
558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fStorageUsed = sizeof(SkTextBlob);
559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStorageSize = safe.add(fStorageUsed, size);
562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // FYI: This relies on everything we store being relocatable, particularly SkPaint.
564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //      Also, this is counting on the underlying realloc to throw when passed max().
565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 uint32_t count, SkPoint offset) {
570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (0 == fLastRun) {
571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(0 == fRunCount);
572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fLastRun >= sizeof(SkTextBlob));
576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                          fLastRun);
578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(run->glyphCount() > 0);
579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (run->textSize() != 0) {
581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (run->positioning() != positioning
585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        || run->font() != font
586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        || (run->glyphCount() + count < run->glyphCount())) {
587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // we can merge same-font/same-positioning runs in the following cases:
591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //   * fully positioned run following another fully positioned run
592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //   * horizontally postioned run following another horizontally positioned run with the same
593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //     y-offset
594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkTextBlob::kFull_Positioning != positioning
595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        && (SkTextBlob::kHorizontal_Positioning != positioning
596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            || run->offset().y() != offset.y())) {
597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSafeMath safe;
601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t sizeDelta =
602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTextBlob::RunRecord::StorageSize(run->glyphCount()        , 0, positioning, &safe);
604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!safe) {
605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->reserve(sizeDelta);
609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // reserve may have realloced
611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t preMergeCount = run->glyphCount();
613fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run->grow(count);
614fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
615fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
616fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
617fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fCurrentRunBuffer.pos = run->posBuffer()
618fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
619fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
620fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStorageUsed += sizeDelta;
621fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
622fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fStorageUsed <= fStorageSize);
623fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run->validate(fStorage.get() + fStorageUsed);
624fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
625fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
626fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
627fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
628fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlobBuilder::allocInternal(const SkPaint &font,
629fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      SkTextBlob::GlyphPositioning positioning,
630fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      int count, int textSize, SkPoint offset,
631fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      const SkRect* bounds) {
632fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (count <= 0 || textSize < 0 || font.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
633fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
634fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
635fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
636fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
637fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
638fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->updateDeferredBounds();
639fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
640fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkSafeMath safe;
641fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
642fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!safe) {
643fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
644fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
645fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
646fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
647fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->reserve(runSize);
648fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
649fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
650fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStorageUsed + runSize <= fStorageSize);
651fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
652fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
653fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
654fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRunBuffer.glyphs = run->glyphBuffer();
655fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRunBuffer.pos = run->posBuffer();
656fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRunBuffer.utf8text = run->textBuffer();
657fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCurrentRunBuffer.clusters = run->clusterBuffer();
658fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
659fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLastRun = fStorageUsed;
660fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fStorageUsed += runSize;
661fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fRunCount++;
662fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
663fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStorageUsed <= fStorageSize);
664fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        run->validate(fStorage.get() + fStorageUsed);
665fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
666fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
667fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
668fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fDeferredBounds) {
669fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (bounds) {
670fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fBounds.join(*bounds);
671fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
672fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fDeferredBounds = true;
673fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
674fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
675fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
676fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
677fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
678fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                    SkScalar x, SkScalar y,
679fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                    int textByteCount,
680fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                    SkString lang,
681fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                    const SkRect* bounds) {
682fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
683fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRunBuffer;
684fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
685fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
686fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
687fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                        SkScalar y,
688fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                        int textByteCount,
689fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                        SkString lang,
690fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                        const SkRect* bounds) {
691fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
692fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        bounds);
693fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
694fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRunBuffer;
695fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
696fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
697fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconst SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
698fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                       int textByteCount,
699fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                       SkString lang,
700fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                       const SkRect *bounds) {
701fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
702fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
703fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fCurrentRunBuffer;
704fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
705fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
706fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkTextBlob> SkTextBlobBuilder::make() {
707fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fRunCount) {
708fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We don't instantiate empty blobs.
709fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(!fStorage.get());
710fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStorageUsed == 0);
711fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStorageSize == 0);
712fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fLastRun == 0);
713fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fBounds.isEmpty());
714fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
715fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
716fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
717fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->updateDeferredBounds();
718fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
719fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Tag the last run as such.
720fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
721fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
722fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
723fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
724fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
725fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
726fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(
727fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkSafeMath safe;
728fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t validateSize = sizeof(SkTextBlob);
729fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
730fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             run = SkTextBlob::RunRecord::Next(run)) {
731fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            validateSize += SkTextBlob::RunRecord::StorageSize(
732fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    run->fCount, run->textSize(), run->positioning(), &safe);
733fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
734fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fRunCount--;
735fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
736fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(validateSize == fStorageUsed);
737fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fRunCount == 0);
738fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(safe);
739fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    )
740fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
741fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStorageUsed = 0;
742fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStorageSize = 0;
743fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fRunCount = 0;
744fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fLastRun = 0;
745fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fBounds.setEmpty();
746fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
747fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return sk_sp<SkTextBlob>(blob);
748fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
749fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
750fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////////////////////////
751fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
752fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkTextBlob::flatten(SkWriteBuffer& buffer) const {
753fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.writeRect(fBounds);
754fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
755fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint runPaint;
756fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextBlobRunIterator it(this);
757fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (!it.done()) {
758fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(it.glyphCount() > 0);
759fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
760fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.write32(it.glyphCount());
761fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        PositioningAndExtended pe;
762fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pe.intValue = 0;
763fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pe.positioning = it.positioning();
764fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((int32_t)it.positioning() == pe.intValue);  // backwards compat.
765fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
766fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t textSize = it.textSize();
767fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pe.extended = textSize > 0;
768fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.write32(pe.intValue);
769fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (pe.extended) {
770fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            buffer.write32(textSize);
771fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
772fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writePoint(it.offset());
773fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // This should go away when switching to SkFont
774fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        it.applyFontToPaint(&runPaint);
775fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writePaint(runPaint);
776fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
777fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
778fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writeByteArray(it.pos(),
779fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
780fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (pe.extended) {
781fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
782fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            buffer.writeByteArray(it.text(), it.textSize());
783fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
784fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
785fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        it.next();
786fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
787fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
788fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Marker for the last run (0 is not a valid glyph count).
789fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.write32(0);
790fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
791fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
792fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
793fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect bounds;
794fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    reader.readRect(&bounds);
795fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
796fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextBlobBuilder blobBuilder;
797fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (;;) {
798fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int glyphCount = reader.read32();
799fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (glyphCount == 0) {
800fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // End-of-runs marker.
801fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
802fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
803fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
804fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        PositioningAndExtended pe;
805fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pe.intValue = reader.read32();
806fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphPositioning pos = pe.positioning;
807fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (glyphCount <= 0 || pos > kFull_Positioning) {
808fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
809fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
810fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int textSize = pe.extended ? reader.read32() : 0;
811fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (textSize < 0) {
812fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
813fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
814fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
815fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint offset;
816fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        reader.readPoint(&offset);
817fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint font;
818fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        reader.readPaint(&font);
819fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
820fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!reader.isValid()) {
821fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
822fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
823fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
824fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkTextBlobBuilder::RunBuffer* buf = nullptr;
825fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (pos) {
826fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kDefault_Positioning:
827fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
828fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                textSize, SkString(), &bounds);
829fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
830fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kHorizontal_Positioning:
831fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
832fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    textSize, SkString(), &bounds);
833fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
834fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kFull_Positioning:
835fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
836fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
837fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            default:
838fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return nullptr;
839fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
840fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
841fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!buf->glyphs ||
842fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            !buf->pos ||
843fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            (pe.extended && (!buf->clusters || !buf->utf8text))) {
844fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
845fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
846fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
847fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
848fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            !reader.readByteArray(buf->pos,
849fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                  glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
850fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return nullptr;
851fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
852fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
853fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (pe.extended) {
854fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t))  ||
855fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                !reader.readByteArray(buf->utf8text, textSize)) {
856fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return nullptr;
857fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
858fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
859fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
860fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
861fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return blobBuilder.make();
862fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
863fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
864fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
865fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBinaryWriteBuffer buffer;
866fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.setSerialProcs(procs);
867fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->flatten(buffer);
868fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
869fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t total = buffer.bytesWritten();
870fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkData> data = SkData::MakeUninitialized(total);
871fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.writeToMemory(data->writable_data());
872fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return data;
873fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
874fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
875fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkData> SkTextBlob::serialize() const {
876fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return this->serialize(SkSerialProcs());
877fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
878fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
879fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
880fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          const SkDeserialProcs& procs) {
881fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkReadBuffer buffer(data, length);
882fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.setDeserialProcs(procs);
883fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkTextBlob::MakeFromBuffer(buffer);
884fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
885fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
886fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length) {
887fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkTextBlob::Deserialize(data, length, SkDeserialProcs());
888fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
889fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
890fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////////////////////////
891fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
892fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
893fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct CatalogState {
894fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTypefaceCatalogerProc fProc;
895fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        void*                   fCtx;
896fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
897fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
898fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkData> catalog_typeface_proc(SkTypeface* face, void* ctx) {
899fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        CatalogState* state = static_cast<CatalogState*>(ctx);
900fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        state->fProc(face, state->fCtx);
901fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t id = face->uniqueID();
902fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkData::MakeWithCopy(&id, sizeof(uint32_t));
903fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
904fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
905fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
906fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkData> SkTextBlob::serialize(SkTypefaceCatalogerProc proc, void* ctx) const {
907fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    CatalogState state = { proc, ctx };
908fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSerialProcs procs;
909fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    procs.fTypefaceProc = catalog_typeface_proc;
910fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    procs.fTypefaceCtx  = &state;
911fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return this->serialize(procs);
912fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
913fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
914fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
915fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct ResolverState {
916fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTypefaceResolverProc  fProc;
917fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        void*                   fCtx;
918fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
919fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
920fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkTypeface> resolver_typeface_proc(const void* data, size_t length, void* ctx) {
921fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (length != 4) {
922fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
923fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
924fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
925fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ResolverState* state = static_cast<ResolverState*>(ctx);
926fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t id;
927fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        memcpy(&id, data, length);
928fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return state->fProc(id, state->fCtx);
929fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
930fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
931fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
932fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
933fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkTypefaceResolverProc proc, void* ctx) {
934fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ResolverState state = { proc, ctx };
935fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDeserialProcs procs;
936fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    procs.fTypefaceProc = resolver_typeface_proc;
937fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    procs.fTypefaceCtx  = &state;
938fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return Deserialize(data, length, procs);
939fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
940