| /* | 
 |  * Copyright 2019 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #ifndef SkGlyphBuffer_DEFINED | 
 | #define SkGlyphBuffer_DEFINED | 
 |  | 
 | #include "src/core/SkEnumerate.h" | 
 | #include "src/core/SkGlyph.h" | 
 | #include "src/core/SkZip.h" | 
 |  | 
 | #include <climits> | 
 |  | 
 | struct SkGlyphPositionRoundingSpec; | 
 | class SkPath; | 
 | class SkDrawable; | 
 |  | 
 | namespace sktext { | 
 | class StrikeForGPU; | 
 | }  // namespace sktext | 
 |  | 
 | // SkSourceGlyphBuffer is the source of glyphs between the different stages of glyph drawing. | 
 | // It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs | 
 | // are reject by a stage they become the source for the next stage. | 
 | class SkSourceGlyphBuffer { | 
 | public: | 
 |     SkSourceGlyphBuffer() = default; | 
 |  | 
 |     void setSource(SkZip<const SkGlyphID, const SkPoint> source) { | 
 |         this->~SkSourceGlyphBuffer(); | 
 |         new (this) SkSourceGlyphBuffer{source}; | 
 |     } | 
 |  | 
 |     void reset(); | 
 |  | 
 |     void reject(size_t index) { | 
 |         SkASSERT(index < fSource.size()); | 
 |         if (!this->sourceIsRejectBuffers()) { | 
 |             // Need to expand the buffers for first use. All other reject sets will be fewer than | 
 |             // this one. | 
 |             auto [glyphID, pos] = fSource[index]; | 
 |             fRejectedGlyphIDs.push_back(glyphID); | 
 |             fRejectedPositions.push_back(pos); | 
 |             fRejectSize++; | 
 |         } else { | 
 |             SkASSERT(fRejectSize < fRejects.size()); | 
 |             fRejects[fRejectSize++] = fSource[index]; | 
 |         } | 
 |     } | 
 |  | 
 |     SkZip<const SkGlyphID, const SkPoint> flipRejectsToSource() { | 
 |         fRejects = SkMakeZip(fRejectedGlyphIDs, fRejectedPositions).first(fRejectSize); | 
 |         fSource = fRejects; | 
 |         fRejectSize = 0; | 
 |         return fSource; | 
 |     } | 
 |  | 
 |     SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; } | 
 |  | 
 | private: | 
 |     SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) { | 
 |         fSource = source; | 
 |     } | 
 |     bool sourceIsRejectBuffers() const { | 
 |         return fSource.get<0>().data() == fRejectedGlyphIDs.data(); | 
 |     } | 
 |  | 
 |     SkZip<const SkGlyphID, const SkPoint> fSource; | 
 |     size_t fRejectSize{0}; | 
 |  | 
 |     SkZip<SkGlyphID, SkPoint> fRejects; | 
 |     SkSTArray<4, SkGlyphID> fRejectedGlyphIDs; | 
 |     SkSTArray<4, SkPoint> fRejectedPositions; | 
 | }; | 
 |  | 
 | // A memory format that allows an SkPackedGlyphID, SkGlyph*, and SkPath* to occupy the same | 
 | // memory. This allows SkPackedGlyphIDs as input, and SkGlyph*/SkPath* as output using the same | 
 | // memory. | 
 | class SkGlyphVariant { | 
 | public: | 
 |     SkGlyphVariant() : fV{nullptr} { } | 
 |     SkGlyphVariant& operator= (SkPackedGlyphID packedID) { | 
 |         fV.packedID = packedID; | 
 |         SkDEBUGCODE(fTag = kPackedID); | 
 |         return *this; | 
 |     } | 
 |     SkGlyphVariant& operator= (const SkGlyph* glyph) { | 
 |         fV.glyph = glyph; | 
 |         SkDEBUGCODE(fTag = kGlyph); | 
 |         return *this; | 
 |     } | 
 |  | 
 |     const SkGlyph* glyph() const { | 
 |         SkASSERT(fTag == kGlyph); | 
 |         return fV.glyph; | 
 |     } | 
 |     SkPackedGlyphID packedID() const { | 
 |         SkASSERT(fTag == kPackedID); | 
 |         return fV.packedID; | 
 |     } | 
 |  | 
 |     operator SkPackedGlyphID()  const { return this->packedID(); } | 
 |     operator const SkGlyph*()   const { return this->glyph();    } | 
 |  | 
 | private: | 
 |     union { | 
 |         const SkGlyph* glyph; | 
 |         SkPackedGlyphID packedID; | 
 |     } fV; | 
 |  | 
 | #ifdef SK_DEBUG | 
 |     enum { | 
 |         kEmpty, | 
 |         kPackedID, | 
 |         kGlyph, | 
 |     } fTag{kEmpty}; | 
 | #endif | 
 | }; | 
 |  | 
 | // A buffer for converting SkPackedGlyph to SkGlyph*s. Initially the buffer contains | 
 | // SkPackedGlyphIDs, but those are used to lookup SkGlyph*s which are then copied over the | 
 | // SkPackedGlyphIDs. | 
 | class SkDrawableGlyphBuffer { | 
 | public: | 
 |     void ensureSize(size_t size); | 
 |  | 
 |     // Load the buffer with SkPackedGlyphIDs and positions at (0, 0) ready to finish positioning | 
 |     // during drawing. | 
 |     void startSource(const SkZip<const SkGlyphID, const SkPoint>& source); | 
 |  | 
 |     void startSourceWithMatrixAdjustment( | 
 |             const SkZip<const SkGlyphID, const SkPoint>& source, const SkMatrix& creationMatrix); | 
 |  | 
 |     // Load the buffer with SkPackedGlyphIDs, calculating positions, so they can be constant. | 
 |     // | 
 |     // The positions are calculated integer positions in devices space, and the mapping of | 
 |     // the source origin through the initial matrix is returned. It is given that these positions | 
 |     // are only reused when the blob is translated by an integral amount. Thus, the shifted | 
 |     // positions are given by the following equation where (ix, iy) is the integer positions of | 
 |     // the glyph, initialMappedOrigin is (0,0) in source mapped to the device using the initial | 
 |     // matrix, and newMappedOrigin is (0,0) in source mapped to the device using the current | 
 |     // drawing matrix. | 
 |     // | 
 |     //    (ix', iy') = (ix, iy) + round(newMappedOrigin - initialMappedOrigin) | 
 |     // | 
 |     // In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of | 
 |     // floating point don't guarantee that, so force it to integer. | 
 |     // | 
 |     // N.B. The positionMatrix is already translated by the origin of the glyph run list. | 
 |     void startDevicePositioning( | 
 |             const SkZip<const SkGlyphID, const SkPoint>& source, | 
 |             const SkMatrix& positionMatrix, | 
 |             const SkGlyphPositionRoundingSpec& roundingSpec); | 
 |  | 
 |     SkString dumpInput() const; | 
 |  | 
 |     // The input of SkPackedGlyphIDs | 
 |     SkZip<SkGlyphVariant, SkPoint> input() { | 
 |         SkASSERT(fPhase == kInput); | 
 |         SkDEBUGCODE(fPhase = kProcess); | 
 |         return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer.get(), fPositions}; | 
 |     } | 
 |  | 
 |     // Store the glyph in the next slot, using the position information located at index from. | 
 |     void accept(SkGlyph* glyph, size_t from) { | 
 |         SkASSERT(fPhase == kProcess); | 
 |         SkASSERT(fAcceptedSize <= from); | 
 |         fPositions[fAcceptedSize] = fPositions[from]; | 
 |         fMultiBuffer[fAcceptedSize] = glyph; | 
 |         fFormats[fAcceptedSize] = glyph->maskFormat(); | 
 |         fAcceptedSize++; | 
 |     } | 
 |  | 
 |     void accept(SkPackedGlyphID glyphID, SkPoint position, SkMask::Format format) { | 
 |         SkASSERT(fPhase == kProcess); | 
 |         fPositions[fAcceptedSize] = position; | 
 |         fMultiBuffer[fAcceptedSize] = glyphID; | 
 |         fFormats[fAcceptedSize] = format; | 
 |         fAcceptedSize++; | 
 |     } | 
 |  | 
 |     void accept(SkPackedGlyphID glyphID, SkPoint position) { | 
 |         SkASSERT(fPhase == kProcess); | 
 |         fPositions[fAcceptedSize] = position; | 
 |         fMultiBuffer[fAcceptedSize] = glyphID; | 
 |         fAcceptedSize++; | 
 |     } | 
 |  | 
 |     // The result after a series of `accept` of accepted SkGlyph* or SkPath*. | 
 |     SkZip<SkGlyphVariant, SkPoint> accepted() { | 
 |         SkASSERT(fPhase == kProcess); | 
 |         SkDEBUGCODE(fPhase = kDraw); | 
 |         return SkZip<SkGlyphVariant, SkPoint>{fAcceptedSize, fMultiBuffer.get(), fPositions}; | 
 |     } | 
 |  | 
 |     SkZip<SkGlyphVariant, SkPoint, SkMask::Format> acceptedWithMaskFormat() { | 
 |         SkASSERT(fPhase == kProcess); | 
 |         SkDEBUGCODE(fPhase = kDraw); | 
 |         return SkZip<SkGlyphVariant, SkPoint, SkMask::Format>{ | 
 |                 fAcceptedSize, fMultiBuffer.get(), fPositions, fFormats}; | 
 |     } | 
 |  | 
 |     bool empty() const { | 
 |         SkASSERT(fPhase == kProcess || fPhase == kDraw); | 
 |         return fAcceptedSize == 0; | 
 |     } | 
 |  | 
 |     void reset(); | 
 |  | 
 |     template <typename Fn> | 
 |     void forEachInput(Fn&& fn) { | 
 |         for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) { | 
 |             fn(i, packedID.packedID(), pos); | 
 |         } | 
 |     } | 
 |  | 
 | private: | 
 |     size_t fMaxSize{0}; | 
 |     size_t fInputSize{0}; | 
 |     size_t fAcceptedSize{0}; | 
 |     SkAutoTArray<SkGlyphVariant> fMultiBuffer; | 
 |     SkAutoTMalloc<SkPoint> fPositions; | 
 |     SkAutoTMalloc<SkMask::Format> fFormats; | 
 |  | 
 | #ifdef SK_DEBUG | 
 |     enum { | 
 |         kReset, | 
 |         kInput, | 
 |         kProcess, | 
 |         kDraw | 
 |     } fPhase{kReset}; | 
 | #endif | 
 | }; | 
 | #endif  // SkGlyphBuffer_DEFINED |