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