| 1 | /* |
| 2 | * Copyright 2019 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #ifndef SkGlyphBuffer_DEFINED |
| 9 | #define SkGlyphBuffer_DEFINED |
| 10 | |
| 11 | #include "src/core/SkEnumerate.h" |
| 12 | #include "src/core/SkGlyph.h" |
| 13 | #include "src/core/SkZip.h" |
| 14 | |
| 15 | class SkStrikeForGPU; |
| 16 | struct SkGlyphPositionRoundingSpec; |
| 17 | |
| 18 | // SkSourceGlyphBuffer is the source of glyphs between the different stages of character drawing. |
| 19 | // It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs |
| 20 | // are reject by a stage they become the source for the next stage. |
| 21 | class SkSourceGlyphBuffer { |
| 22 | public: |
| 23 | SkSourceGlyphBuffer() = default; |
| 24 | |
| 25 | void setSource(SkZip<const SkGlyphID, const SkPoint> source) { |
| 26 | this->~SkSourceGlyphBuffer(); |
| 27 | new (this) SkSourceGlyphBuffer{source}; |
| 28 | } |
| 29 | |
| 30 | void reset(); |
| 31 | |
| 32 | void reject(size_t index) { |
| 33 | SkASSERT(index < fSource.size()); |
| 34 | if (!this->sourceIsRejectBuffers()) { |
| 35 | // Need to expand the buffers for first use. All other reject sets will be fewer than |
| 36 | // this one. |
| 37 | auto [glyphID, pos] = fSource[index]; |
| 38 | fRejectedGlyphIDs.push_back(glyphID); |
| 39 | fRejectedPositions.push_back(pos); |
| 40 | fRejectSize++; |
| 41 | } else { |
| 42 | SkASSERT(fRejectSize < fRejects.size()); |
| 43 | fRejects[fRejectSize++] = fSource[index]; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | void reject(size_t index, int rejectedMaxDimension) { |
| 48 | fRejectedMaxDimension = std::max(fRejectedMaxDimension, rejectedMaxDimension); |
| 49 | this->reject(index); |
| 50 | } |
| 51 | |
| 52 | SkZip<const SkGlyphID, const SkPoint> flipRejectsToSource() { |
| 53 | fRejects = SkMakeZip(fRejectedGlyphIDs, fRejectedPositions).first(fRejectSize); |
| 54 | fSource = fRejects; |
| 55 | fRejectSize = 0; |
| 56 | fSourceMaxDimension = fRejectedMaxDimension; |
| 57 | fRejectedMaxDimension = 0; |
| 58 | return fSource; |
| 59 | } |
| 60 | |
| 61 | SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; } |
| 62 | |
| 63 | int rejectedMaxDimension() const { return fSourceMaxDimension; } |
| 64 | |
| 65 | private: |
| 66 | SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) { |
| 67 | fSource = source; |
| 68 | } |
| 69 | bool sourceIsRejectBuffers() const { |
| 70 | return fSource.get<0>().data() == fRejectedGlyphIDs.data(); |
| 71 | } |
| 72 | |
| 73 | SkZip<const SkGlyphID, const SkPoint> fSource; |
| 74 | size_t fRejectSize{0}; |
| 75 | int fSourceMaxDimension{0}; |
| 76 | int fRejectedMaxDimension{0}; |
| 77 | SkZip<SkGlyphID, SkPoint> fRejects; |
| 78 | SkSTArray<4, SkGlyphID> fRejectedGlyphIDs; |
| 79 | SkSTArray<4, SkPoint> fRejectedPositions; |
| 80 | }; |
| 81 | |
| 82 | // A memory format that allows an SkPackedGlyphID, SkGlyph*, and SkPath* to occupy the same |
| 83 | // memory. This allows SkPackedGlyphIDs as input, and SkGlyph*/SkPath* as output using the same |
| 84 | // memory. |
| 85 | class SkGlyphVariant { |
| 86 | public: |
| 87 | SkGlyphVariant() : fV{nullptr} { } |
| 88 | SkGlyphVariant& operator= (SkPackedGlyphID packedID) { |
| 89 | fV.packedID = packedID; |
| 90 | SkDEBUGCODE(fTag = kPackedID); |
| 91 | return *this; |
| 92 | } |
| 93 | SkGlyphVariant& operator= (SkGlyph* glyph) { |
| 94 | fV.glyph = glyph; |
| 95 | SkDEBUGCODE(fTag = kGlyph); |
| 96 | return *this; |
| 97 | |
| 98 | } |
| 99 | SkGlyphVariant& operator= (const SkPath* path) { |
| 100 | fV.path = path; |
| 101 | SkDEBUGCODE(fTag = kPath); |
| 102 | return *this; |
| 103 | } |
| 104 | |
| 105 | SkGlyph* glyph() const { |
| 106 | SkASSERT(fTag == kGlyph); |
| 107 | return fV.glyph; |
| 108 | } |
| 109 | const SkPath* path() const { |
| 110 | SkASSERT(fTag == kPath); |
| 111 | return fV.path; |
| 112 | } |
| 113 | SkPackedGlyphID packedID() const { |
| 114 | SkASSERT(fTag == kPackedID); |
| 115 | return fV.packedID; |
| 116 | } |
| 117 | |
| 118 | operator SkPackedGlyphID() const { return this->packedID(); } |
| 119 | operator SkGlyph*() const { return this->glyph(); } |
| 120 | operator const SkPath*() const { return this->path(); } |
| 121 | |
| 122 | private: |
| 123 | union { |
| 124 | SkGlyph* glyph; |
| 125 | const SkPath* path; |
| 126 | SkPackedGlyphID packedID; |
| 127 | } fV; |
| 128 | |
| 129 | #ifdef SK_DEBUG |
| 130 | enum { |
| 131 | kEmpty, |
| 132 | kPackedID, |
| 133 | kGlyph, |
| 134 | kPath |
| 135 | } fTag{kEmpty}; |
| 136 | #endif |
| 137 | }; |
| 138 | |
| 139 | // A buffer for converting SkPackedGlyph to SkGlyph* or SkPath*. Initially the buffer contains |
| 140 | // SkPackedGlyphIDs, but those are used to lookup SkGlyph*/SkPath* which are then copied over the |
| 141 | // SkPackedGlyphIDs. |
| 142 | class SkDrawableGlyphBuffer { |
| 143 | public: |
| 144 | void ensureSize(size_t size); |
| 145 | |
| 146 | // Load the buffer with SkPackedGlyphIDs and positions in source space. |
| 147 | void startSource(const SkZip<const SkGlyphID, const SkPoint>& source, SkPoint origin); |
| 148 | |
| 149 | // Use the original glyphIDs and positions. |
| 150 | void startPaths(const SkZip<const SkGlyphID, const SkPoint>& source); |
| 151 | |
| 152 | // Load the buffer with SkPackedGlyphIDs and positions using the device transform. |
| 153 | void startDevice( |
| 154 | const SkZip<const SkGlyphID, const SkPoint>& source, |
| 155 | SkPoint origin, const SkMatrix& viewMatrix, |
| 156 | const SkGlyphPositionRoundingSpec& roundingSpec); |
| 157 | |
| 158 | // The input of SkPackedGlyphIDs |
| 159 | SkZip<SkGlyphVariant, SkPoint> input() { |
| 160 | SkASSERT(fPhase == kInput); |
| 161 | SkDEBUGCODE(fPhase = kProcess); |
| 162 | return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer, fPositions}; |
| 163 | } |
| 164 | |
| 165 | // Store the glyph in the next drawable slot, using the position information located at index |
| 166 | // from. |
| 167 | void push_back(SkGlyph* glyph, size_t from) { |
| 168 | SkASSERT(fPhase == kProcess); |
| 169 | SkASSERT(fDrawableSize <= from); |
| 170 | fPositions[fDrawableSize] = fPositions[from]; |
| 171 | fMultiBuffer[fDrawableSize] = glyph; |
| 172 | fDrawableSize++; |
| 173 | } |
| 174 | |
| 175 | // Store the path in the next drawable slot, using the position information located at index |
| 176 | // from. |
| 177 | void push_back(const SkPath* path, size_t from) { |
| 178 | SkASSERT(fPhase == kProcess); |
| 179 | SkASSERT(fDrawableSize <= from); |
| 180 | fPositions[fDrawableSize] = fPositions[from]; |
| 181 | fMultiBuffer[fDrawableSize] = path; |
| 182 | fDrawableSize++; |
| 183 | } |
| 184 | |
| 185 | // The result after a series of push_backs of drawable SkGlyph* or SkPath*. |
| 186 | SkZip<SkGlyphVariant, SkPoint> drawable() { |
| 187 | SkASSERT(fPhase == kProcess); |
| 188 | SkDEBUGCODE(fPhase = kDraw); |
| 189 | return SkZip<SkGlyphVariant, SkPoint>{fDrawableSize, fMultiBuffer, fPositions}; |
| 190 | } |
| 191 | |
| 192 | void reset(); |
| 193 | |
| 194 | template <typename Fn> |
| 195 | void forEachGlyphID(Fn&& fn) { |
| 196 | for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) { |
| 197 | fn(i, packedID.packedID(), pos); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | private: |
| 202 | size_t fMaxSize{0}; |
| 203 | size_t fInputSize{0}; |
| 204 | size_t fDrawableSize{0}; |
| 205 | SkAutoTMalloc<SkGlyphVariant> fMultiBuffer; |
| 206 | SkAutoTMalloc<SkPoint> fPositions; |
| 207 | |
| 208 | #ifdef SK_DEBUG |
| 209 | enum { |
| 210 | kReset, |
| 211 | kInput, |
| 212 | kProcess, |
| 213 | kDraw |
| 214 | } fPhase{kReset}; |
| 215 | #endif |
| 216 | }; |
| 217 | #endif // SkGlyphBuffer_DEFINED |
| 218 | |