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