123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 |
- /*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "include/core/SkRSXform.h"
- #include "include/core/SkTextBlob.h"
- #include "include/core/SkTypeface.h"
- #include "src/core/SkFontPriv.h"
- #include "src/core/SkGlyphRun.h"
- #include "src/core/SkPaintPriv.h"
- #include "src/core/SkReadBuffer.h"
- #include "src/core/SkSafeMath.h"
- #include "src/core/SkStrikeCache.h"
- #include "src/core/SkStrikeSpec.h"
- #include "src/core/SkTextBlobPriv.h"
- #include "src/core/SkWriteBuffer.h"
- #include <atomic>
- #include <limits>
- #include <new>
- #if SK_SUPPORT_GPU
- #include "src/gpu/text/GrTextBlobCache.h"
- #endif
- namespace {
- struct RunFontStorageEquivalent {
- SkScalar fSize, fScaleX;
- void* fTypeface;
- SkScalar fSkewX;
- uint32_t fFlags;
- };
- static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
- }
- size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
- SkTextBlob::GlyphPositioning positioning,
- SkSafeMath* safe) {
- static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
- auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
- posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
- // RunRecord object + (aligned) glyph buffer + position buffer
- auto size = sizeof(SkTextBlob::RunRecord);
- size = safe->add(size, safe->alignUp(glyphSize, 4));
- size = safe->add(size, posSize);
- if (textSize) { // Extended run.
- size = safe->add(size, sizeof(uint32_t));
- size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
- size = safe->add(size, textSize);
- }
- return safe->alignUp(size, sizeof(void*));
- }
- const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) {
- // The first record (if present) is stored following the blob object.
- // (aligned up to make the RunRecord aligned too)
- return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
- }
- const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
- return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
- }
- namespace {
- struct RunRecordStorageEquivalent {
- SkFont fFont;
- SkPoint fOffset;
- uint32_t fCount;
- uint32_t fFlags;
- SkDEBUGCODE(unsigned fMagic;)
- };
- }
- void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
- SkASSERT(kRunRecordMagic == fMagic);
- SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
- SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
- SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
- <= (SkScalar*)NextUnchecked(this));
- if (isExtended()) {
- SkASSERT(textSize() > 0);
- SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
- SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
- SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
- }
- static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
- "runrecord_should_stay_packed");
- }
- const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
- SkSafeMath safe;
- auto res = reinterpret_cast<const RunRecord*>(
- reinterpret_cast<const uint8_t*>(run)
- + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
- SkASSERT(safe);
- return res;
- }
- size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
- SkTextBlob::GlyphPositioning positioning,
- SkSafeMath* safe) {
- return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
- }
- uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
- // textSize follows the position buffer.
- SkASSERT(isExtended());
- SkSafeMath safe;
- auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
- SkASSERT(safe);
- return res;
- }
- void SkTextBlob::RunRecord::grow(uint32_t count) {
- SkScalar* initialPosBuffer = posBuffer();
- uint32_t initialCount = fCount;
- fCount += count;
- // Move the initial pos scalars to their new location.
- size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
- SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
- // memmove, as the buffers may overlap
- memmove(posBuffer(), initialPosBuffer, copySize);
- }
- static int32_t next_id() {
- static std::atomic<int32_t> nextID{1};
- int32_t id;
- do {
- id = nextID++;
- } while (id == SK_InvalidGenID);
- return id;
- }
- SkTextBlob::SkTextBlob(const SkRect& bounds)
- : fBounds(bounds)
- , fUniqueID(next_id())
- , fCacheID(SK_InvalidUniqueID) {}
- SkTextBlob::~SkTextBlob() {
- #if SK_SUPPORT_GPU
- if (SK_InvalidUniqueID != fCacheID.load()) {
- GrTextBlobCache::PostPurgeBlobMessage(fUniqueID, fCacheID);
- }
- #endif
- const auto* run = RunRecord::First(this);
- do {
- const auto* nextRun = RunRecord::Next(run);
- SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
- run->~RunRecord();
- run = nextRun;
- } while (run);
- }
- namespace {
- union PositioningAndExtended {
- int32_t intValue;
- struct {
- uint8_t positioning;
- uint8_t extended;
- uint16_t padding;
- };
- };
- static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
- } // namespace
- enum SkTextBlob::GlyphPositioning : uint8_t {
- kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph.
- kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph.
- kFull_Positioning = 2, // Point positioning -- two scalars per glyph.
- kRSXform_Positioning = 3, // RSXform positioning -- four scalars per glyph.
- };
- unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
- const uint8_t gScalarsPerPositioning[] = {
- 0, // kDefault_Positioning
- 1, // kHorizontal_Positioning
- 2, // kFull_Positioning
- 4, // kRSXform_Positioning
- };
- SkASSERT((unsigned)pos <= 3);
- return gScalarsPerPositioning[pos];
- }
- void SkTextBlob::operator delete(void* p) {
- sk_free(p);
- }
- void* SkTextBlob::operator new(size_t) {
- SK_ABORT("All blobs are created by placement new.");
- return sk_malloc_throw(0);
- }
- void* SkTextBlob::operator new(size_t, void* p) {
- return p;
- }
- SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
- : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
- SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
- }
- void SkTextBlobRunIterator::next() {
- SkASSERT(!this->done());
- if (!this->done()) {
- SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
- fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
- }
- }
- SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const {
- SkASSERT(!this->done());
- static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
- kDefault_Positioning, "");
- static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) ==
- kHorizontal_Positioning, "");
- static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) ==
- kFull_Positioning, "");
- static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) ==
- kRSXform_Positioning, "");
- return SkTo<GlyphPositioning>(fCurrentRun->positioning());
- }
- bool SkTextBlobRunIterator::isLCD() const {
- return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
- }
- SkTextBlobBuilder::SkTextBlobBuilder()
- : fStorageSize(0)
- , fStorageUsed(0)
- , fRunCount(0)
- , fDeferredBounds(false)
- , fLastRun(0) {
- fBounds.setEmpty();
- }
- SkTextBlobBuilder::~SkTextBlobBuilder() {
- if (nullptr != fStorage.get()) {
- // We are abandoning runs and must destruct the associated font data.
- // The easiest way to accomplish that is to use the blob destructor.
- this->make();
- }
- }
- SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
- const SkFont& font = run.font();
- SkRect bounds;
- if (SkTextBlob::kDefault_Positioning == run.positioning()) {
- font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
- SkTextEncoding::kGlyphID, &bounds);
- return bounds.makeOffset(run.offset().x(), run.offset().y());
- }
- SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
- font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
- SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
- SkTextBlob::kHorizontal_Positioning == run.positioning());
- // kFull_Positioning => [ x, y, x, y... ]
- // kHorizontal_Positioning => [ x, x, x... ]
- // (const y applied by runBounds.offset(run->offset()) later)
- const SkScalar horizontalConstY = 0;
- const SkScalar* glyphPosX = run.posBuffer();
- const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
- glyphPosX + 1 : &horizontalConstY;
- const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
- const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
- posXInc : 0;
- bounds.setEmpty();
- for (unsigned i = 0; i < run.glyphCount(); ++i) {
- bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
- glyphPosX += posXInc;
- glyphPosY += posYInc;
- }
- SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
- return bounds.makeOffset(run.offset().x(), run.offset().y());
- }
- static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
- return SkMatrix().setRSXform(xform).mapRect(rect);
- }
- SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
- SkASSERT(run.glyphCount() > 0);
- SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
- SkTextBlob::kHorizontal_Positioning == run.positioning() ||
- SkTextBlob::kRSXform_Positioning == run.positioning());
- const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
- if (fontBounds.isEmpty()) {
- // Empty font bounds are likely a font bug. TightBounds has a better chance of
- // producing useful results in this case.
- return TightRunBounds(run);
- }
- // Compute the glyph position bbox.
- SkRect bounds;
- switch (run.positioning()) {
- case SkTextBlob::kHorizontal_Positioning: {
- const SkScalar* glyphPos = run.posBuffer();
- SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
- SkScalar minX = *glyphPos;
- SkScalar maxX = *glyphPos;
- for (unsigned i = 1; i < run.glyphCount(); ++i) {
- SkScalar x = glyphPos[i];
- minX = SkMinScalar(x, minX);
- maxX = SkMaxScalar(x, maxX);
- }
- bounds.setLTRB(minX, 0, maxX, 0);
- } break;
- case SkTextBlob::kFull_Positioning: {
- const SkPoint* glyphPosPts = run.pointBuffer();
- SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
- bounds.setBounds(glyphPosPts, run.glyphCount());
- } break;
- case SkTextBlob::kRSXform_Positioning: {
- const SkRSXform* xform = run.xformBuffer();
- SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
- bounds = map_quad_to_rect(xform[0], fontBounds);
- for (unsigned i = 1; i < run.glyphCount(); ++i) {
- bounds.join(map_quad_to_rect(xform[i], fontBounds));
- }
- } break;
- default:
- SK_ABORT("unsupported positioning mode");
- }
- if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
- // Expand by typeface glyph bounds.
- bounds.fLeft += fontBounds.left();
- bounds.fTop += fontBounds.top();
- bounds.fRight += fontBounds.right();
- bounds.fBottom += fontBounds.bottom();
- }
- // Offset by run position.
- return bounds.makeOffset(run.offset().x(), run.offset().y());
- }
- void SkTextBlobBuilder::updateDeferredBounds() {
- SkASSERT(!fDeferredBounds || fRunCount > 0);
- if (!fDeferredBounds) {
- return;
- }
- SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
- SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
- fLastRun);
- // FIXME: we should also use conservative bounds for kDefault_Positioning.
- SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
- TightRunBounds(*run) : ConservativeRunBounds(*run);
- fBounds.join(runBounds);
- fDeferredBounds = false;
- }
- void SkTextBlobBuilder::reserve(size_t size) {
- SkSafeMath safe;
- // We don't currently pre-allocate, but maybe someday...
- if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
- return;
- }
- if (0 == fRunCount) {
- SkASSERT(nullptr == fStorage.get());
- SkASSERT(0 == fStorageSize);
- SkASSERT(0 == fStorageUsed);
- // the first allocation also includes blob storage
- // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
- fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
- }
- fStorageSize = safe.add(fStorageUsed, size);
- // FYI: This relies on everything we store being relocatable, particularly SkPaint.
- // Also, this is counting on the underlying realloc to throw when passed max().
- fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
- }
- bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
- uint32_t count, SkPoint offset) {
- if (0 == fLastRun) {
- SkASSERT(0 == fRunCount);
- return false;
- }
- SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
- SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
- fLastRun);
- SkASSERT(run->glyphCount() > 0);
- if (run->textSize() != 0) {
- return false;
- }
- if (run->positioning() != positioning
- || run->font() != font
- || (run->glyphCount() + count < run->glyphCount())) {
- return false;
- }
- // we can merge same-font/same-positioning runs in the following cases:
- // * fully positioned run following another fully positioned run
- // * horizontally postioned run following another horizontally positioned run with the same
- // y-offset
- if (SkTextBlob::kFull_Positioning != positioning
- && (SkTextBlob::kHorizontal_Positioning != positioning
- || run->offset().y() != offset.y())) {
- return false;
- }
- SkSafeMath safe;
- size_t sizeDelta =
- SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
- SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe);
- if (!safe) {
- return false;
- }
- this->reserve(sizeDelta);
- // reserve may have realloced
- run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
- uint32_t preMergeCount = run->glyphCount();
- run->grow(count);
- // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
- fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
- fCurrentRunBuffer.pos = run->posBuffer()
- + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
- fStorageUsed += sizeDelta;
- SkASSERT(fStorageUsed <= fStorageSize);
- run->validate(fStorage.get() + fStorageUsed);
- return true;
- }
- void SkTextBlobBuilder::allocInternal(const SkFont& font,
- SkTextBlob::GlyphPositioning positioning,
- int count, int textSize, SkPoint offset,
- const SkRect* bounds) {
- if (count <= 0 || textSize < 0) {
- fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
- return;
- }
- if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
- this->updateDeferredBounds();
- SkSafeMath safe;
- size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
- if (!safe) {
- fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
- return;
- }
- this->reserve(runSize);
- SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
- SkASSERT(fStorageUsed + runSize <= fStorageSize);
- SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
- SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
- fCurrentRunBuffer.glyphs = run->glyphBuffer();
- fCurrentRunBuffer.pos = run->posBuffer();
- fCurrentRunBuffer.utf8text = run->textBuffer();
- fCurrentRunBuffer.clusters = run->clusterBuffer();
- fLastRun = fStorageUsed;
- fStorageUsed += runSize;
- fRunCount++;
- SkASSERT(fStorageUsed <= fStorageSize);
- run->validate(fStorage.get() + fStorageUsed);
- }
- SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
- SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
- if (!fDeferredBounds) {
- if (bounds) {
- fBounds.join(*bounds);
- } else {
- fDeferredBounds = true;
- }
- }
- }
- // SkFont versions
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count,
- SkScalar x, SkScalar y,
- const SkRect* bounds) {
- this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count,
- SkScalar y,
- const SkRect* bounds) {
- this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count,
- const SkRect* bounds) {
- this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer&
- SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) {
- this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count,
- SkScalar x, SkScalar y,
- int textByteCount,
- SkString lang,
- const SkRect* bounds) {
- this->allocInternal(font,
- SkTextBlob::kDefault_Positioning,
- count,
- textByteCount,
- SkPoint::Make(x, y),
- bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font, int count,
- SkScalar y,
- int textByteCount,
- SkString lang,
- const SkRect* bounds) {
- this->allocInternal(font,
- SkTextBlob::kHorizontal_Positioning,
- count,
- textByteCount,
- SkPoint::Make(0, y),
- bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font, int count,
- int textByteCount,
- SkString lang,
- const SkRect *bounds) {
- this->allocInternal(font,
- SkTextBlob::kFull_Positioning,
- count, textByteCount,
- SkPoint::Make(0, 0),
- bounds);
- return fCurrentRunBuffer;
- }
- const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count,
- int textByteCount,
- SkString lang,
- const SkRect* bounds) {
- this->allocInternal(font,
- SkTextBlob::kRSXform_Positioning,
- count,
- textByteCount,
- {0, 0},
- bounds);
- return fCurrentRunBuffer;
- }
- sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
- if (!fRunCount) {
- // We don't instantiate empty blobs.
- SkASSERT(!fStorage.get());
- SkASSERT(fStorageUsed == 0);
- SkASSERT(fStorageSize == 0);
- SkASSERT(fLastRun == 0);
- SkASSERT(fBounds.isEmpty());
- return nullptr;
- }
- this->updateDeferredBounds();
- // Tag the last run as such.
- auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
- lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
- SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
- SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
- SkDEBUGCODE(
- SkSafeMath safe;
- size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
- for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
- run = SkTextBlob::RunRecord::Next(run)) {
- validateSize += SkTextBlob::RunRecord::StorageSize(
- run->fCount, run->textSize(), run->positioning(), &safe);
- run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
- fRunCount--;
- }
- SkASSERT(validateSize == fStorageUsed);
- SkASSERT(fRunCount == 0);
- SkASSERT(safe);
- )
- fStorageUsed = 0;
- fStorageSize = 0;
- fRunCount = 0;
- fLastRun = 0;
- fBounds.setEmpty();
- return sk_sp<SkTextBlob>(blob);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) {
- // seems like we could skip this, and just recompute bounds in unflatten, but
- // some cc_unittests fail if we remove this...
- buffer.writeRect(blob.bounds());
- SkTextBlobRunIterator it(&blob);
- while (!it.done()) {
- SkASSERT(it.glyphCount() > 0);
- buffer.write32(it.glyphCount());
- PositioningAndExtended pe;
- pe.intValue = 0;
- pe.positioning = it.positioning();
- SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
- uint32_t textSize = it.textSize();
- pe.extended = textSize > 0;
- buffer.write32(pe.intValue);
- if (pe.extended) {
- buffer.write32(textSize);
- }
- buffer.writePoint(it.offset());
- SkFontPriv::Flatten(it.font(), buffer);
- buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
- buffer.writeByteArray(it.pos(),
- it.glyphCount() * sizeof(SkScalar) *
- SkTextBlob::ScalarsPerGlyph(
- SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
- if (pe.extended) {
- buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
- buffer.writeByteArray(it.text(), it.textSize());
- }
- it.next();
- }
- // Marker for the last run (0 is not a valid glyph count).
- buffer.write32(0);
- }
- sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
- SkRect bounds;
- reader.readRect(&bounds);
- SkTextBlobBuilder blobBuilder;
- SkSafeMath safe;
- for (;;) {
- int glyphCount = reader.read32();
- if (glyphCount == 0) {
- // End-of-runs marker.
- break;
- }
- PositioningAndExtended pe;
- pe.intValue = reader.read32();
- const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
- if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
- return nullptr;
- }
- int textSize = pe.extended ? reader.read32() : 0;
- if (textSize < 0) {
- return nullptr;
- }
- SkPoint offset;
- reader.readPoint(&offset);
- SkFont font;
- if (reader.isVersionLT(SkReadBuffer::kSerializeFonts_Version)) {
- SkPaint paint;
- reader.readPaint(&paint, &font);
- } else {
- SkFontPriv::Unflatten(&font, reader);
- }
- // Compute the expected size of the buffer and ensure we have enough to deserialize
- // a run before allocating it.
- const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
- posSize =
- safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
- SkTextBlob::ScalarsPerGlyph(pos))),
- clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
- const size_t totalSize =
- safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
- if (!reader.isValid() || !safe || totalSize > reader.available()) {
- return nullptr;
- }
- const SkTextBlobBuilder::RunBuffer* buf = nullptr;
- switch (pos) {
- case SkTextBlob::kDefault_Positioning:
- buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
- textSize, SkString(), &bounds);
- break;
- case SkTextBlob::kHorizontal_Positioning:
- buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
- textSize, SkString(), &bounds);
- break;
- case SkTextBlob::kFull_Positioning:
- buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
- break;
- case SkTextBlob::kRSXform_Positioning:
- buf = &blobBuilder.allocRunRSXform(font, glyphCount, textSize, SkString(), &bounds);
- break;
- }
- if (!buf->glyphs ||
- !buf->pos ||
- (pe.extended && (!buf->clusters || !buf->utf8text))) {
- return nullptr;
- }
- if (!reader.readByteArray(buf->glyphs, glyphSize) ||
- !reader.readByteArray(buf->pos, posSize)) {
- return nullptr;
- }
- if (pe.extended) {
- if (!reader.readByteArray(buf->clusters, clusterSize) ||
- !reader.readByteArray(buf->utf8text, textSize)) {
- return nullptr;
- }
- }
- }
- return blobBuilder.make();
- }
- sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
- SkTextEncoding encoding) {
- // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the
- // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now.
- const int count = font.countText(text, byteLength, encoding);
- SkTextBlobBuilder builder;
- auto buffer = builder.allocRunPos(font, count);
- font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
- font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
- return builder.make();
- }
- sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength,
- const SkPoint pos[], const SkFont& font,
- SkTextEncoding encoding) {
- const int count = font.countText(text, byteLength, encoding);
- SkTextBlobBuilder builder;
- auto buffer = builder.allocRunPos(font, count);
- font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
- memcpy(buffer.points(), pos, count * sizeof(SkPoint));
- return builder.make();
- }
- sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength,
- const SkScalar xpos[], SkScalar constY,
- const SkFont& font, SkTextEncoding encoding) {
- const int count = font.countText(text, byteLength, encoding);
- SkTextBlobBuilder builder;
- auto buffer = builder.allocRunPosH(font, count, constY);
- font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
- memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
- return builder.make();
- }
- sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength,
- const SkRSXform xform[], const SkFont& font,
- SkTextEncoding encoding) {
- const int count = font.countText(text, byteLength, encoding);
- SkTextBlobBuilder builder;
- auto buffer = builder.allocRunRSXform(font, count);
- font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
- memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
- return builder.make();
- }
- sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
- SkBinaryWriteBuffer buffer;
- buffer.setSerialProcs(procs);
- SkTextBlobPriv::Flatten(*this, buffer);
- size_t total = buffer.bytesWritten();
- sk_sp<SkData> data = SkData::MakeUninitialized(total);
- buffer.writeToMemory(data->writable_data());
- return data;
- }
- sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
- const SkDeserialProcs& procs) {
- SkReadBuffer buffer(data, length);
- buffer.setDeserialProcs(procs);
- return SkTextBlobPriv::MakeFromBuffer(buffer);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
- SkBinaryWriteBuffer buffer(memory, memory_size);
- buffer.setSerialProcs(procs);
- SkTextBlobPriv::Flatten(*this, buffer);
- return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace {
- int get_glyph_run_intercepts(const SkGlyphRun& glyphRun,
- const SkPaint& paint,
- const SkScalar bounds[2],
- SkScalar intervals[],
- int* intervalCount) {
- SkScalar scale = SK_Scalar1;
- SkPaint interceptPaint{paint};
- SkFont interceptFont{glyphRun.font()};
- interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
- // can't use our canonical size if we need to apply path effects
- if (interceptPaint.getPathEffect() == nullptr) {
- // If the wrong size is going to be used, don't hint anything.
- interceptFont.setHinting(SkFontHinting::kNone);
- interceptFont.setSubpixel(true);
- scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
- interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
- // Note: fScale can be zero here (even if it wasn't before the divide). It can also
- // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
- // since downstream we will check for the resulting coordinates being non-finite anyway.
- // Thus we don't need to check for zero here.
- if (interceptPaint.getStrokeWidth() > 0
- && interceptPaint.getStyle() != SkPaint::kFill_Style) {
- interceptPaint.setStrokeWidth(
- sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
- }
- }
- interceptPaint.setStyle(SkPaint::kFill_Style);
- interceptPaint.setPathEffect(nullptr);
- SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
- auto cache = strikeSpec.findOrCreateExclusiveStrike();
- SkScalar xOffset = 0;
- SkScalar xPos = xOffset;
- SkScalar prevAdvance = 0;
- // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
- SkScalar scaledBounds[2] = {bounds[0] / scale, bounds[1] / scale};
- const SkPoint* posCursor = glyphRun.positions().begin();
- for (auto glyphID : glyphRun.glyphsIDs()) {
- SkPoint pos = *posCursor++;
- SkGlyph* glyph = cache->glyph(glyphID);
- xPos += prevAdvance * scale;
- prevAdvance = glyph->advanceX();
- if (cache->preparePath(glyph) != nullptr) {
- cache->findIntercepts(scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
- }
- }
- return *intervalCount;
- }
- } // namespace
- int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
- const SkPaint* paint) const {
- SkTLazy<SkPaint> defaultPaint;
- if (paint == nullptr) {
- defaultPaint.init();
- paint = defaultPaint.get();
- }
- SkGlyphRunBuilder builder;
- builder.textBlobToGlyphRunListIgnoringRSXForm(*paint, *this, SkPoint{0, 0});
- auto glyphRunList = builder.useGlyphRunList();
- int intervalCount = 0;
- for (const SkGlyphRun& glyphRun : glyphRunList) {
- intervalCount = get_glyph_run_intercepts(glyphRun, *paint, bounds, intervals, &intervalCount);
- }
- return intervalCount;
- }
|