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 
setSource(SkZip<const SkGlyphID,const SkPoint> source)25     void setSource(SkZip<const SkGlyphID, const SkPoint> source) {
26         this->~SkSourceGlyphBuffer();
27         new (this) SkSourceGlyphBuffer{source};
28     }
29 
30     void reset();
31 
reject(size_t index)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 
reject(size_t index,int rejectedMaxDimension)47     void reject(size_t index, int rejectedMaxDimension) {
48         fRejectedMaxDimension = std::max(fRejectedMaxDimension, rejectedMaxDimension);
49         this->reject(index);
50     }
51 
flipRejectsToSource()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 
source()61     SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; }
62 
rejectedMaxDimension()63     int rejectedMaxDimension() const { return fSourceMaxDimension; }
64 
65 private:
SkSourceGlyphBuffer(const SkZip<const SkGlyphID,const SkPoint> & source)66     SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) {
67         fSource = source;
68     }
sourceIsRejectBuffers()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:
SkGlyphVariant()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 
glyph()105     SkGlyph* glyph() const {
106         SkASSERT(fTag == kGlyph);
107         return fV.glyph;
108     }
path()109     const SkPath* path() const {
110         SkASSERT(fTag == kPath);
111         return fV.path;
112     }
packedID()113     SkPackedGlyphID packedID() const {
114         SkASSERT(fTag == kPackedID);
115         return fV.packedID;
116     }
117 
SkPackedGlyphID()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 at (0, 0) ready to finish positioning
147     // during drawing.
148     void startSource(const SkZip<const SkGlyphID, const SkPoint>& source);
149 
150     // Load the buffer with SkPackedGlyphIDs and positions using the device transform.
151     void startBitmapDevice(
152             const SkZip<const SkGlyphID, const SkPoint>& source,
153             SkPoint origin, const SkMatrix& viewMatrix,
154             const SkGlyphPositionRoundingSpec& roundingSpec);
155 
156     // Load the buffer with SkPackedGlyphIDs, calculating positions so they can be constant.
157     //
158     // The positions are calculated integer positions in devices space, and the mapping of the
159     // the source origin through the initial matrix is returned. It is given that these positions
160     // are only reused when the blob is translated by an integral amount. Thus the shifted
161     // positions are given by the following equation where (ix, iy) is the integer positions of
162     // the glyph, initialMappedOrigin is (0,0) in source mapped to the device using the initial
163     // matrix, and newMappedOrigin is (0,0) in source mapped to the device using the current
164     // drawing matrix.
165     //
166     //    (ix', iy') = (ix, iy) + round(newMappedOrigin - initialMappedOrigin)
167     //
168     // In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of
169     // floating point don't guarantee that, so force it to integer.
170     void startGPUDevice(
171             const SkZip<const SkGlyphID, const SkPoint>& source,
172             const SkMatrix& drawMatrix,
173             const SkGlyphPositionRoundingSpec& roundingSpec);
174 
175     SkString dumpInput() const;
176 
177     // The input of SkPackedGlyphIDs
input()178     SkZip<SkGlyphVariant, SkPoint> input() {
179         SkASSERT(fPhase == kInput);
180         SkDEBUGCODE(fPhase = kProcess);
181         return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer.get(), fPositions};
182     }
183 
184     // Store the glyph in the next drawable slot, using the position information located at index
185     // from.
push_back(SkGlyph * glyph,size_t from)186     void push_back(SkGlyph* glyph, size_t from) {
187         SkASSERT(fPhase == kProcess);
188         SkASSERT(fDrawableSize <= from);
189         fPositions[fDrawableSize] = fPositions[from];
190         fMultiBuffer[fDrawableSize] = glyph;
191         fDrawableSize++;
192     }
193 
194     // Store the path in the next drawable slot, using the position information located at index
195     // from.
push_back(const SkPath * path,size_t from)196     void push_back(const SkPath* path, size_t from) {
197         SkASSERT(fPhase == kProcess);
198         SkASSERT(fDrawableSize <= from);
199         fPositions[fDrawableSize] = fPositions[from];
200         fMultiBuffer[fDrawableSize] = path;
201         fDrawableSize++;
202     }
203 
204     // The result after a series of push_backs of drawable SkGlyph* or SkPath*.
drawable()205     SkZip<SkGlyphVariant, SkPoint> drawable() {
206         SkASSERT(fPhase == kProcess);
207         SkDEBUGCODE(fPhase = kDraw);
208         return SkZip<SkGlyphVariant, SkPoint>{fDrawableSize, fMultiBuffer.get(), fPositions};
209     }
210 
drawableIsEmpty()211     bool drawableIsEmpty() const {
212         SkASSERT(fPhase == kProcess || fPhase == kDraw);
213         return fDrawableSize == 0;
214     }
215 
216     void reset();
217 
218     template <typename Fn>
forEachGlyphID(Fn && fn)219     void forEachGlyphID(Fn&& fn) {
220         for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) {
221             fn(i, packedID.packedID(), pos);
222         }
223     }
224 
225 private:
226     size_t fMaxSize{0};
227     size_t fInputSize{0};
228     size_t fDrawableSize{0};
229     SkAutoTArray<SkGlyphVariant> fMultiBuffer;
230     SkAutoTMalloc<SkPoint> fPositions;
231 
232 #ifdef SK_DEBUG
233     enum {
234         kReset,
235         kInput,
236         kProcess,
237         kDraw
238     } fPhase{kReset};
239 #endif
240 };
241 #endif  // SkGlyphBuffer_DEFINED
242