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