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