• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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