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