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
15class SkStrikeForGPU;
16struct 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.
21class SkSourceGlyphBuffer {
22public:
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
65private:
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.
85class SkGlyphVariant {
86public:
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
122private:
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.
142class SkDrawableGlyphBuffer {
143public:
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
201private:
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