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