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