1 /*
2 * Copyright 2021 Google LLC
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 #include "src/gpu/graphite/geom/Shape.h"
9
10 #include "include/private/base/SkAlign.h"
11 #include "src/core/SkPathPriv.h"
12 #include "src/core/SkRRectPriv.h"
13 #include "src/utils/SkPolyUtils.h"
14
15 namespace {
16 // Keys for paths may be extracted from the path data for small paths, to maximize matches
17 // even when the genIDs may differ. The value is based on emperical experience, to trade off
18 // matches vs. key size.
19 constexpr int kMaxKeyFromDataVerbCnt = 10;
20 }
21
22 namespace skgpu::graphite {
23
operator =(const Shape & shape)24 Shape& Shape::operator=(const Shape& shape) {
25 switch (shape.type()) {
26 case Type::kEmpty: this->reset(); break;
27 case Type::kLine: this->setLine(shape.p0(), shape.p1()); break;
28 case Type::kRect: this->setRect(shape.rect()); break;
29 case Type::kRRect: this->setRRect(shape.rrect()); break;
30 case Type::kPath: this->setPath(shape.path()); break;
31 }
32
33 fInverted = shape.fInverted;
34 return *this;
35 }
36
conservativeContains(const Rect & rect) const37 bool Shape::conservativeContains(const Rect& rect) const {
38 switch (fType) {
39 case Type::kEmpty: return false;
40 case Type::kLine: return false;
41 case Type::kRect: return fRect.contains(rect);
42 case Type::kRRect: return fRRect.contains(rect.asSkRect());
43 case Type::kPath: return fPath.conservativelyContainsRect(rect.asSkRect());
44 }
45 SkUNREACHABLE;
46 }
47
conservativeContains(skvx::float2 point) const48 bool Shape::conservativeContains(skvx::float2 point) const {
49 switch (fType) {
50 case Type::kEmpty: return false;
51 case Type::kLine: return false;
52 case Type::kRect: return fRect.contains(Rect::Point(point));
53 case Type::kRRect: return SkRRectPriv::ContainsPoint(fRRect, {point.x(), point.y()});
54 case Type::kPath: return fPath.contains(point.x(), point.y());
55 }
56 SkUNREACHABLE;
57 }
58
convex(bool simpleFill) const59 bool Shape::convex(bool simpleFill) const {
60 if (this->isPath()) {
61 // SkPath.isConvex() really means "is this path convex were it to be closed".
62 return (simpleFill || fPath.isLastContourClosed()) && fPath.isConvex();
63 } else {
64 // Every other shape type is convex by construction.
65 return true;
66 }
67 }
68
bounds() const69 Rect Shape::bounds() const {
70 switch (fType) {
71 case Type::kEmpty: return Rect(0, 0, 0, 0);
72 case Type::kLine: return fRect.makeSorted(); // sorting corners computes bbox of segment
73 case Type::kRect: return fRect; // assuming it's sorted
74 case Type::kRRect: return fRRect.getBounds();
75 case Type::kPath: return fPath.getBounds();
76 }
77 SkUNREACHABLE;
78 }
79
asPath() const80 SkPath Shape::asPath() const {
81 if (fType == Type::kPath) {
82 return fPath;
83 }
84
85 SkPathBuilder builder(this->fillType());
86 switch (fType) {
87 case Type::kEmpty: /* do nothing */ break;
88 case Type::kLine: builder.moveTo(fRect.left(), fRect.top())
89 .lineTo(fRect.right(), fRect.bot()); break;
90 case Type::kRect: builder.addRect(fRect.asSkRect()); break;
91 case Type::kRRect: builder.addRRect(fRRect); break;
92 case Type::kPath: SkUNREACHABLE;
93 }
94 return builder.detach();
95 }
96
97 namespace {
path_key_from_data_size(const SkPath & path)98 int path_key_from_data_size(const SkPath& path) {
99 const int verbCnt = path.countVerbs();
100 if (verbCnt > kMaxKeyFromDataVerbCnt) {
101 return -1;
102 }
103 const int pointCnt = path.countPoints();
104 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
105
106 static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
107 static_assert(sizeof(SkScalar) == sizeof(uint32_t));
108 // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to
109 // a uint32_t length.
110 return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
111 }
112
113 // Writes the path data key into the passed pointer.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)114 void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
115 uint32_t* key = origKey;
116 // The check below should take care of negative values casted positive.
117 const int verbCnt = path.countVerbs();
118 const int pointCnt = path.countPoints();
119 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
120 SkASSERT(verbCnt <= kMaxKeyFromDataVerbCnt);
121 SkASSERT(pointCnt && verbCnt);
122 *key++ = verbCnt;
123 memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
124 int verbKeySize = SkAlign4(verbCnt);
125 // pad out to uint32_t alignment using value that will stand out when debugging.
126 uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
127 memset(pad, 0xDE, verbKeySize - verbCnt);
128 key += verbKeySize >> 2;
129
130 memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
131 static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
132 key += 2 * pointCnt;
133 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
134 static_assert(sizeof(SkScalar) == sizeof(uint32_t));
135 SkDEBUGCODE(key += conicWeightCnt);
136 SkASSERT(key - origKey == path_key_from_data_size(path));
137 }
138 } // anonymous namespace
139
keySize() const140 int Shape::keySize() const {
141 int count = 1; // Every key has the state flags from the Shape
142 switch(this->type()) {
143 case Type::kLine:
144 static_assert(0 == sizeof(skvx::float4) % sizeof(uint32_t));
145 count += sizeof(skvx::float4) / sizeof(uint32_t);
146 break;
147 case Type::kRect:
148 static_assert(0 == sizeof(Rect) % sizeof(uint32_t));
149 count += sizeof(Rect) / sizeof(uint32_t);
150 break;
151 case Type::kRRect:
152 static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
153 count += SkRRect::kSizeInMemory / sizeof(uint32_t);
154 break;
155 case Type::kPath: {
156 if (this->path().isVolatile()) {
157 return -1; // volatile, so won't be keyed
158 }
159 if (this->path().isEmpty()) {
160 return -1; // empty, so won't be keyed
161 }
162 int dataKeySize = path_key_from_data_size(this->path());
163 if (dataKeySize >= 0) {
164 count += dataKeySize;
165 } else {
166 count++; // Just adds the gen ID.
167 }
168 break;
169 }
170 default:
171 // else it's empty, which just needs the state flags for its key
172 SkASSERT(this->isEmpty());
173 }
174 return count;
175 }
176
writeKey(uint32_t * key,bool includeInverted) const177 void Shape::writeKey(uint32_t* key, bool includeInverted) const {
178 SkASSERT(this->keySize());
179 SkDEBUGCODE(uint32_t* origKey = key;)
180
181 // Every key starts with the state from the Shape (this includes path fill type,
182 // and any tracked inversion, as well as the class of geometry).
183 *key++ = this->stateKey(includeInverted);
184
185 switch(this->type()) {
186 case Type::kPath: {
187 SkASSERT(!this->path().isVolatile());
188 SkASSERT(!this->path().isEmpty());
189 // Ensure that the path's inversion matches our state so that the path's key suffices.
190 SkASSERT(this->inverted() == this->path().isInverseFillType());
191
192 int dataKeySize = path_key_from_data_size(this->path());
193 if (dataKeySize >= 0) {
194 write_path_key_from_data(this->path(), key);
195 return;
196 } else {
197 *key++ = this->path().getGenerationID();
198 }
199 break;
200 }
201 case Type::kRect:
202 memcpy(key, &this->rect(), sizeof(Rect));
203 key += sizeof(Rect) / sizeof(uint32_t);
204 break;
205 case Type::kRRect:
206 this->rrect().writeToMemory(key);
207 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
208 break;
209 case Type::kLine: {
210 skvx::float4 line = this->line();
211 memcpy(key, &line, sizeof(skvx::float4));
212 key += sizeof(skvx::float4) / sizeof(uint32_t);
213 break;
214 }
215 default:
216 // Nothing other than the flag state is needed in the key for an empty shape
217 SkASSERT(this->isEmpty());
218 }
219 SkASSERT(key - origKey == this->keySize());
220 }
221
222 namespace {
noninverted_fill_type(SkPathFillType fillType)223 SkPathFillType noninverted_fill_type(SkPathFillType fillType) {
224 switch (fillType) {
225 case SkPathFillType::kWinding:
226 case SkPathFillType::kInverseWinding:
227 return SkPathFillType::kWinding;
228 case SkPathFillType::kEvenOdd:
229 case SkPathFillType::kInverseEvenOdd:
230 return SkPathFillType::kEvenOdd;
231 }
232 SkUNREACHABLE;
233 }
234 } // anonymous namespace
235
stateKey(bool includeInverted) const236 uint32_t Shape::stateKey(bool includeInverted) const {
237 uint32_t key;
238 if (includeInverted) {
239 // Use the path's full fill type instead of just whether or not it's inverted.
240 key = this->isPath() ? static_cast<uint32_t>(fPath.getFillType())
241 : (fInverted ? 1 : 0);
242 } else {
243 // Use the path's noninverted fill type.
244 key = this->isPath() ? static_cast<uint32_t>(noninverted_fill_type(fPath.getFillType()))
245 : 0;
246 }
247 key |= ((uint32_t) fType) << 2; // fill type was 2 bits
248 return key;
249 }
250
251 } // namespace skgpu::graphite
252