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