41struct RunFontStorageEquivalent {
47static_assert(
sizeof(
SkFont) ==
sizeof(RunFontStorageEquivalent),
"runfont_should_stay_packed");
50size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
55 auto glyphSize = safe->
mul(glyphCount,
sizeof(uint16_t)),
56 posSize = safe->
mul(PosCount(glyphCount, positioning, safe),
sizeof(
SkScalar));
59 auto size =
sizeof(SkTextBlob::RunRecord);
61 size = safe->
add(size, posSize);
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);
69 return safe->
alignUp(size,
sizeof(
void*));
72const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(
const SkTextBlob* blob) {
75 return reinterpret_cast<const RunRecord*
>(
SkAlignPtr((uintptr_t)(blob + 1)));
78const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(
const RunRecord*
run) {
79 return SkToBool(
run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(
run);
83struct RunRecordStorageEquivalent {
92void SkTextBlob::RunRecord::validate(
const uint8_t* storageTop)
const {
94 SkASSERT((
const uint8_t*)NextUnchecked(
this) <= storageTop);
96 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
97 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
98 <= (
const SkScalar*)NextUnchecked(
this));
101 SkASSERT(textSizePtr() < (
const uint32_t*)NextUnchecked(
this));
102 SkASSERT(clusterBuffer() < (
const uint32_t*)NextUnchecked(
this));
103 SkASSERT(textBuffer() + textSize() <= (
const char*)NextUnchecked(
this));
105 static_assert(
sizeof(SkTextBlob::RunRecord) ==
sizeof(RunRecordStorageEquivalent),
106 "runrecord_should_stay_packed");
109const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(
const RunRecord*
run) {
111 auto res =
reinterpret_cast<const RunRecord*
>(
112 reinterpret_cast<const uint8_t*
>(
run)
113 + StorageSize(
run->glyphCount(),
run->textSize(),
run->positioning(), &safe));
118size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
121 return safe->
mul(glyphCount, ScalarsPerGlyph(positioning));
124uint32_t* SkTextBlob::RunRecord::textSizePtr()
const {
128 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
133void SkTextBlob::RunRecord::grow(uint32_t
count) {
134 SkScalar* initialPosBuffer = posBuffer();
135 uint32_t initialCount =
fCount;
139 size_t copySize = initialCount *
sizeof(
SkScalar) * ScalarsPerGlyph(positioning());
140 SkASSERT((uint8_t*)posBuffer() + copySize <= (
const uint8_t*)NextUnchecked(
this));
143 memmove(posBuffer(), initialPosBuffer, copySize);
147 static std::atomic<uint32_t> nextID{1};
150 id = nextID.fetch_add(1, std::memory_order_relaxed);
155SkTextBlob::SkTextBlob(
const SkRect& bounds)
159 , fPurgeDelegate(nullptr) {}
161SkTextBlob::~SkTextBlob() {
163 PurgeDelegate
f = fPurgeDelegate.load();
165 f(fUniqueID, fCacheID);
168 const auto*
run = RunRecord::First(
this);
170 const auto* nextRun = RunRecord::Next(
run);
179union PositioningAndExtended {
188static_assert(
sizeof(PositioningAndExtended) ==
sizeof(int32_t),
"");
199unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning
pos) {
200 const uint8_t gScalarsPerPositioning[] = {
207 return gScalarsPerPositioning[
pos];
210void SkTextBlob::operator
delete(
void*
p) {
214void* SkTextBlob::operator
new(size_t) {
215 SK_ABORT(
"All blobs are created by placement new.");
218void* SkTextBlob::operator
new(size_t,
void*
p) {
223 : fCurrentRun(
SkTextBlob::RunRecord::First(blob)) {
224 SkDEBUGCODE(fStorageTop = (
const uint8_t*)blob + blob->fStorageSize;)
232 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
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) ==
247 return SkTo<GlyphPositioning>(fCurrentRun->positioning());
251 return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning());
262 , fDeferredBounds(false)
268 if (
nullptr != fStorage.
get()) {
279SkRect SkTextBlobBuilder::TightRunBounds(
const SkTextBlob::RunRecord&
run) {
283 if (SkTextBlob::kDefault_Positioning ==
run.positioning()) {
284 font.measureText(
run.glyphBuffer(),
run.glyphCount() *
sizeof(uint16_t),
286 return bounds.makeOffset(
run.offset().x(),
run.offset().y());
290 font.getBounds(
run.glyphBuffer(),
run.glyphCount(), glyphBounds.get(),
nullptr);
292 if (SkTextBlob::kRSXform_Positioning ==
run.positioning()) {
295 SkASSERT((
void*)(xform +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
296 for (
unsigned i = 0; i <
run.glyphCount(); ++i) {
300 SkASSERT(SkTextBlob::kFull_Positioning ==
run.positioning() ||
301 SkTextBlob::kHorizontal_Positioning ==
run.positioning());
305 const SkScalar horizontalConstY = 0;
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) ?
314 for (
unsigned i = 0; i <
run.glyphCount(); ++i) {
315 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
316 glyphPosX += posXInc;
317 glyphPosY += posYInc;
320 SkASSERT((
void*)glyphPosX <= SkTextBlob::RunRecord::Next(&
run));
322 return bounds.makeOffset(
run.offset().x(),
run.offset().y());
325SkRect SkTextBlobBuilder::ConservativeRunBounds(
const SkTextBlob::RunRecord&
run) {
327 SkASSERT(SkTextBlob::kFull_Positioning ==
run.positioning() ||
328 SkTextBlob::kHorizontal_Positioning ==
run.positioning() ||
329 SkTextBlob::kRSXform_Positioning ==
run.positioning());
335 return TightRunBounds(
run);
340 switch (
run.positioning()) {
341 case SkTextBlob::kHorizontal_Positioning: {
343 SkASSERT((
void*)(glyphPos +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
347 for (
unsigned i = 1; i <
run.glyphCount(); ++i) {
349 minX = std::min(
x, minX);
350 maxX = std::max(
x, maxX);
353 bounds.setLTRB(minX, 0, maxX, 0);
355 case SkTextBlob::kFull_Positioning: {
356 const SkPoint* glyphPosPts =
run.pointBuffer();
357 SkASSERT((
void*)(glyphPosPts +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
359 bounds.setBounds(glyphPosPts,
run.glyphCount());
361 case SkTextBlob::kRSXform_Positioning: {
363 SkASSERT((
void*)(xform +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
365 for (
unsigned i = 0; i <
run.glyphCount(); ++i) {
370 SK_ABORT(
"unsupported positioning mode");
373 if (
run.positioning() != SkTextBlob::kRSXform_Positioning) {
382 return bounds.makeOffset(
run.offset().x(),
run.offset().y());
385void SkTextBlobBuilder::updateDeferredBounds() {
386 SkASSERT(!fDeferredBounds || fRunCount > 0);
388 if (!fDeferredBounds) {
393 SkTextBlob::RunRecord*
run =
reinterpret_cast<SkTextBlob::RunRecord*
>(fStorage.
get() +
397 SkRect runBounds = SkTextBlob::kDefault_Positioning ==
run->positioning() ?
398 TightRunBounds(*
run) : ConservativeRunBounds(*
run);
399 fBounds.
join(runBounds);
400 fDeferredBounds =
false;
403void SkTextBlobBuilder::reserve(
size_t size) {
407 if (safe.
add(fStorageUsed, size) <= fStorageSize && safe) {
411 if (0 == fRunCount) {
421 fStorageSize = safe.
add(fStorageUsed, size);
425 fStorage.
realloc(safe ? fStorageSize :
std::numeric_limits<size_t>::
max());
436 SkTextBlob::RunRecord*
run =
reinterpret_cast<SkTextBlob::RunRecord*
>(fStorage.
get() +
440 if (
run->textSize() != 0) {
444 if (
run->positioning() != positioning
445 ||
run->font() != font
446 || (
run->glyphCount() + count < run->glyphCount())) {
454 if (SkTextBlob::kFull_Positioning != positioning
455 && (SkTextBlob::kHorizontal_Positioning != positioning
462 SkTextBlob::RunRecord::StorageSize(
run->glyphCount() +
count, 0, positioning, &safe) -
463 SkTextBlob::RunRecord::StorageSize(
run->glyphCount() , 0, positioning, &safe);
468 this->reserve(sizeDelta);
471 run =
reinterpret_cast<SkTextBlob::RunRecord*
>(fStorage.
get() + fLastRun);
472 uint32_t preMergeCount =
run->glyphCount();
476 fCurrentRunBuffer.
glyphs =
run->glyphBuffer() + preMergeCount;
477 fCurrentRunBuffer.
pos =
run->posBuffer()
478 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
480 fStorageUsed += sizeDelta;
482 SkASSERT(fStorageUsed <= fStorageSize);
483 run->validate(fStorage.
get() + fStorageUsed);
488void SkTextBlobBuilder::allocInternal(
const SkFont& font,
492 if (
count <= 0 || textSize < 0) {
493 fCurrentRunBuffer = {
nullptr,
nullptr,
nullptr,
nullptr };
497 if (textSize != 0 || !this->mergeRun(font, positioning,
count,
offset)) {
498 this->updateDeferredBounds();
501 size_t runSize = SkTextBlob::RunRecord::StorageSize(
count, textSize, positioning, &safe);
503 fCurrentRunBuffer = {
nullptr,
nullptr,
nullptr,
nullptr };
507 this->reserve(runSize);
510 SkASSERT(fStorageUsed + runSize <= fStorageSize);
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();
519 fLastRun = fStorageUsed;
520 fStorageUsed += runSize;
523 SkASSERT(fStorageUsed <= fStorageSize);
524 run->validate(fStorage.
get() + fStorageUsed);
528 if (!fDeferredBounds) {
530 fBounds.
join(*bounds);
532 fDeferredBounds =
true;
542 this->allocInternal(font, SkTextBlob::kDefault_Positioning,
count, 0, {
x,
y}, bounds);
543 return fCurrentRunBuffer;
549 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning,
count, 0, {0,
y}, bounds);
550 return fCurrentRunBuffer;
555 this->allocInternal(font, SkTextBlob::kFull_Positioning,
count, 0, {0, 0}, bounds);
556 return fCurrentRunBuffer;
561 this->allocInternal(font, SkTextBlob::kRSXform_Positioning,
count, 0, {0, 0},
nullptr);
562 return fCurrentRunBuffer;
569 this->allocInternal(font,
570 SkTextBlob::kDefault_Positioning,
575 return fCurrentRunBuffer;
583 this->allocInternal(font,
584 SkTextBlob::kHorizontal_Positioning,
589 return fCurrentRunBuffer;
596 this->allocInternal(font,
597 SkTextBlob::kFull_Positioning,
598 count, textByteCount,
601 return fCurrentRunBuffer;
608 this->allocInternal(font,
609 SkTextBlob::kRSXform_Positioning,
614 return fCurrentRunBuffer;
628 this->updateDeferredBounds();
631 auto* lastRun =
reinterpret_cast<SkTextBlob::RunRecord*
>(fStorage.
get() + fLastRun);
632 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
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);
647 SkASSERT(validateSize == fStorageUsed);
673 PositioningAndExtended pe;
679 pe.extended = textSize > 0;
680 buffer.write32(pe.intValue);
691 SkTextBlob::ScalarsPerGlyph(
692 SkTo<SkTextBlob::GlyphPositioning>(it.
positioning())));
712 int glyphCount = reader.
read32();
713 if (glyphCount == 0) {
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) {
724 int textSize = pe.extended ? reader.
read32() : 0;
736 const size_t glyphSize = safe.
mul(glyphCount,
sizeof(uint16_t)),
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));
750 case SkTextBlob::kDefault_Positioning:
754 case SkTextBlob::kHorizontal_Positioning:
758 case SkTextBlob::kFull_Positioning:
759 buf = &blobBuilder.
allocRunTextPos(font, glyphCount, textSize, &bounds);
761 case SkTextBlob::kRSXform_Positioning:
785 return blobBuilder.
make();
792 const int count = font.countText(
text, byteLength, encoding);
800 return builder.make();
806 const int count = font.countText(
text, byteLength, encoding);
814 return builder.make();
820 const int count = font.countText(
text, byteLength, encoding);
825 auto buffer = builder.allocRunPosH(font,
count, constY);
828 return builder.make();
834 const int count = font.countText(
text, byteLength, encoding);
839 auto buffer = builder.allocRunRSXform(font,
count);
842 return builder.make();
849 size_t total =
buffer.bytesWritten();
851 buffer.writeToMemory(data->writable_data());
858 buffer.setDeserialProcs(procs);
867 return buffer.usingInitialStorage() ?
buffer.bytesWritten() : 0u;
877 int* intervalCount) {
882 interceptPaint.setMaskFilter(
nullptr);
885 if (interceptPaint.getPathEffect() ==
nullptr) {
888 interceptFont.setSubpixel(
true);
895 if (interceptPaint.getStrokeWidth() > 0
897 interceptPaint.setStrokeWidth(
903 interceptPaint.setPathEffect(
nullptr);
909 for (
const SkGlyph* glyph : metricsAndPaths.
glyphs(glyphRun.glyphsIDs())) {
912 if (glyph->path() !=
nullptr) {
919 metricsAndPaths.findIntercepts(
920 scaledBounds,
scale,
pos.
x(), glyph, intervals, intervalCount);
923 return *intervalCount;
930 if (
paint ==
nullptr) {
936 auto glyphRunList = builder.blobToGlyphRunList(*
this, {0, 0});
938 int intervalCount = 0;
942 intervalCount = get_glyph_run_intercepts(
943 glyphRun, *
paint,
bounds, intervals, &intervalCount);
947 return intervalCount;
953 const SkPaint* paintPtr)
const {
955 return std::vector<SkScalar>();
959 const SkScalar bounds[] = {top, bottom};
962 {
nullptr, 0}, {
nullptr, 0}, {
nullptr, 0});
964 std::vector<SkScalar>
result;
966 int intervalCount = 0;
967 intervalCount = get_glyph_run_intercepts(
run,
paint, bounds,
result.data(), &intervalCount);
968 result.resize(intervalCount);
975 fRunRecord = RunRecord::First(&blob);
981 rec->
fTypeface = fRunRecord->font().getTypeface();
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();
990 if (fRunRecord->isLastRun()) {
991 fRunRecord =
nullptr;
993 fRunRecord = RunRecord::Next(fRunRecord);
1003 rec->
font = fRunRecord->font();
1004 rec->
count = fRunRecord->glyphCount();
1005 rec->
glyphs = fRunRecord->glyphBuffer();
1006 rec->
positions = fRunRecord->pointBuffer();
1008 if (fRunRecord->isLastRun()) {
1009 fRunRecord =
nullptr;
1011 fRunRecord = RunRecord::Next(fRunRecord);
static constexpr bool SkIsAlign4(T x)
static constexpr T SkAlignPtr(T x)
#define SK_ABORT(message,...)
static constexpr float sk_ieee_float_divide(float numer, float denom)
@ kNone
glyph outlines unchanged
@ kGlyphID
uses two byte words to represent glyph indices
SK_API void sk_free(void *)
static SkRect map_quad_to_rect(const SkRSXform &xform, const SkRect &rect)
static uint32_t next_id()
@ kHorizontal_Positioning
static constexpr bool SkToBool(const T &x)
static constexpr uint32_t SK_InvalidUniqueID
static constexpr uint32_t SK_InvalidGenID
static sk_sp< SkData > MakeUninitialized(size_t length)
static void Flatten(const SkFont &, SkWriteBuffer &buffer)
static constexpr int kCanonicalTextSizeForPaths
static bool Unflatten(SkFont *, SkReadBuffer &buffer)
static SkRect GetFontBounds(const SkFont &)
std::vector< SkScalar > getIntercepts(const SkGlyphID glyphs[], int count, const SkPoint pos[], SkScalar top, SkScalar bottom, const SkPaint *=nullptr) const
void setHinting(SkFontHinting hintingLevel)
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
SkMatrix & setRSXform(const SkRSXform &rsxForm)
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
@ kFill_Style
set to fill geometry
void readRect(SkRect *rect)
void readPoint(SkPoint *point)
bool readByteArray(void *value, size_t size)
size_t add(size_t x, size_t y)
size_t alignUp(size_t x, size_t alignment)
size_t mul(size_t x, size_t y)
static SkStrikeSpec MakeWithNoDevice(const SkFont &font, const SkPaint *paint=nullptr)
T * init(Args &&... args)
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)
@ kHorizontal_Positioning
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 experimentalNext(ExperimentalRun *)
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
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
const SkFont & font() const
SkSpan< const SkPoint > positions() const
static const uint8_t buffer[]
static float max(float r, float g, float b)
Optional< SkRect > bounds
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
font
Font Metadata and Metrics.
static constexpr SkPoint Make(float x, float y)
constexpr float y() const
constexpr float x() const
constexpr float left() const
constexpr float top() const
constexpr float right() const
void join(const SkRect &r)
constexpr float bottom() const
SkScalar * pos
storage for glyph positions in run
char * utf8text
storage for text UTF-8 code units in run
SkGlyphID * glyphs
storage for glyph indexes in run
uint32_t * clusters
storage for glyph clusters (index of UTF-8 code unit)
const SkPoint * positions
const uint16_t * fGlyphIndices