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);
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);
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);
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);
171 SkDEBUGCODE(
run->validate((uint8_t*)
this + fStorageSize);)
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);
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) {
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);
295 SkASSERT((
void*)(xform +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
296 for (
unsigned i = 0;
i <
run.glyphCount(); ++
i) {
305 const SkScalar horizontalConstY = 0;
308 glyphPosX + 1 : &horizontalConstY;
309 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(
run.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) {
335 return TightRunBounds(
run);
340 switch (
run.positioning()) {
343 SkASSERT((
void*)(glyphPos +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
347 for (
unsigned i = 1;
i <
run.glyphCount(); ++
i) {
353 bounds.setLTRB(minX, 0, maxX, 0);
356 const SkPoint* glyphPosPts =
run.pointBuffer();
357 SkASSERT((
void*)(glyphPosPts +
run.glyphCount()) <= SkTextBlob::RunRecord::Next(&
run));
359 bounds.setBounds(glyphPosPts,
run.glyphCount());
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");
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() +
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);
436 SkTextBlob::RunRecord*
run =
reinterpret_cast<SkTextBlob::RunRecord*
>(fStorage.
get() +
440 if (
run->textSize() != 0) {
444 if (
run->positioning() != positioning
446 || (
run->glyphCount() + count < run->glyphCount())) {
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)
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) {
532 fDeferredBounds =
true;
543 return fCurrentRunBuffer;
550 return fCurrentRunBuffer;
556 return fCurrentRunBuffer;
562 return fCurrentRunBuffer;
569 this->allocInternal(
font,
575 return fCurrentRunBuffer;
583 this->allocInternal(
font,
589 return fCurrentRunBuffer;
596 this->allocInternal(
font,
598 count, textByteCount,
601 return fCurrentRunBuffer;
608 this->allocInternal(
font,
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);
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));
785 return blobBuilder.
make();
792 const int count =
font.countText(
text, byteLength, encoding);
806 const int count =
font.countText(
text, byteLength, encoding);
820 const int count =
font.countText(
text, byteLength, encoding);
834 const int count =
font.countText(
text, byteLength, encoding);
849 size_t total =
buffer.bytesWritten();
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);
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>();
962 {
nullptr, 0}, {
nullptr, 0}, {
nullptr, 0});
964 std::vector<SkScalar>
result;
966 int intervalCount = 0;
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
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
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 SkGlyphID > glyphsIDs() const
SkSpan< const SkPoint > positions() const
static float max(float r, float g, float b)
static float min(float r, float g, float b)
Optional< SkRect > bounds
sk_sp< SkBlender > blender SkRect rect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
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
std::shared_ptr< const fml::Mapping > data