xref: /third_party/skia/src/core/SkTextBlob.cpp (revision cb93a386)
1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkRSXform.h"
9#include "include/core/SkTextBlob.h"
10#include "include/core/SkTypeface.h"
11#include "src/core/SkFontPriv.h"
12#include "src/core/SkGlyphRun.h"
13#include "src/core/SkPaintPriv.h"
14#include "src/core/SkReadBuffer.h"
15#include "src/core/SkSafeMath.h"
16#include "src/core/SkStrikeCache.h"
17#include "src/core/SkStrikeSpec.h"
18#include "src/core/SkTextBlobPriv.h"
19#include "src/core/SkWriteBuffer.h"
20
21#include <atomic>
22#include <limits>
23#include <new>
24
25#if SK_SUPPORT_GPU
26#include "src/gpu/text/GrTextBlobCache.h"
27#endif
28
29namespace {
30struct RunFontStorageEquivalent {
31    SkScalar fSize, fScaleX;
32    void*    fTypeface;
33    SkScalar fSkewX;
34    uint32_t fFlags;
35};
36static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
37}  // namespace
38
39size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
40                                          SkTextBlob::GlyphPositioning positioning,
41                                          SkSafeMath* safe) {
42    static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
43
44    auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
45            posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
46
47    // RunRecord object + (aligned) glyph buffer + position buffer
48    auto size = sizeof(SkTextBlob::RunRecord);
49    size = safe->add(size, safe->alignUp(glyphSize, 4));
50    size = safe->add(size, posSize);
51
52    if (textSize) {  // Extended run.
53        size = safe->add(size, sizeof(uint32_t));
54        size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
55        size = safe->add(size, textSize);
56    }
57
58    return safe->alignUp(size, sizeof(void*));
59}
60
61const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) {
62    // The first record (if present) is stored following the blob object.
63    // (aligned up to make the RunRecord aligned too)
64    return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
65}
66
67const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
68    return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
69}
70
71namespace {
72struct RunRecordStorageEquivalent {
73    SkFont   fFont;
74    SkPoint  fOffset;
75    uint32_t fCount;
76    uint32_t fFlags;
77    SkDEBUGCODE(unsigned fMagic;)
78};
79}  // namespace
80
81void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
82    SkASSERT(kRunRecordMagic == fMagic);
83    SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
84
85    SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
86    SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
87             <= (SkScalar*)NextUnchecked(this));
88    if (isExtended()) {
89        SkASSERT(textSize() > 0);
90        SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
91        SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
92        SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
93    }
94    static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
95                  "runrecord_should_stay_packed");
96}
97
98const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
99    SkSafeMath safe;
100    auto res = reinterpret_cast<const RunRecord*>(
101            reinterpret_cast<const uint8_t*>(run)
102            + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
103    SkASSERT(safe);
104    return res;
105}
106
107size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
108                                       SkTextBlob::GlyphPositioning positioning,
109                                       SkSafeMath* safe) {
110    return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
111}
112
113uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
114    // textSize follows the position buffer.
115    SkASSERT(isExtended());
116    SkSafeMath safe;
117    auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
118    SkASSERT(safe);
119    return res;
120}
121
122void SkTextBlob::RunRecord::grow(uint32_t count) {
123    SkScalar* initialPosBuffer = posBuffer();
124    uint32_t initialCount = fCount;
125    fCount += count;
126
127    // Move the initial pos scalars to their new location.
128    size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
129    SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
130
131    // memmove, as the buffers may overlap
132    memmove(posBuffer(), initialPosBuffer, copySize);
133}
134
135static int32_t next_id() {
136    static std::atomic<int32_t> nextID{1};
137    int32_t id;
138    do {
139        id = nextID.fetch_add(1, std::memory_order_relaxed);
140    } while (id == SK_InvalidGenID);
141    return id;
142}
143
144SkTextBlob::SkTextBlob(const SkRect& bounds)
145    : fBounds(bounds)
146    , fUniqueID(next_id())
147    , fCacheID(SK_InvalidUniqueID) {}
148
149SkTextBlob::~SkTextBlob() {
150#if SK_SUPPORT_GPU
151    if (SK_InvalidUniqueID != fCacheID.load()) {
152        GrTextBlobCache::PostPurgeBlobMessage(fUniqueID, fCacheID);
153    }
154#endif
155
156    const auto* run = RunRecord::First(this);
157    do {
158        const auto* nextRun = RunRecord::Next(run);
159        SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
160        run->~RunRecord();
161        run = nextRun;
162    } while (run);
163}
164
165namespace {
166
167union PositioningAndExtended {
168    int32_t intValue;
169    struct {
170        uint8_t  positioning;
171        uint8_t  extended;
172        uint16_t padding;
173    };
174};
175
176static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
177
178} // namespace
179
180enum SkTextBlob::GlyphPositioning : uint8_t {
181    kDefault_Positioning      = 0, // Default glyph advances -- zero scalars per glyph.
182    kHorizontal_Positioning   = 1, // Horizontal positioning -- one scalar per glyph.
183    kFull_Positioning         = 2, // Point positioning -- two scalars per glyph.
184    kRSXform_Positioning      = 3, // RSXform positioning -- four scalars per glyph.
185};
186
187unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
188    const uint8_t gScalarsPerPositioning[] = {
189        0,  // kDefault_Positioning
190        1,  // kHorizontal_Positioning
191        2,  // kFull_Positioning
192        4,  // kRSXform_Positioning
193    };
194    SkASSERT((unsigned)pos <= 3);
195    return gScalarsPerPositioning[pos];
196}
197
198void SkTextBlob::operator delete(void* p) {
199    sk_free(p);
200}
201
202void* SkTextBlob::operator new(size_t) {
203    SK_ABORT("All blobs are created by placement new.");
204}
205
206void* SkTextBlob::operator new(size_t, void* p) {
207    return p;
208}
209
210SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
211    : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
212    SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
213}
214
215void SkTextBlobRunIterator::next() {
216    SkASSERT(!this->done());
217
218    if (!this->done()) {
219        SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
220        fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
221    }
222}
223
224SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const {
225    SkASSERT(!this->done());
226    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
227                  kDefault_Positioning, "");
228    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) ==
229                  kHorizontal_Positioning, "");
230    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) ==
231                  kFull_Positioning, "");
232    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) ==
233                  kRSXform_Positioning, "");
234
235    return SkTo<GlyphPositioning>(fCurrentRun->positioning());
236}
237
238unsigned SkTextBlobRunIterator::scalarsPerGlyph() const {
239    return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning());
240}
241
242bool SkTextBlobRunIterator::isLCD() const {
243    return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
244}
245
246SkTextBlobBuilder::SkTextBlobBuilder()
247    : fStorageSize(0)
248    , fStorageUsed(0)
249    , fRunCount(0)
250    , fDeferredBounds(false)
251    , fLastRun(0) {
252    fBounds.setEmpty();
253}
254
255SkTextBlobBuilder::~SkTextBlobBuilder() {
256    if (nullptr != fStorage.get()) {
257        // We are abandoning runs and must destruct the associated font data.
258        // The easiest way to accomplish that is to use the blob destructor.
259        this->make();
260    }
261}
262
263static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
264    return SkMatrix().setRSXform(xform).mapRect(rect);
265}
266
267SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
268    const SkFont& font = run.font();
269    SkRect bounds;
270
271    if (SkTextBlob::kDefault_Positioning == run.positioning()) {
272        font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
273                         SkTextEncoding::kGlyphID, &bounds);
274        return bounds.makeOffset(run.offset().x(), run.offset().y());
275    }
276
277    SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
278    font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
279
280    if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
281        bounds.setEmpty();
282        const SkRSXform* xform = run.xformBuffer();
283        SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
284        for (unsigned i = 0; i < run.glyphCount(); ++i) {
285            bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
286        }
287    } else {
288        SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
289                 SkTextBlob::kHorizontal_Positioning == run.positioning());
290        // kFull_Positioning       => [ x, y, x, y... ]
291        // kHorizontal_Positioning => [ x, x, x... ]
292        //                            (const y applied by runBounds.offset(run->offset()) later)
293        const SkScalar horizontalConstY = 0;
294        const SkScalar* glyphPosX = run.posBuffer();
295        const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
296                                                        glyphPosX + 1 : &horizontalConstY;
297        const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
298        const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
299                                                    posXInc : 0;
300
301        bounds.setEmpty();
302        for (unsigned i = 0; i < run.glyphCount(); ++i) {
303            bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
304            glyphPosX += posXInc;
305            glyphPosY += posYInc;
306        }
307
308        SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
309    }
310    return bounds.makeOffset(run.offset().x(), run.offset().y());
311}
312
313SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
314    SkASSERT(run.glyphCount() > 0);
315    SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
316             SkTextBlob::kHorizontal_Positioning == run.positioning() ||
317             SkTextBlob::kRSXform_Positioning == run.positioning());
318
319    const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
320    if (fontBounds.isEmpty()) {
321        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
322        // producing useful results in this case.
323        return TightRunBounds(run);
324    }
325
326    // Compute the glyph position bbox.
327    SkRect bounds;
328    switch (run.positioning()) {
329    case SkTextBlob::kHorizontal_Positioning: {
330        const SkScalar* glyphPos = run.posBuffer();
331        SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
332
333        SkScalar minX = *glyphPos;
334        SkScalar maxX = *glyphPos;
335        for (unsigned i = 1; i < run.glyphCount(); ++i) {
336            SkScalar x = glyphPos[i];
337            minX = std::min(x, minX);
338            maxX = std::max(x, maxX);
339        }
340
341        bounds.setLTRB(minX, 0, maxX, 0);
342    } break;
343    case SkTextBlob::kFull_Positioning: {
344        const SkPoint* glyphPosPts = run.pointBuffer();
345        SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
346
347        bounds.setBounds(glyphPosPts, run.glyphCount());
348    } break;
349    case SkTextBlob::kRSXform_Positioning: {
350        const SkRSXform* xform = run.xformBuffer();
351        SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
352        bounds.setEmpty();
353        for (unsigned i = 0; i < run.glyphCount(); ++i) {
354            bounds.join(map_quad_to_rect(xform[i], fontBounds));
355        }
356    } break;
357    default:
358        SK_ABORT("unsupported positioning mode");
359    }
360
361    if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
362        // Expand by typeface glyph bounds.
363        bounds.fLeft   += fontBounds.left();
364        bounds.fTop    += fontBounds.top();
365        bounds.fRight  += fontBounds.right();
366        bounds.fBottom += fontBounds.bottom();
367    }
368
369    // Offset by run position.
370    return bounds.makeOffset(run.offset().x(), run.offset().y());
371}
372
373void SkTextBlobBuilder::updateDeferredBounds() {
374    SkASSERT(!fDeferredBounds || fRunCount > 0);
375
376    if (!fDeferredBounds) {
377        return;
378    }
379
380    SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
381    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
382                                                                          fLastRun);
383
384    // FIXME: we should also use conservative bounds for kDefault_Positioning.
385    SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
386                       TightRunBounds(*run) : ConservativeRunBounds(*run);
387    fBounds.join(runBounds);
388    fDeferredBounds = false;
389}
390
391void SkTextBlobBuilder::reserve(size_t size) {
392    SkSafeMath safe;
393
394    // We don't currently pre-allocate, but maybe someday...
395    if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
396        return;
397    }
398
399    if (0 == fRunCount) {
400        SkASSERT(nullptr == fStorage.get());
401        SkASSERT(0 == fStorageSize);
402        SkASSERT(0 == fStorageUsed);
403
404        // the first allocation also includes blob storage
405        // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
406        fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
407    }
408
409    fStorageSize = safe.add(fStorageUsed, size);
410
411    // FYI: This relies on everything we store being relocatable, particularly SkPaint.
412    //      Also, this is counting on the underlying realloc to throw when passed max().
413    fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
414}
415
416bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
417                                 uint32_t count, SkPoint offset) {
418    if (0 == fLastRun) {
419        SkASSERT(0 == fRunCount);
420        return false;
421    }
422
423    SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
424    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
425                                                                          fLastRun);
426    SkASSERT(run->glyphCount() > 0);
427
428    if (run->textSize() != 0) {
429        return false;
430    }
431
432    if (run->positioning() != positioning
433        || run->font() != font
434        || (run->glyphCount() + count < run->glyphCount())) {
435        return false;
436    }
437
438    // we can merge same-font/same-positioning runs in the following cases:
439    //   * fully positioned run following another fully positioned run
440    //   * horizontally postioned run following another horizontally positioned run with the same
441    //     y-offset
442    if (SkTextBlob::kFull_Positioning != positioning
443        && (SkTextBlob::kHorizontal_Positioning != positioning
444            || run->offset().y() != offset.y())) {
445        return false;
446    }
447
448    SkSafeMath safe;
449    size_t sizeDelta =
450        SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
451        SkTextBlob::RunRecord::StorageSize(run->glyphCount()        , 0, positioning, &safe);
452    if (!safe) {
453        return false;
454    }
455
456    this->reserve(sizeDelta);
457
458    // reserve may have realloced
459    run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
460    uint32_t preMergeCount = run->glyphCount();
461    run->grow(count);
462
463    // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
464    fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
465    fCurrentRunBuffer.pos = run->posBuffer()
466                          + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
467
468    fStorageUsed += sizeDelta;
469
470    SkASSERT(fStorageUsed <= fStorageSize);
471    run->validate(fStorage.get() + fStorageUsed);
472
473    return true;
474}
475
476void SkTextBlobBuilder::allocInternal(const SkFont& font,
477                                      SkTextBlob::GlyphPositioning positioning,
478                                      int count, int textSize, SkPoint offset,
479                                      const SkRect* bounds) {
480    if (count <= 0 || textSize < 0) {
481        fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
482        return;
483    }
484
485    if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
486        this->updateDeferredBounds();
487
488        SkSafeMath safe;
489        size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
490        if (!safe) {
491            fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
492            return;
493        }
494
495        this->reserve(runSize);
496
497        SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
498        SkASSERT(fStorageUsed + runSize <= fStorageSize);
499
500        SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
501            SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
502        fCurrentRunBuffer.glyphs = run->glyphBuffer();
503        fCurrentRunBuffer.pos = run->posBuffer();
504        fCurrentRunBuffer.utf8text = run->textBuffer();
505        fCurrentRunBuffer.clusters = run->clusterBuffer();
506
507        fLastRun = fStorageUsed;
508        fStorageUsed += runSize;
509        fRunCount++;
510
511        SkASSERT(fStorageUsed <= fStorageSize);
512        run->validate(fStorage.get() + fStorageUsed);
513    }
514    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
515    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
516    if (!fDeferredBounds) {
517        if (bounds) {
518            fBounds.join(*bounds);
519        } else {
520            fDeferredBounds = true;
521        }
522    }
523}
524
525// SkFont versions
526
527const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count,
528                                                                SkScalar x, SkScalar y,
529                                                                const SkRect* bounds) {
530    this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
531    return fCurrentRunBuffer;
532}
533
534const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count,
535                                                                    SkScalar y,
536                                                                    const SkRect* bounds) {
537    this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
538    return fCurrentRunBuffer;
539}
540
541const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count,
542                                                                   const SkRect* bounds) {
543    this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
544    return fCurrentRunBuffer;
545}
546
547const SkTextBlobBuilder::RunBuffer&
548SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) {
549    this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
550    return fCurrentRunBuffer;
551}
552
553const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count,
554                                                                    SkScalar x, SkScalar y,
555                                                                    int textByteCount,
556                                                                    const SkRect* bounds) {
557    this->allocInternal(font,
558                        SkTextBlob::kDefault_Positioning,
559                        count,
560                        textByteCount,
561                        SkPoint::Make(x, y),
562                        bounds);
563    return fCurrentRunBuffer;
564}
565
566const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font,
567                                                                        int count,
568                                                                        SkScalar y,
569                                                                        int textByteCount,
570                                                                        const SkRect* bounds) {
571    this->allocInternal(font,
572                        SkTextBlob::kHorizontal_Positioning,
573                        count,
574                        textByteCount,
575                        SkPoint::Make(0, y),
576                        bounds);
577    return fCurrentRunBuffer;
578}
579
580const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font,
581                                                                       int count,
582                                                                       int textByteCount,
583                                                                       const SkRect *bounds) {
584    this->allocInternal(font,
585                        SkTextBlob::kFull_Positioning,
586                        count, textByteCount,
587                        SkPoint::Make(0, 0),
588                        bounds);
589    return fCurrentRunBuffer;
590}
591
592const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextRSXform(const SkFont& font,
593                                                                           int count,
594                                                                           int textByteCount,
595                                                                           const SkRect *bounds) {
596    this->allocInternal(font,
597                        SkTextBlob::kRSXform_Positioning,
598                        count,
599                        textByteCount,
600                        {0, 0},
601                        bounds);
602    return fCurrentRunBuffer;
603}
604
605sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
606    if (!fRunCount) {
607        // We don't instantiate empty blobs.
608        SkASSERT(!fStorage.get());
609        SkASSERT(fStorageUsed == 0);
610        SkASSERT(fStorageSize == 0);
611        SkASSERT(fLastRun == 0);
612        SkASSERT(fBounds.isEmpty());
613        return nullptr;
614    }
615
616    this->updateDeferredBounds();
617
618    // Tag the last run as such.
619    auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
620    lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
621
622    SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
623    SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
624
625    SkDEBUGCODE(
626        SkSafeMath safe;
627        size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
628        for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
629             run = SkTextBlob::RunRecord::Next(run)) {
630            validateSize += SkTextBlob::RunRecord::StorageSize(
631                    run->fCount, run->textSize(), run->positioning(), &safe);
632            run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
633            fRunCount--;
634        }
635        SkASSERT(validateSize == fStorageUsed);
636        SkASSERT(fRunCount == 0);
637        SkASSERT(safe);
638    )
639
640    fStorageUsed = 0;
641    fStorageSize = 0;
642    fRunCount = 0;
643    fLastRun = 0;
644    fBounds.setEmpty();
645
646    return sk_sp<SkTextBlob>(blob);
647}
648
649///////////////////////////////////////////////////////////////////////////////////////////////////
650
651void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) {
652    // seems like we could skip this, and just recompute bounds in unflatten, but
653    // some cc_unittests fail if we remove this...
654    buffer.writeRect(blob.bounds());
655
656    SkTextBlobRunIterator it(&blob);
657    while (!it.done()) {
658        SkASSERT(it.glyphCount() > 0);
659
660        buffer.write32(it.glyphCount());
661        PositioningAndExtended pe;
662        pe.intValue = 0;
663        pe.positioning = it.positioning();
664        SkASSERT((int32_t)it.positioning() == pe.intValue);  // backwards compat.
665
666        uint32_t textSize = it.textSize();
667        pe.extended = textSize > 0;
668        buffer.write32(pe.intValue);
669        if (pe.extended) {
670            buffer.write32(textSize);
671        }
672        buffer.writePoint(it.offset());
673
674        SkFontPriv::Flatten(it.font(), buffer);
675
676        buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
677        buffer.writeByteArray(it.pos(),
678                              it.glyphCount() * sizeof(SkScalar) *
679                              SkTextBlob::ScalarsPerGlyph(
680                                  SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
681        if (pe.extended) {
682            buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
683            buffer.writeByteArray(it.text(), it.textSize());
684        }
685
686        it.next();
687    }
688
689    // Marker for the last run (0 is not a valid glyph count).
690    buffer.write32(0);
691}
692
693sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
694    SkRect bounds;
695    reader.readRect(&bounds);
696
697    SkTextBlobBuilder blobBuilder;
698    SkSafeMath safe;
699    for (;;) {
700        int glyphCount = reader.read32();
701        if (glyphCount == 0) {
702            // End-of-runs marker.
703            break;
704        }
705
706        PositioningAndExtended pe;
707        pe.intValue = reader.read32();
708        const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
709        if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
710            return nullptr;
711        }
712        int textSize = pe.extended ? reader.read32() : 0;
713        if (textSize < 0) {
714            return nullptr;
715        }
716
717        SkPoint offset;
718        reader.readPoint(&offset);
719        SkFont font;
720        SkFontPriv::Unflatten(&font, reader);
721
722        // Compute the expected size of the buffer and ensure we have enough to deserialize
723        // a run before allocating it.
724        const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
725                     posSize =
726                             safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
727                             SkTextBlob::ScalarsPerGlyph(pos))),
728                     clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
729        const size_t totalSize =
730                safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
731
732        if (!reader.isValid() || !safe || totalSize > reader.available()) {
733            return nullptr;
734        }
735
736        const SkTextBlobBuilder::RunBuffer* buf = nullptr;
737        switch (pos) {
738            case SkTextBlob::kDefault_Positioning:
739                buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
740                                                textSize, &bounds);
741                break;
742            case SkTextBlob::kHorizontal_Positioning:
743                buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
744                                                    textSize, &bounds);
745                break;
746            case SkTextBlob::kFull_Positioning:
747                buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds);
748                break;
749            case SkTextBlob::kRSXform_Positioning:
750                buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds);
751                break;
752        }
753
754        if (!buf->glyphs ||
755            !buf->pos ||
756            (pe.extended && (!buf->clusters || !buf->utf8text))) {
757            return nullptr;
758        }
759
760        if (!reader.readByteArray(buf->glyphs, glyphSize) ||
761            !reader.readByteArray(buf->pos, posSize)) {
762            return nullptr;
763            }
764
765        if (pe.extended) {
766            if (!reader.readByteArray(buf->clusters, clusterSize) ||
767                !reader.readByteArray(buf->utf8text, textSize)) {
768                return nullptr;
769            }
770        }
771    }
772
773    return blobBuilder.make();
774}
775
776sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
777                                           SkTextEncoding encoding) {
778    // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the
779    // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now.
780    const int count = font.countText(text, byteLength, encoding);
781    if (count < 1) {
782        return nullptr;
783    }
784    SkTextBlobBuilder builder;
785    auto buffer = builder.allocRunPos(font, count);
786    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
787    font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
788    return builder.make();
789}
790
791sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength,
792                                              const SkPoint pos[], const SkFont& font,
793                                              SkTextEncoding encoding) {
794    const int count = font.countText(text, byteLength, encoding);
795    if (count < 1) {
796        return nullptr;
797    }
798    SkTextBlobBuilder builder;
799    auto buffer = builder.allocRunPos(font, count);
800    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
801    memcpy(buffer.points(), pos, count * sizeof(SkPoint));
802    return builder.make();
803}
804
805sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength,
806                                               const SkScalar xpos[], SkScalar constY,
807                                               const SkFont& font, SkTextEncoding encoding) {
808    const int count = font.countText(text, byteLength, encoding);
809    if (count < 1) {
810        return nullptr;
811    }
812    SkTextBlobBuilder builder;
813    auto buffer = builder.allocRunPosH(font, count, constY);
814    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
815    memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
816    return builder.make();
817}
818
819sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength,
820                                              const SkRSXform xform[], const SkFont& font,
821                                              SkTextEncoding encoding) {
822    const int count = font.countText(text, byteLength, encoding);
823    if (count < 1) {
824        return nullptr;
825    }
826    SkTextBlobBuilder builder;
827    auto buffer = builder.allocRunRSXform(font, count);
828    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
829    memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
830    return builder.make();
831}
832
833sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
834    SkBinaryWriteBuffer buffer;
835    buffer.setSerialProcs(procs);
836    SkTextBlobPriv::Flatten(*this, buffer);
837
838    size_t total = buffer.bytesWritten();
839    sk_sp<SkData> data = SkData::MakeUninitialized(total);
840    buffer.writeToMemory(data->writable_data());
841    return data;
842}
843
844sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
845                                          const SkDeserialProcs& procs) {
846    SkReadBuffer buffer(data, length);
847    buffer.setDeserialProcs(procs);
848    return SkTextBlobPriv::MakeFromBuffer(buffer);
849}
850
851void SkTextBlob::dump(std::string& desc, int depth) const {
852    std::string split(depth, '\t');
853    desc += split + "\n SkTextBlob:{ \n";
854    fBounds.dump(desc, depth + 1);
855    desc += split + "\t fUniqueID:" + std::to_string(fUniqueID) + "\n";
856    desc += split + "}\n";
857}
858
859///////////////////////////////////////////////////////////////////////////////////////////////////
860
861size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
862    SkBinaryWriteBuffer buffer(memory, memory_size);
863    buffer.setSerialProcs(procs);
864    SkTextBlobPriv::Flatten(*this, buffer);
865    return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
866}
867
868///////////////////////////////////////////////////////////////////////////////////////////////////
869
870namespace {
871int get_glyph_run_intercepts(const SkGlyphRun& glyphRun,
872                             const SkPaint& paint,
873                             const SkScalar bounds[2],
874                             SkScalar intervals[],
875                             int* intervalCount) {
876    SkScalar scale = SK_Scalar1;
877    SkPaint interceptPaint{paint};
878    SkFont interceptFont{glyphRun.font()};
879
880    interceptPaint.setMaskFilter(nullptr);   // don't want this affecting our path-cache lookup
881
882    // can't use our canonical size if we need to apply path effects
883    if (interceptPaint.getPathEffect() == nullptr) {
884        // If the wrong size is going to be used, don't hint anything.
885        interceptFont.setHinting(SkFontHinting::kNone);
886        interceptFont.setSubpixel(true);
887        scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
888        interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
889        // Note: fScale can be zero here (even if it wasn't before the divide). It can also
890        // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
891        // since downstream we will check for the resulting coordinates being non-finite anyway.
892        // Thus we don't need to check for zero here.
893        if (interceptPaint.getStrokeWidth() > 0
894            && interceptPaint.getStyle() != SkPaint::kFill_Style) {
895            interceptPaint.setStrokeWidth(
896                    sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
897        }
898    }
899
900    interceptPaint.setStyle(SkPaint::kFill_Style);
901    interceptPaint.setPathEffect(nullptr);
902
903    SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
904    SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
905
906    const SkPoint* posCursor = glyphRun.positions().begin();
907    for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
908        SkPoint pos = *posCursor++;
909
910        if (glyph->path() != nullptr) {
911            // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
912            // Also ensure the bounds are properly offset by the vertical positioning of the glyph.
913            SkScalar scaledBounds[2] = {
914                (bounds[0] - pos.y()) / scale,
915                (bounds[1] - pos.y()) / scale
916            };
917            metricsAndPaths.findIntercepts(
918                    scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
919        }
920    }
921    return *intervalCount;
922}
923}  // namespace
924
925int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
926                              const SkPaint* paint) const {
927    SkTLazy<SkPaint> defaultPaint;
928    if (paint == nullptr) {
929        defaultPaint.init();
930        paint = defaultPaint.get();
931    }
932
933    SkGlyphRunBuilder builder;
934    auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0});
935
936    int intervalCount = 0;
937    for (const SkGlyphRun& glyphRun : glyphRunList) {
938        // Ignore RSXForm runs.
939        if (glyphRun.scaledRotations().empty()) {
940            intervalCount = get_glyph_run_intercepts(
941                glyphRun, *paint, bounds, intervals, &intervalCount);
942        }
943    }
944
945    return intervalCount;
946}
947
948std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count,
949                                            const SkPoint positions[],
950                                            SkScalar top, SkScalar bottom,
951                                            const SkPaint* paintPtr) const {
952    if (count <= 0) {
953        return std::vector<SkScalar>();
954    }
955
956    const SkPaint paint(paintPtr ? *paintPtr : SkPaint());
957    const SkScalar bounds[] = {top, bottom};
958    const SkGlyphRun run(*this,
959                         {positions, size_t(count)}, {glyphs, size_t(count)},
960                         {nullptr, 0}, {nullptr, 0}, {nullptr, 0});
961
962    std::vector<SkScalar> result;
963    result.resize(count * 2);   // worst case allocation
964    int intervalCount = 0;
965    intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount);
966    result.resize(intervalCount);
967    return result;
968}
969
970////////
971
972SkTextBlob::Iter::Iter(const SkTextBlob& blob) {
973    fRunRecord = RunRecord::First(&blob);
974}
975
976bool SkTextBlob::Iter::next(Run* rec) {
977    if (fRunRecord) {
978        if (rec) {
979            rec->fTypeface = fRunRecord->font().getTypeface();
980            rec->fGlyphCount = fRunRecord->glyphCount();
981            rec->fGlyphIndices = fRunRecord->glyphBuffer();
982#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED
983            rec->fClusterIndex_forTest = fRunRecord->clusterBuffer();
984            rec->fUtf8Size_forTest = fRunRecord->textSize();
985            rec->fUtf8_forTest = fRunRecord->textBuffer();
986#endif
987        }
988        if (fRunRecord->isLastRun()) {
989            fRunRecord = nullptr;
990        } else {
991            fRunRecord = RunRecord::Next(fRunRecord);
992        }
993        return true;
994    }
995    return false;
996}
997
998bool SkTextBlob::Iter::experimentalNext(ExperimentalRun* rec) {
999    if (fRunRecord) {
1000        if (rec) {
1001            rec->font = fRunRecord->font();
1002            rec->count = fRunRecord->glyphCount();
1003            rec->glyphs = fRunRecord->glyphBuffer();
1004            rec->positions = fRunRecord->pointBuffer();
1005        }
1006        if (fRunRecord->isLastRun()) {
1007            fRunRecord = nullptr;
1008        } else {
1009            fRunRecord = RunRecord::Next(fRunRecord);
1010        }
1011        return true;
1012    }
1013    return false;
1014}
1015
1016void GetGlyphIDforTextBlob(const SkTextBlob* blob, std::vector<SkGlyphID>& glyphIds)
1017{
1018    if (blob == nullptr) {
1019        return;
1020    }
1021    SkTextBlobRunIterator it(blob);
1022    if (!it.done()) {
1023        size_t runSize = it.glyphCount();
1024        auto glyphIDs = it.glyphs();
1025        for (size_t i = 0; i < runSize; ++i) {
1026            glyphIds.push_back(glyphIDs[i]);
1027        }
1028    }
1029}
1030
1031SkPath GetPathforTextBlob(const SkGlyphID& glyphId, const SkTextBlob* blob)
1032{
1033    SkPath path;
1034    if (blob == nullptr) {
1035        return path;
1036    }
1037    SkTextBlobRunIterator it(blob);
1038    if (!it.done()) {
1039        SkFont font = it.font();
1040        font.getPath(glyphId, &path);
1041    }
1042    return path;
1043}
1044
1045void GetPointsForTextBlob(const SkTextBlob* blob, std::vector<SkPoint>& points)
1046{
1047    if (blob == nullptr) {
1048        return;
1049    }
1050    SkTextBlobRunIterator run(blob);
1051    if (!run.done()) {
1052        const auto glyphCount = run.glyphCount();
1053        switch (run.positioning()) {
1054            case SkTextBlobRunIterator::kFull_Positioning: {
1055                for (auto i = 0; i < glyphCount; i++) {
1056                    const SkPoint* glyphPoints = run.points();
1057                    const auto* point = glyphPoints + i;
1058                    points.push_back(*point);
1059                }
1060                break;
1061            }
1062            default:
1063                break;
1064        }
1065        run.next();
1066    }
1067}
1068