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
29namespace {
30struct RunFontStorageEquivalent {
31 SkScalar fSize, fScaleX;
32 void* fTypeface;
33 SkScalar fSkewX;
34 uint32_t fFlags;
35};
36static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
37} // namespace
38
39size_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
61const 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
67const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
68 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
69}
70
71namespace {
72struct RunRecordStorageEquivalent {
73 SkFont fFont;
74 SkPoint fOffset;
75 uint32_t fCount;
76 uint32_t fFlags;
77 SkDEBUGCODE(unsigned fMagic;)
78};
79} // namespace
80
81void 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
98const 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
107size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
108 SkTextBlob::GlyphPositioning positioning,
109 SkSafeMath* safe) {
110 return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
111}
112
113uint32_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
122void 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
135static int32_t next_id() {
136 static std::atomic<int32_t> nextID{1};
137 int32_t id;
138 do {
139 id = nextID++;
140 } while (id == SK_InvalidGenID);
141 return id;
142}
143
144SkTextBlob::SkTextBlob(const SkRect& bounds)
145 : fBounds(bounds)
146 , fUniqueID(next_id())
147 , fCacheID(SK_InvalidUniqueID) {}
148
149SkTextBlob::~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
165namespace {
166
167union PositioningAndExtended {
168 int32_t intValue;
169 struct {
170 uint8_t positioning;
171 uint8_t extended;
172 uint16_t padding;
173 };
174};
175
176static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
177
178} // namespace
179
180enum 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
187unsigned 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
198void SkTextBlob::operator delete(void* p) {
199 sk_free(p);
200}
201
202void* SkTextBlob::operator new(size_t) {
203 SK_ABORT("All blobs are created by placement new.");
204}
205
206void* SkTextBlob::operator new(size_t, void* p) {
207 return p;
208}
209
210SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
211 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
212 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
213}
214
215void 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
224SkTextBlobRunIterator::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
238bool SkTextBlobRunIterator::isLCD() const {
239 return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
240}
241
242SkTextBlobBuilder::SkTextBlobBuilder()
243 : fStorageSize(0)
244 , fStorageUsed(0)
245 , fRunCount(0)
246 , fDeferredBounds(false)
247 , fLastRun(0) {
248 fBounds.setEmpty();
249}
250
251SkTextBlobBuilder::~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
259static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
260 return SkMatrix().setRSXform(xform).mapRect(rect);
261}
262
263SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
264 const SkFont& font = run.font();
265 SkRect bounds;
266
267 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
268 font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
269 SkTextEncoding::kGlyphID, &bounds);
270 return bounds.makeOffset(run.offset().x(), run.offset().y());
271 }
272
273 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
274 font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
275
276 if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
277 bounds.setEmpty();
278 const SkRSXform* xform = run.xformBuffer();
279 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
280 for (unsigned i = 0; i < run.glyphCount(); ++i) {
281 bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
282 }
283 } else {
284 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
285 SkTextBlob::kHorizontal_Positioning == run.positioning());
286 // kFull_Positioning => [ x, y, x, y... ]
287 // kHorizontal_Positioning => [ x, x, x... ]
288 // (const y applied by runBounds.offset(run->offset()) later)
289 const SkScalar horizontalConstY = 0;
290 const SkScalar* glyphPosX = run.posBuffer();
291 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
292 glyphPosX + 1 : &horizontalConstY;
293 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
294 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
295 posXInc : 0;
296
297 bounds.setEmpty();
298 for (unsigned i = 0; i < run.glyphCount(); ++i) {
299 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
300 glyphPosX += posXInc;
301 glyphPosY += posYInc;
302 }
303
304 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
305 }
306 return bounds.makeOffset(run.offset().x(), run.offset().y());
307}
308
309SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
310 SkASSERT(run.glyphCount() > 0);
311 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
312 SkTextBlob::kHorizontal_Positioning == run.positioning() ||
313 SkTextBlob::kRSXform_Positioning == run.positioning());
314
315 const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
316 if (fontBounds.isEmpty()) {
317 // Empty font bounds are likely a font bug. TightBounds has a better chance of
318 // producing useful results in this case.
319 return TightRunBounds(run);
320 }
321
322 // Compute the glyph position bbox.
323 SkRect bounds;
324 switch (run.positioning()) {
325 case SkTextBlob::kHorizontal_Positioning: {
326 const SkScalar* glyphPos = run.posBuffer();
327 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
328
329 SkScalar minX = *glyphPos;
330 SkScalar maxX = *glyphPos;
331 for (unsigned i = 1; i < run.glyphCount(); ++i) {
332 SkScalar x = glyphPos[i];
333 minX = std::min(x, minX);
334 maxX = std::max(x, maxX);
335 }
336
337 bounds.setLTRB(minX, 0, maxX, 0);
338 } break;
339 case SkTextBlob::kFull_Positioning: {
340 const SkPoint* glyphPosPts = run.pointBuffer();
341 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
342
343 bounds.setBounds(glyphPosPts, run.glyphCount());
344 } break;
345 case SkTextBlob::kRSXform_Positioning: {
346 const SkRSXform* xform = run.xformBuffer();
347 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
348 bounds.setEmpty();
349 for (unsigned i = 0; i < run.glyphCount(); ++i) {
350 bounds.join(map_quad_to_rect(xform[i], fontBounds));
351 }
352 } break;
353 default:
354 SK_ABORT("unsupported positioning mode");
355 }
356
357 if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
358 // Expand by typeface glyph bounds.
359 bounds.fLeft += fontBounds.left();
360 bounds.fTop += fontBounds.top();
361 bounds.fRight += fontBounds.right();
362 bounds.fBottom += fontBounds.bottom();
363 }
364
365 // Offset by run position.
366 return bounds.makeOffset(run.offset().x(), run.offset().y());
367}
368
369void SkTextBlobBuilder::updateDeferredBounds() {
370 SkASSERT(!fDeferredBounds || fRunCount > 0);
371
372 if (!fDeferredBounds) {
373 return;
374 }
375
376 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
377 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
378 fLastRun);
379
380 // FIXME: we should also use conservative bounds for kDefault_Positioning.
381 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
382 TightRunBounds(*run) : ConservativeRunBounds(*run);
383 fBounds.join(runBounds);
384 fDeferredBounds = false;
385}
386
387void SkTextBlobBuilder::reserve(size_t size) {
388 SkSafeMath safe;
389
390 // We don't currently pre-allocate, but maybe someday...
391 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
392 return;
393 }
394
395 if (0 == fRunCount) {
396 SkASSERT(nullptr == fStorage.get());
397 SkASSERT(0 == fStorageSize);
398 SkASSERT(0 == fStorageUsed);
399
400 // the first allocation also includes blob storage
401 // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
402 fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
403 }
404
405 fStorageSize = safe.add(fStorageUsed, size);
406
407 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
408 // Also, this is counting on the underlying realloc to throw when passed max().
409 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
410}
411
412bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
413 uint32_t count, SkPoint offset) {
414 if (0 == fLastRun) {
415 SkASSERT(0 == fRunCount);
416 return false;
417 }
418
419 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
420 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
421 fLastRun);
422 SkASSERT(run->glyphCount() > 0);
423
424 if (run->textSize() != 0) {
425 return false;
426 }
427
428 if (run->positioning() != positioning
429 || run->font() != font
430 || (run->glyphCount() + count < run->glyphCount())) {
431 return false;
432 }
433
434 // we can merge same-font/same-positioning runs in the following cases:
435 // * fully positioned run following another fully positioned run
436 // * horizontally postioned run following another horizontally positioned run with the same
437 // y-offset
438 if (SkTextBlob::kFull_Positioning != positioning
439 && (SkTextBlob::kHorizontal_Positioning != positioning
440 || run->offset().y() != offset.y())) {
441 return false;
442 }
443
444 SkSafeMath safe;
445 size_t sizeDelta =
446 SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
447 SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe);
448 if (!safe) {
449 return false;
450 }
451
452 this->reserve(sizeDelta);
453
454 // reserve may have realloced
455 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
456 uint32_t preMergeCount = run->glyphCount();
457 run->grow(count);
458
459 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
460 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
461 fCurrentRunBuffer.pos = run->posBuffer()
462 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
463
464 fStorageUsed += sizeDelta;
465
466 SkASSERT(fStorageUsed <= fStorageSize);
467 run->validate(fStorage.get() + fStorageUsed);
468
469 return true;
470}
471
472void SkTextBlobBuilder::allocInternal(const SkFont& font,
473 SkTextBlob::GlyphPositioning positioning,
474 int count, int textSize, SkPoint offset,
475 const SkRect* bounds) {
476 if (count <= 0 || textSize < 0) {
477 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
478 return;
479 }
480
481 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
482 this->updateDeferredBounds();
483
484 SkSafeMath safe;
485 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
486 if (!safe) {
487 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
488 return;
489 }
490
491 this->reserve(runSize);
492
493 SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
494 SkASSERT(fStorageUsed + runSize <= fStorageSize);
495
496 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
497 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
498 fCurrentRunBuffer.glyphs = run->glyphBuffer();
499 fCurrentRunBuffer.pos = run->posBuffer();
500 fCurrentRunBuffer.utf8text = run->textBuffer();
501 fCurrentRunBuffer.clusters = run->clusterBuffer();
502
503 fLastRun = fStorageUsed;
504 fStorageUsed += runSize;
505 fRunCount++;
506
507 SkASSERT(fStorageUsed <= fStorageSize);
508 run->validate(fStorage.get() + fStorageUsed);
509 }
510 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
511 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
512 if (!fDeferredBounds) {
513 if (bounds) {
514 fBounds.join(*bounds);
515 } else {
516 fDeferredBounds = true;
517 }
518 }
519}
520
521// SkFont versions
522
523const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count,
524 SkScalar x, SkScalar y,
525 const SkRect* bounds) {
526 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
527 return fCurrentRunBuffer;
528}
529
530const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count,
531 SkScalar y,
532 const SkRect* bounds) {
533 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
534 return fCurrentRunBuffer;
535}
536
537const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count,
538 const SkRect* bounds) {
539 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
540 return fCurrentRunBuffer;
541}
542
543const SkTextBlobBuilder::RunBuffer&
544SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) {
545 this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
546 return fCurrentRunBuffer;
547}
548
549const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count,
550 SkScalar x, SkScalar y,
551 int textByteCount,
552 SkString lang,
553 const SkRect* bounds) {
554 this->allocInternal(font,
555 SkTextBlob::kDefault_Positioning,
556 count,
557 textByteCount,
558 SkPoint::Make(x, y),
559 bounds);
560 return fCurrentRunBuffer;
561}
562
563const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font, int count,
564 SkScalar y,
565 int textByteCount,
566 SkString lang,
567 const SkRect* bounds) {
568 this->allocInternal(font,
569 SkTextBlob::kHorizontal_Positioning,
570 count,
571 textByteCount,
572 SkPoint::Make(0, y),
573 bounds);
574 return fCurrentRunBuffer;
575}
576
577const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font, int count,
578 int textByteCount,
579 SkString lang,
580 const SkRect *bounds) {
581 this->allocInternal(font,
582 SkTextBlob::kFull_Positioning,
583 count, textByteCount,
584 SkPoint::Make(0, 0),
585 bounds);
586 return fCurrentRunBuffer;
587}
588
589const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count,
590 int textByteCount,
591 SkString lang,
592 const SkRect* bounds) {
593 this->allocInternal(font,
594 SkTextBlob::kRSXform_Positioning,
595 count,
596 textByteCount,
597 {0, 0},
598 bounds);
599 return fCurrentRunBuffer;
600}
601
602sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
603 if (!fRunCount) {
604 // We don't instantiate empty blobs.
605 SkASSERT(!fStorage.get());
606 SkASSERT(fStorageUsed == 0);
607 SkASSERT(fStorageSize == 0);
608 SkASSERT(fLastRun == 0);
609 SkASSERT(fBounds.isEmpty());
610 return nullptr;
611 }
612
613 this->updateDeferredBounds();
614
615 // Tag the last run as such.
616 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
617 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
618
619 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
620 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
621
622 SkDEBUGCODE(
623 SkSafeMath safe;
624 size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
625 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
626 run = SkTextBlob::RunRecord::Next(run)) {
627 validateSize += SkTextBlob::RunRecord::StorageSize(
628 run->fCount, run->textSize(), run->positioning(), &safe);
629 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
630 fRunCount--;
631 }
632 SkASSERT(validateSize == fStorageUsed);
633 SkASSERT(fRunCount == 0);
634 SkASSERT(safe);
635 )
636
637 fStorageUsed = 0;
638 fStorageSize = 0;
639 fRunCount = 0;
640 fLastRun = 0;
641 fBounds.setEmpty();
642
643 return sk_sp<SkTextBlob>(blob);
644}
645
646///////////////////////////////////////////////////////////////////////////////////////////////////
647
648void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) {
649 // seems like we could skip this, and just recompute bounds in unflatten, but
650 // some cc_unittests fail if we remove this...
651 buffer.writeRect(blob.bounds());
652
653 SkTextBlobRunIterator it(&blob);
654 while (!it.done()) {
655 SkASSERT(it.glyphCount() > 0);
656
657 buffer.write32(it.glyphCount());
658 PositioningAndExtended pe;
659 pe.intValue = 0;
660 pe.positioning = it.positioning();
661 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
662
663 uint32_t textSize = it.textSize();
664 pe.extended = textSize > 0;
665 buffer.write32(pe.intValue);
666 if (pe.extended) {
667 buffer.write32(textSize);
668 }
669 buffer.writePoint(it.offset());
670
671 SkFontPriv::Flatten(it.font(), buffer);
672
673 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
674 buffer.writeByteArray(it.pos(),
675 it.glyphCount() * sizeof(SkScalar) *
676 SkTextBlob::ScalarsPerGlyph(
677 SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
678 if (pe.extended) {
679 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
680 buffer.writeByteArray(it.text(), it.textSize());
681 }
682
683 it.next();
684 }
685
686 // Marker for the last run (0 is not a valid glyph count).
687 buffer.write32(0);
688}
689
690sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
691 SkRect bounds;
692 reader.readRect(&bounds);
693
694 SkTextBlobBuilder blobBuilder;
695 SkSafeMath safe;
696 for (;;) {
697 int glyphCount = reader.read32();
698 if (glyphCount == 0) {
699 // End-of-runs marker.
700 break;
701 }
702
703 PositioningAndExtended pe;
704 pe.intValue = reader.read32();
705 const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
706 if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
707 return nullptr;
708 }
709 int textSize = pe.extended ? reader.read32() : 0;
710 if (textSize < 0) {
711 return nullptr;
712 }
713
714 SkPoint offset;
715 reader.readPoint(&offset);
716 SkFont font;
717 SkFontPriv::Unflatten(&font, reader);
718
719 // Compute the expected size of the buffer and ensure we have enough to deserialize
720 // a run before allocating it.
721 const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
722 posSize =
723 safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
724 SkTextBlob::ScalarsPerGlyph(pos))),
725 clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
726 const size_t totalSize =
727 safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
728
729 if (!reader.isValid() || !safe || totalSize > reader.available()) {
730 return nullptr;
731 }
732
733 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
734 switch (pos) {
735 case SkTextBlob::kDefault_Positioning:
736 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
737 textSize, SkString(), &bounds);
738 break;
739 case SkTextBlob::kHorizontal_Positioning:
740 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
741 textSize, SkString(), &bounds);
742 break;
743 case SkTextBlob::kFull_Positioning:
744 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
745 break;
746 case SkTextBlob::kRSXform_Positioning:
747 buf = &blobBuilder.allocRunRSXform(font, glyphCount, textSize, SkString(), &bounds);
748 break;
749 }
750
751 if (!buf->glyphs ||
752 !buf->pos ||
753 (pe.extended && (!buf->clusters || !buf->utf8text))) {
754 return nullptr;
755 }
756
757 if (!reader.readByteArray(buf->glyphs, glyphSize) ||
758 !reader.readByteArray(buf->pos, posSize)) {
759 return nullptr;
760 }
761
762 if (pe.extended) {
763 if (!reader.readByteArray(buf->clusters, clusterSize) ||
764 !reader.readByteArray(buf->utf8text, textSize)) {
765 return nullptr;
766 }
767 }
768 }
769
770 return blobBuilder.make();
771}
772
773sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
774 SkTextEncoding encoding) {
775 // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the
776 // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now.
777 const int count = font.countText(text, byteLength, encoding);
778 if (count < 1) {
779 return nullptr;
780 }
781 SkTextBlobBuilder builder;
782 auto buffer = builder.allocRunPos(font, count);
783 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
784 font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
785 return builder.make();
786}
787
788sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength,
789 const SkPoint pos[], const SkFont& font,
790 SkTextEncoding encoding) {
791 const int count = font.countText(text, byteLength, encoding);
792 if (count < 1) {
793 return nullptr;
794 }
795 SkTextBlobBuilder builder;
796 auto buffer = builder.allocRunPos(font, count);
797 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
798 memcpy(buffer.points(), pos, count * sizeof(SkPoint));
799 return builder.make();
800}
801
802sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength,
803 const SkScalar xpos[], SkScalar constY,
804 const SkFont& font, SkTextEncoding encoding) {
805 const int count = font.countText(text, byteLength, encoding);
806 if (count < 1) {
807 return nullptr;
808 }
809 SkTextBlobBuilder builder;
810 auto buffer = builder.allocRunPosH(font, count, constY);
811 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
812 memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
813 return builder.make();
814}
815
816sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength,
817 const SkRSXform xform[], const SkFont& font,
818 SkTextEncoding encoding) {
819 const int count = font.countText(text, byteLength, encoding);
820 if (count < 1) {
821 return nullptr;
822 }
823 SkTextBlobBuilder builder;
824 auto buffer = builder.allocRunRSXform(font, count);
825 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
826 memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
827 return builder.make();
828}
829
830sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
831 SkBinaryWriteBuffer buffer;
832 buffer.setSerialProcs(procs);
833 SkTextBlobPriv::Flatten(*this, buffer);
834
835 size_t total = buffer.bytesWritten();
836 sk_sp<SkData> data = SkData::MakeUninitialized(total);
837 buffer.writeToMemory(data->writable_data());
838 return data;
839}
840
841sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
842 const SkDeserialProcs& procs) {
843 SkReadBuffer buffer(data, length);
844 buffer.setDeserialProcs(procs);
845 return SkTextBlobPriv::MakeFromBuffer(buffer);
846}
847
848///////////////////////////////////////////////////////////////////////////////////////////////////
849
850size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
851 SkBinaryWriteBuffer buffer(memory, memory_size);
852 buffer.setSerialProcs(procs);
853 SkTextBlobPriv::Flatten(*this, buffer);
854 return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
855}
856
857///////////////////////////////////////////////////////////////////////////////////////////////////
858
859namespace {
860int get_glyph_run_intercepts(const SkGlyphRun& glyphRun,
861 const SkPaint& paint,
862 const SkScalar bounds[2],
863 SkScalar intervals[],
864 int* intervalCount) {
865 SkScalar scale = SK_Scalar1;
866 SkPaint interceptPaint{paint};
867 SkFont interceptFont{glyphRun.font()};
868
869 interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
870
871 // can't use our canonical size if we need to apply path effects
872 if (interceptPaint.getPathEffect() == nullptr) {
873 // If the wrong size is going to be used, don't hint anything.
874 interceptFont.setHinting(SkFontHinting::kNone);
875 interceptFont.setSubpixel(true);
876 scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
877 interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
878 // Note: fScale can be zero here (even if it wasn't before the divide). It can also
879 // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
880 // since downstream we will check for the resulting coordinates being non-finite anyway.
881 // Thus we don't need to check for zero here.
882 if (interceptPaint.getStrokeWidth() > 0
883 && interceptPaint.getStyle() != SkPaint::kFill_Style) {
884 interceptPaint.setStrokeWidth(
885 sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
886 }
887 }
888
889 interceptPaint.setStyle(SkPaint::kFill_Style);
890 interceptPaint.setPathEffect(nullptr);
891
892 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
893 SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
894
895 SkScalar xOffset = 0;
896 SkScalar xPos = xOffset;
897 SkScalar prevAdvance = 0;
898
899 const SkPoint* posCursor = glyphRun.positions().begin();
900 for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
901 SkPoint pos = *posCursor++;
902
903 xPos += prevAdvance * scale;
904 prevAdvance = glyph->advanceX();
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
920int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
921 const SkPaint* paint) const {
922
923 SkTLazy<SkPaint> defaultPaint;
924 if (paint == nullptr) {
925 defaultPaint.init();
926 paint = defaultPaint.get();
927 }
928
929 SkGlyphRunBuilder builder;
930 builder.textBlobToGlyphRunListIgnoringRSXForm(*paint, *this, SkPoint{0, 0});
931 auto glyphRunList = builder.useGlyphRunList();
932
933 int intervalCount = 0;
934 for (const SkGlyphRun& glyphRun : glyphRunList) {
935 intervalCount = get_glyph_run_intercepts(glyphRun, *paint, bounds, intervals, &intervalCount);
936 }
937
938 return intervalCount;
939}
940
941////////
942
943SkTextBlob::Iter::Iter(const SkTextBlob& blob) {
944 fRunRecord = RunRecord::First(&blob);
945}
946
947bool SkTextBlob::Iter::next(Run* rec) {
948 if (fRunRecord) {
949 if (rec) {
950 rec->fTypeface = fRunRecord->font().getTypeface();
951 rec->fGlyphCount = fRunRecord->glyphCount();
952 rec->fGlyphIndices = fRunRecord->glyphBuffer();
953 }
954 if (fRunRecord->isLastRun()) {
955 fRunRecord = nullptr;
956 } else {
957 fRunRecord = RunRecord::Next(fRunRecord);
958 }
959 return true;
960 }
961 return false;
962}
963