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 #include <climits> 16 17 class SkStrikeForGPU; 18 struct SkGlyphPositionRoundingSpec; 19 class SkPath; 20 class SkDrawable; 21 22 // SkSourceGlyphBuffer is the source of glyphs between the different stages of glyph drawing. 23 // It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs 24 // are reject by a stage they become the source for the next stage. 25 class SkSourceGlyphBuffer { 26 public: 27 SkSourceGlyphBuffer() = default; 28 setSource(SkZip<const SkGlyphID,const SkPoint> source)29 void setSource(SkZip<const SkGlyphID, const SkPoint> source) { 30 this->~SkSourceGlyphBuffer(); 31 new (this) SkSourceGlyphBuffer{source}; 32 } 33 34 void reset(); 35 reject(size_t index)36 void reject(size_t index) { 37 SkASSERT(index < fSource.size()); 38 if (!this->sourceIsRejectBuffers()) { 39 // Need to expand the buffers for first use. All other reject sets will be fewer than 40 // this one. 41 auto [glyphID, pos] = fSource[index]; 42 fRejectedGlyphIDs.push_back(glyphID); 43 fRejectedPositions.push_back(pos); 44 fRejectSize++; 45 } else { 46 SkASSERT(fRejectSize < fRejects.size()); 47 fRejects[fRejectSize++] = fSource[index]; 48 } 49 } 50 reject(size_t index,int rejectedMaxDimension)51 void reject(size_t index, int rejectedMaxDimension) { 52 auto [prevMin, prevMax] = fMaxDimensionHintForRejects; 53 fMaxDimensionHintForRejects = 54 {std::min(prevMin, rejectedMaxDimension), 55 std::max(prevMax, rejectedMaxDimension)}; 56 this->reject(index); 57 } 58 flipRejectsToSource()59 SkZip<const SkGlyphID, const SkPoint> flipRejectsToSource() { 60 fRejects = SkMakeZip(fRejectedGlyphIDs, fRejectedPositions).first(fRejectSize); 61 fSource = fRejects; 62 fRejectSize = 0; 63 fMaxDimensionHintForSource = fMaxDimensionHintForRejects; 64 fMaxDimensionHintForRejects = {INT_MAX, 0}; 65 return fSource; 66 } 67 source()68 SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; } 69 maxDimensionHint()70 std::tuple<int, int> maxDimensionHint() const {return fMaxDimensionHintForSource;} 71 72 private: SkSourceGlyphBuffer(const SkZip<const SkGlyphID,const SkPoint> & source)73 SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) { 74 fSource = source; 75 } sourceIsRejectBuffers()76 bool sourceIsRejectBuffers() const { 77 return fSource.get<0>().data() == fRejectedGlyphIDs.data(); 78 } 79 80 SkZip<const SkGlyphID, const SkPoint> fSource; 81 size_t fRejectSize{0}; 82 83 // Calculate the smallest and largest max glyph dimension. fMaxDimensionHintForSource captures 84 // fMaxDimensionHintForRejects when flipping rejects to the source. 85 std::tuple<int, int> fMaxDimensionHintForSource{INT_MAX, 0}; 86 std::tuple<int, int> fMaxDimensionHintForRejects{INT_MAX, 0}; 87 88 SkZip<SkGlyphID, SkPoint> fRejects; 89 SkSTArray<4, SkGlyphID> fRejectedGlyphIDs; 90 SkSTArray<4, SkPoint> fRejectedPositions; 91 }; 92 93 // A memory format that allows an SkPackedGlyphID, SkGlyph*, and SkPath* to occupy the same 94 // memory. This allows SkPackedGlyphIDs as input, and SkGlyph*/SkPath* as output using the same 95 // memory. 96 class SkGlyphVariant { 97 public: SkGlyphVariant()98 SkGlyphVariant() : fV{nullptr} { } 99 SkGlyphVariant& operator= (SkPackedGlyphID packedID) { 100 fV.packedID = packedID; 101 SkDEBUGCODE(fTag = kPackedID); 102 return *this; 103 } 104 SkGlyphVariant& operator= (const SkGlyph* glyph) { 105 fV.glyph = glyph; 106 SkDEBUGCODE(fTag = kGlyph); 107 return *this; 108 109 } 110 SkGlyphVariant& operator= (const SkPath* path) { 111 fV.path = path; 112 SkDEBUGCODE(fTag = kPath); 113 return *this; 114 } 115 SkGlyphVariant& operator= (SkDrawable* drawable) { 116 fV.drawable = drawable; 117 SkDEBUGCODE(fTag = kDrawable); 118 return *this; 119 } 120 glyph()121 const SkGlyph* glyph() const { 122 SkASSERT(fTag == kGlyph); 123 return fV.glyph; 124 } path()125 const SkPath* path() const { 126 SkASSERT(fTag == kPath); 127 return fV.path; 128 } drawable()129 SkDrawable* drawable() const { 130 SkASSERT(fTag == kDrawable); 131 return fV.drawable; 132 } packedID()133 SkPackedGlyphID packedID() const { 134 SkASSERT(fTag == kPackedID); 135 return fV.packedID; 136 } 137 SkPackedGlyphID()138 operator SkPackedGlyphID() const { return this->packedID(); } 139 operator const SkGlyph*() const { return this->glyph(); } 140 operator const SkPath*() const { return this->path(); } 141 operator const SkDrawable*()const { return this->drawable(); } 142 143 private: 144 union { 145 const SkGlyph* glyph; 146 const SkPath* path; 147 SkDrawable* drawable; 148 SkPackedGlyphID packedID; 149 } fV; 150 151 #ifdef SK_DEBUG 152 enum { 153 kEmpty, 154 kPackedID, 155 kGlyph, 156 kPath, 157 kDrawable, 158 } fTag{kEmpty}; 159 #endif 160 }; 161 162 // A buffer for converting SkPackedGlyph to SkGlyph* or SkPath*. Initially the buffer contains 163 // SkPackedGlyphIDs, but those are used to lookup SkGlyph*/SkPath* which are then copied over the 164 // SkPackedGlyphIDs. 165 class SkDrawableGlyphBuffer { 166 public: 167 void ensureSize(size_t size); 168 169 // Load the buffer with SkPackedGlyphIDs and positions at (0, 0) ready to finish positioning 170 // during drawing. 171 void startSource(const SkZip<const SkGlyphID, const SkPoint>& source); 172 173 // Load the buffer with SkPackedGlyphIDs and positions using the device transform. 174 void startBitmapDevice( 175 const SkZip<const SkGlyphID, const SkPoint>& source, 176 SkPoint origin, const SkMatrix& viewMatrix, 177 const SkGlyphPositionRoundingSpec& roundingSpec); 178 179 // Load the buffer with SkPackedGlyphIDs, calculating positions so they can be constant. 180 // 181 // The positions are calculated integer positions in devices space, and the mapping of the 182 // the source origin through the initial matrix is returned. It is given that these positions 183 // are only reused when the blob is translated by an integral amount. Thus the shifted 184 // positions are given by the following equation where (ix, iy) is the integer positions of 185 // the glyph, initialMappedOrigin is (0,0) in source mapped to the device using the initial 186 // matrix, and newMappedOrigin is (0,0) in source mapped to the device using the current 187 // drawing matrix. 188 // 189 // (ix', iy') = (ix, iy) + round(newMappedOrigin - initialMappedOrigin) 190 // 191 // In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of 192 // floating point don't guarantee that, so force it to integer. 193 void startGPUDevice( 194 const SkZip<const SkGlyphID, const SkPoint>& source, 195 const SkMatrix& drawMatrix, 196 const SkGlyphPositionRoundingSpec& roundingSpec); 197 198 SkString dumpInput() const; 199 200 // The input of SkPackedGlyphIDs input()201 SkZip<SkGlyphVariant, SkPoint> input() { 202 SkASSERT(fPhase == kInput); 203 SkDEBUGCODE(fPhase = kProcess); 204 return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer.get(), fPositions}; 205 } 206 207 // Store the glyph in the next slot, using the position information located at index from. accept(SkGlyph * glyph,size_t from)208 void accept(SkGlyph* glyph, size_t from) { 209 SkASSERT(fPhase == kProcess); 210 SkASSERT(fAcceptedSize <= from); 211 fPositions[fAcceptedSize] = fPositions[from]; 212 fMultiBuffer[fAcceptedSize] = glyph; 213 fAcceptedSize++; 214 } 215 216 // Store the path in the next slot, using the position information located at index from. accept(const SkPath * path,size_t from)217 void accept(const SkPath* path, size_t from) { 218 SkASSERT(fPhase == kProcess); 219 SkASSERT(fAcceptedSize <= from); 220 fPositions[fAcceptedSize] = fPositions[from]; 221 fMultiBuffer[fAcceptedSize] = path; 222 fAcceptedSize++; 223 } 224 225 // Store drawable in the next slot, using the position information located at index from. accept(SkDrawable * drawable,size_t from)226 void accept(SkDrawable* drawable, size_t from) { 227 SkASSERT(fPhase == kProcess); 228 SkASSERT(fAcceptedSize <= from); 229 fPositions[fAcceptedSize] = fPositions[from]; 230 fMultiBuffer[fAcceptedSize] = drawable; 231 fAcceptedSize++; 232 } 233 234 // The result after a series of `accept` of accepted SkGlyph* or SkPath*. accepted()235 SkZip<SkGlyphVariant, SkPoint> accepted() { 236 SkASSERT(fPhase == kProcess); 237 SkDEBUGCODE(fPhase = kDraw); 238 return SkZip<SkGlyphVariant, SkPoint>{fAcceptedSize, fMultiBuffer.get(), fPositions}; 239 } 240 empty()241 bool empty() const { 242 SkASSERT(fPhase == kProcess || fPhase == kDraw); 243 return fAcceptedSize == 0; 244 } 245 246 void reset(); 247 248 template <typename Fn> forEachInput(Fn && fn)249 void forEachInput(Fn&& fn) { 250 for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) { 251 fn(i, packedID.packedID(), pos); 252 } 253 } 254 255 private: 256 size_t fMaxSize{0}; 257 size_t fInputSize{0}; 258 size_t fAcceptedSize{0}; 259 SkAutoTArray<SkGlyphVariant> fMultiBuffer; 260 SkAutoTMalloc<SkPoint> fPositions; 261 262 #ifdef SK_DEBUG 263 enum { 264 kReset, 265 kInput, 266 kProcess, 267 kDraw 268 } fPhase{kReset}; 269 #endif 270 }; 271 #endif // SkGlyphBuffer_DEFINED 272