/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTextBlobPriv_DEFINED #define SkTextBlobPriv_DEFINED #include "include/core/SkColorFilter.h" #include "include/core/SkFont.h" #include "include/core/SkImageFilter.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkPathEffect.h" #include "include/core/SkShader.h" #include "include/core/SkTextBlob.h" #include "include/core/SkTypeface.h" #include "src/base/SkSafeMath.h" #include "src/core/SkPaintPriv.h" class SkReadBuffer; class SkWriteBuffer; class SkTextBlobPriv { public: /** * Serialize to a buffer. */ static void Flatten(const SkTextBlob& , SkWriteBuffer&); /** * Recreate an SkTextBlob that was serialized into a buffer. * * @param SkReadBuffer Serialized blob data. * @return A new SkTextBlob representing the serialized data, or NULL if the buffer is * invalid. */ static sk_sp MakeFromBuffer(SkReadBuffer&); static bool HasRSXForm(const SkTextBlob& blob); }; // // Textblob data is laid out into externally-managed storage as follows: // // ----------------------------------------------------------------------------- // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ... // ----------------------------------------------------------------------------- // // Each run record describes a text blob run, and can be used to determine the (implicit) // location of the following record. // // Extended Textblob runs have more data after the Pos[] array: // // ------------------------------------------------------------------------- // ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ... // ------------------------------------------------------------------------- // // To determine the length of the extended run data, the TextSize must be read. // // Extended Textblob runs may be mixed with non-extended runs. SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) class SkTextBlob::RunRecord { public: RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkFont& font, GlyphPositioning pos) : fFont(font) , fCount(count) , fOffset(offset) , fFlags(pos) { SkASSERT(static_cast(pos) <= Flags::kPositioning_Mask); SkDEBUGCODE(fMagic = kRunRecordMagic); if (textSize > 0) { fFlags |= kExtended_Flag; *this->textSizePtr() = textSize; } } uint32_t glyphCount() const { return fCount; } const SkPoint& offset() const { return fOffset; } const SkFont& font() const { return fFont; } GlyphPositioning positioning() const { return static_cast(fFlags & kPositioning_Mask); } uint16_t* glyphBuffer() const { static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); // Glyphs are stored immediately following the record. return reinterpret_cast(const_cast(this) + 1); } // can be aliased with pointBuffer() or xformBuffer() SkScalar* posBuffer() const { // Position scalars follow the (aligned) glyph buffer. return reinterpret_cast(reinterpret_cast(this->glyphBuffer()) + SkAlign4(fCount * sizeof(uint16_t))); } // alias for posBuffer() SkPoint* pointBuffer() const { SkASSERT(this->positioning() == (GlyphPositioning)2); return reinterpret_cast(this->posBuffer()); } // alias for posBuffer() SkRSXform* xformBuffer() const { SkASSERT(this->positioning() == (GlyphPositioning)3); return reinterpret_cast(this->posBuffer()); } uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; } uint32_t* clusterBuffer() const { // clusters follow the textSize. return isExtended() ? 1 + this->textSizePtr() : nullptr; } char* textBuffer() const { return isExtended() ? reinterpret_cast(this->clusterBuffer() + fCount) : nullptr; } bool isLastRun() const { return SkToBool(fFlags & kLast_Flag); } static size_t StorageSize(uint32_t glyphCount, uint32_t textSize, SkTextBlob::GlyphPositioning positioning, SkSafeMath* safe); static const RunRecord* First(const SkTextBlob* blob); static const RunRecord* Next(const RunRecord* run); void validate(const uint8_t* storageTop) const; private: friend class SkTextBlobBuilder; enum Flags { kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning kLast_Flag = 0x04, // set for the last blob run kExtended_Flag = 0x08, // set for runs with text/cluster info }; static const RunRecord* NextUnchecked(const RunRecord* run); static size_t PosCount(uint32_t glyphCount, SkTextBlob::GlyphPositioning positioning, SkSafeMath* safe); uint32_t* textSizePtr() const; void grow(uint32_t count); bool isExtended() const { return fFlags & kExtended_Flag; } SkFont fFont; uint32_t fCount; SkPoint fOffset; uint32_t fFlags; SkDEBUGCODE(unsigned fMagic;) }; /** * Iterate through all of the text runs of the text blob. For example: * for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) { * ..... * } */ class SkTextBlobRunIterator { public: SkTextBlobRunIterator(const SkTextBlob* blob); enum 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. }; bool done() const { return !fCurrentRun; } void next(); uint32_t glyphCount() const { SkASSERT(!this->done()); return fCurrentRun->glyphCount(); } const uint16_t* glyphs() const { SkASSERT(!this->done()); return fCurrentRun->glyphBuffer(); } const SkScalar* pos() const { SkASSERT(!this->done()); return fCurrentRun->posBuffer(); } // alias for pos() const SkPoint* points() const { return fCurrentRun->pointBuffer(); } // alias for pos() const SkRSXform* xforms() const { return fCurrentRun->xformBuffer(); } const SkPoint& offset() const { SkASSERT(!this->done()); return fCurrentRun->offset(); } const SkFont& font() const { SkASSERT(!this->done()); return fCurrentRun->font(); } GlyphPositioning positioning() const; unsigned scalarsPerGlyph() const; uint32_t* clusters() const { SkASSERT(!this->done()); return fCurrentRun->clusterBuffer(); } uint32_t textSize() const { SkASSERT(!this->done()); return fCurrentRun->textSize(); } char* text() const { SkASSERT(!this->done()); return fCurrentRun->textBuffer(); } bool isLCD() const; private: const SkTextBlob::RunRecord* fCurrentRun; SkDEBUGCODE(uint8_t* fStorageTop;) }; inline bool SkTextBlobPriv::HasRSXForm(const SkTextBlob& blob) { for (SkTextBlobRunIterator i{&blob}; !i.done(); i.next()) { if (i.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) { return true; } } return false; } #endif // SkTextBlobPriv_DEFINED