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