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