1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Shape.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathBuilder.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRRectPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker
19*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Worker namespace {
22*c8dee2aaSAndroid Build Coastguard Worker // Keys for paths may be extracted from the path data for small paths, to maximize matches
23*c8dee2aaSAndroid Build Coastguard Worker // even when the genIDs may differ. The value is based on emperical experience, to trade off
24*c8dee2aaSAndroid Build Coastguard Worker // matches vs. key size.
25*c8dee2aaSAndroid Build Coastguard Worker constexpr int kMaxKeyFromDataVerbCnt = 10;
26*c8dee2aaSAndroid Build Coastguard Worker }
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
29*c8dee2aaSAndroid Build Coastguard Worker
operator =(const Shape & shape)30*c8dee2aaSAndroid Build Coastguard Worker Shape& Shape::operator=(const Shape& shape) {
31*c8dee2aaSAndroid Build Coastguard Worker switch (shape.type()) {
32*c8dee2aaSAndroid Build Coastguard Worker case Type::kEmpty: this->reset(); break;
33*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: this->setLine(shape.p0(), shape.p1()); break;
34*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect: this->setRect(shape.rect()); break;
35*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect: this->setRRect(shape.rrect()); break;
36*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: this->setArc(shape.arc()); break;
37*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: this->setPath(shape.path()); break;
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker
40*c8dee2aaSAndroid Build Coastguard Worker fInverted = shape.fInverted;
41*c8dee2aaSAndroid Build Coastguard Worker return *this;
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker
conservativeContains(const Rect & rect) const44*c8dee2aaSAndroid Build Coastguard Worker bool Shape::conservativeContains(const Rect& rect) const {
45*c8dee2aaSAndroid Build Coastguard Worker switch (fType) {
46*c8dee2aaSAndroid Build Coastguard Worker case Type::kEmpty: return false;
47*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: return false;
48*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect: return fRect.contains(rect);
49*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect: return fRRect.contains(rect.asSkRect());
50*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: // We need to ensure the path is non-inverted.
51*c8dee2aaSAndroid Build Coastguard Worker if (this->inverted()) {
52*c8dee2aaSAndroid Build Coastguard Worker SkPath nonInverted(fPath);
53*c8dee2aaSAndroid Build Coastguard Worker nonInverted.toggleInverseFillType();
54*c8dee2aaSAndroid Build Coastguard Worker return nonInverted.conservativelyContainsRect(rect.asSkRect());
55*c8dee2aaSAndroid Build Coastguard Worker } else {
56*c8dee2aaSAndroid Build Coastguard Worker return fPath.conservativelyContainsRect(rect.asSkRect());
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: if (fArc.fType == SkArc::Type::kWedge) {
59*c8dee2aaSAndroid Build Coastguard Worker SkPath arc = this->asPath();
60*c8dee2aaSAndroid Build Coastguard Worker if (this->inverted()) {
61*c8dee2aaSAndroid Build Coastguard Worker arc.toggleInverseFillType();
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker return arc.conservativelyContainsRect(rect.asSkRect());
64*c8dee2aaSAndroid Build Coastguard Worker } else {
65*c8dee2aaSAndroid Build Coastguard Worker return false;
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker
conservativeContains(skvx::float2 point) const71*c8dee2aaSAndroid Build Coastguard Worker bool Shape::conservativeContains(skvx::float2 point) const {
72*c8dee2aaSAndroid Build Coastguard Worker switch (fType) {
73*c8dee2aaSAndroid Build Coastguard Worker case Type::kEmpty: return false;
74*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: return false;
75*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect: return fRect.contains(Rect::Point(point));
76*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect: return SkRRectPriv::ContainsPoint(fRRect, {point.x(), point.y()});
77*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: // We need to ensure the path is non-inverted.
78*c8dee2aaSAndroid Build Coastguard Worker if (this->inverted()) {
79*c8dee2aaSAndroid Build Coastguard Worker SkPath nonInverted(fPath);
80*c8dee2aaSAndroid Build Coastguard Worker nonInverted.toggleInverseFillType();
81*c8dee2aaSAndroid Build Coastguard Worker return nonInverted.contains(point.x(), point.y());
82*c8dee2aaSAndroid Build Coastguard Worker } else {
83*c8dee2aaSAndroid Build Coastguard Worker return fPath.contains(point.x(), point.y());
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: return false;
86*c8dee2aaSAndroid Build Coastguard Worker }
87*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker
convex(bool simpleFill) const90*c8dee2aaSAndroid Build Coastguard Worker bool Shape::convex(bool simpleFill) const {
91*c8dee2aaSAndroid Build Coastguard Worker if (this->isPath()) {
92*c8dee2aaSAndroid Build Coastguard Worker // SkPath.isConvex() really means "is this path convex were it to be closed".
93*c8dee2aaSAndroid Build Coastguard Worker return (simpleFill || fPath.isLastContourClosed()) && fPath.isConvex();
94*c8dee2aaSAndroid Build Coastguard Worker } else if (this->isArc()) {
95*c8dee2aaSAndroid Build Coastguard Worker return SkPathPriv::DrawArcIsConvex(fArc.sweepAngle(), fArc.fType, simpleFill);
96*c8dee2aaSAndroid Build Coastguard Worker } else {
97*c8dee2aaSAndroid Build Coastguard Worker // Every other shape type is convex by construction.
98*c8dee2aaSAndroid Build Coastguard Worker return true;
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker }
101*c8dee2aaSAndroid Build Coastguard Worker
bounds() const102*c8dee2aaSAndroid Build Coastguard Worker Rect Shape::bounds() const {
103*c8dee2aaSAndroid Build Coastguard Worker switch (fType) {
104*c8dee2aaSAndroid Build Coastguard Worker case Type::kEmpty: return Rect(0, 0, 0, 0);
105*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: return fRect.makeSorted(); // sorting corners computes bbox of segment
106*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect: return fRect; // assuming it's sorted
107*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect: return fRRect.getBounds();
108*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: return fArc.oval();
109*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: return fPath.getBounds();
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker
asPath() const114*c8dee2aaSAndroid Build Coastguard Worker SkPath Shape::asPath() const {
115*c8dee2aaSAndroid Build Coastguard Worker if (fType == Type::kPath) {
116*c8dee2aaSAndroid Build Coastguard Worker return fPath;
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker if (fType == Type::kArc) {
120*c8dee2aaSAndroid Build Coastguard Worker SkPath out;
121*c8dee2aaSAndroid Build Coastguard Worker // Filled ovals are already culled out so we assume no simple fills
122*c8dee2aaSAndroid Build Coastguard Worker SkPathPriv::CreateDrawArcPath(&out, fArc, /*isFillNoPathEffect=*/false);
123*c8dee2aaSAndroid Build Coastguard Worker // CreateDrawArcPath resets the output path and configures its fill
124*c8dee2aaSAndroid Build Coastguard Worker // type, so we just have to ensure invertedness is correct.
125*c8dee2aaSAndroid Build Coastguard Worker if (fInverted) {
126*c8dee2aaSAndroid Build Coastguard Worker out.toggleInverseFillType();
127*c8dee2aaSAndroid Build Coastguard Worker }
128*c8dee2aaSAndroid Build Coastguard Worker return out;
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker
131*c8dee2aaSAndroid Build Coastguard Worker SkPathBuilder builder(this->fillType());
132*c8dee2aaSAndroid Build Coastguard Worker switch (fType) {
133*c8dee2aaSAndroid Build Coastguard Worker case Type::kEmpty: /* do nothing */ break;
134*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: builder.moveTo(fRect.left(), fRect.top())
135*c8dee2aaSAndroid Build Coastguard Worker .lineTo(fRect.right(), fRect.bot()); break;
136*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect: builder.addRect(fRect.asSkRect()); break;
137*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect: builder.addRRect(fRRect); break;
138*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath:
139*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: SkUNREACHABLE;
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker return builder.detach();
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker namespace {
path_key_from_data_size(const SkPath & path)145*c8dee2aaSAndroid Build Coastguard Worker int path_key_from_data_size(const SkPath& path) {
146*c8dee2aaSAndroid Build Coastguard Worker const int verbCnt = path.countVerbs();
147*c8dee2aaSAndroid Build Coastguard Worker if (verbCnt > kMaxKeyFromDataVerbCnt) {
148*c8dee2aaSAndroid Build Coastguard Worker return -1;
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker const int pointCnt = path.countPoints();
151*c8dee2aaSAndroid Build Coastguard Worker const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
152*c8dee2aaSAndroid Build Coastguard Worker
153*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
154*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkScalar) == sizeof(uint32_t));
155*c8dee2aaSAndroid Build Coastguard Worker // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to
156*c8dee2aaSAndroid Build Coastguard Worker // a uint32_t length.
157*c8dee2aaSAndroid Build Coastguard Worker return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker
160*c8dee2aaSAndroid Build Coastguard Worker // Writes the path data key into the passed pointer.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)161*c8dee2aaSAndroid Build Coastguard Worker void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
162*c8dee2aaSAndroid Build Coastguard Worker uint32_t* key = origKey;
163*c8dee2aaSAndroid Build Coastguard Worker // The check below should take care of negative values casted positive.
164*c8dee2aaSAndroid Build Coastguard Worker const int verbCnt = path.countVerbs();
165*c8dee2aaSAndroid Build Coastguard Worker const int pointCnt = path.countPoints();
166*c8dee2aaSAndroid Build Coastguard Worker const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
167*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(verbCnt <= kMaxKeyFromDataVerbCnt);
168*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(pointCnt && verbCnt);
169*c8dee2aaSAndroid Build Coastguard Worker *key++ = verbCnt;
170*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
171*c8dee2aaSAndroid Build Coastguard Worker int verbKeySize = SkAlign4(verbCnt);
172*c8dee2aaSAndroid Build Coastguard Worker // pad out to uint32_t alignment using value that will stand out when debugging.
173*c8dee2aaSAndroid Build Coastguard Worker uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
174*c8dee2aaSAndroid Build Coastguard Worker memset(pad, 0xDE, verbKeySize - verbCnt);
175*c8dee2aaSAndroid Build Coastguard Worker key += verbKeySize >> 2;
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
178*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
179*c8dee2aaSAndroid Build Coastguard Worker key += 2 * pointCnt;
180*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
181*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkScalar) == sizeof(uint32_t));
182*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(key += conicWeightCnt);
183*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(key - origKey == path_key_from_data_size(path));
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
186*c8dee2aaSAndroid Build Coastguard Worker
keySize() const187*c8dee2aaSAndroid Build Coastguard Worker int Shape::keySize() const {
188*c8dee2aaSAndroid Build Coastguard Worker int count = 1; // Every key has the state flags from the Shape
189*c8dee2aaSAndroid Build Coastguard Worker switch(this->type()) {
190*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine:
191*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(skvx::float4) % sizeof(uint32_t));
192*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(skvx::float4) / sizeof(uint32_t);
193*c8dee2aaSAndroid Build Coastguard Worker break;
194*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect:
195*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(Rect) % sizeof(uint32_t));
196*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(Rect) / sizeof(uint32_t);
197*c8dee2aaSAndroid Build Coastguard Worker break;
198*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect:
199*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
200*c8dee2aaSAndroid Build Coastguard Worker count += SkRRect::kSizeInMemory / sizeof(uint32_t);
201*c8dee2aaSAndroid Build Coastguard Worker break;
202*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc:
203*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(SkArc) % sizeof(uint32_t));
204*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(SkArc) / sizeof(uint32_t);
205*c8dee2aaSAndroid Build Coastguard Worker break;
206*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: {
207*c8dee2aaSAndroid Build Coastguard Worker if (this->path().isVolatile()) {
208*c8dee2aaSAndroid Build Coastguard Worker return -1; // volatile, so won't be keyed
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker if (this->path().isEmpty()) {
211*c8dee2aaSAndroid Build Coastguard Worker return -1; // empty, so won't be keyed
212*c8dee2aaSAndroid Build Coastguard Worker }
213*c8dee2aaSAndroid Build Coastguard Worker int dataKeySize = path_key_from_data_size(this->path());
214*c8dee2aaSAndroid Build Coastguard Worker if (dataKeySize >= 0) {
215*c8dee2aaSAndroid Build Coastguard Worker count += dataKeySize;
216*c8dee2aaSAndroid Build Coastguard Worker } else {
217*c8dee2aaSAndroid Build Coastguard Worker count++; // Just adds the gen ID.
218*c8dee2aaSAndroid Build Coastguard Worker }
219*c8dee2aaSAndroid Build Coastguard Worker break;
220*c8dee2aaSAndroid Build Coastguard Worker }
221*c8dee2aaSAndroid Build Coastguard Worker default:
222*c8dee2aaSAndroid Build Coastguard Worker // else it's empty, which just needs the state flags for its key
223*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->isEmpty());
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker return count;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker
writeKey(uint32_t * key,bool includeInverted) const228*c8dee2aaSAndroid Build Coastguard Worker void Shape::writeKey(uint32_t* key, bool includeInverted) const {
229*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->keySize());
230*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(uint32_t* origKey = key;)
231*c8dee2aaSAndroid Build Coastguard Worker
232*c8dee2aaSAndroid Build Coastguard Worker // Every key starts with the state from the Shape (this includes path fill type,
233*c8dee2aaSAndroid Build Coastguard Worker // and any tracked inversion, as well as the class of geometry).
234*c8dee2aaSAndroid Build Coastguard Worker *key++ = this->stateKey(includeInverted);
235*c8dee2aaSAndroid Build Coastguard Worker
236*c8dee2aaSAndroid Build Coastguard Worker switch(this->type()) {
237*c8dee2aaSAndroid Build Coastguard Worker case Type::kPath: {
238*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->path().isVolatile());
239*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->path().isEmpty());
240*c8dee2aaSAndroid Build Coastguard Worker // Ensure that the path's inversion matches our state so that the path's key suffices.
241*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->inverted() == this->path().isInverseFillType());
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker int dataKeySize = path_key_from_data_size(this->path());
244*c8dee2aaSAndroid Build Coastguard Worker if (dataKeySize >= 0) {
245*c8dee2aaSAndroid Build Coastguard Worker write_path_key_from_data(this->path(), key);
246*c8dee2aaSAndroid Build Coastguard Worker return;
247*c8dee2aaSAndroid Build Coastguard Worker } else {
248*c8dee2aaSAndroid Build Coastguard Worker *key++ = this->path().getGenerationID();
249*c8dee2aaSAndroid Build Coastguard Worker }
250*c8dee2aaSAndroid Build Coastguard Worker break;
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker case Type::kRect:
253*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &this->rect(), sizeof(Rect));
254*c8dee2aaSAndroid Build Coastguard Worker key += sizeof(Rect) / sizeof(uint32_t);
255*c8dee2aaSAndroid Build Coastguard Worker break;
256*c8dee2aaSAndroid Build Coastguard Worker case Type::kRRect:
257*c8dee2aaSAndroid Build Coastguard Worker this->rrect().writeToMemory(key);
258*c8dee2aaSAndroid Build Coastguard Worker key += SkRRect::kSizeInMemory / sizeof(uint32_t);
259*c8dee2aaSAndroid Build Coastguard Worker break;
260*c8dee2aaSAndroid Build Coastguard Worker case Type::kArc: {
261*c8dee2aaSAndroid Build Coastguard Worker // Write dense floats first
262*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &fArc, sizeof(SkRect) + 2 * sizeof(float));
263*c8dee2aaSAndroid Build Coastguard Worker key += (sizeof(SkArc) / sizeof(uint32_t) - 1);
264*c8dee2aaSAndroid Build Coastguard Worker // Then write the final bool as an int, to make sure upper bits are set
265*c8dee2aaSAndroid Build Coastguard Worker *key++ = fArc.isWedge() ? 1 : 0;
266*c8dee2aaSAndroid Build Coastguard Worker break;
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker case Type::kLine: {
269*c8dee2aaSAndroid Build Coastguard Worker skvx::float4 line = this->line();
270*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &line, sizeof(skvx::float4));
271*c8dee2aaSAndroid Build Coastguard Worker key += sizeof(skvx::float4) / sizeof(uint32_t);
272*c8dee2aaSAndroid Build Coastguard Worker break;
273*c8dee2aaSAndroid Build Coastguard Worker }
274*c8dee2aaSAndroid Build Coastguard Worker default:
275*c8dee2aaSAndroid Build Coastguard Worker // Nothing other than the flag state is needed in the key for an empty shape
276*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->isEmpty());
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(key - origKey == this->keySize());
279*c8dee2aaSAndroid Build Coastguard Worker }
280*c8dee2aaSAndroid Build Coastguard Worker
281*c8dee2aaSAndroid Build Coastguard Worker namespace {
noninverted_fill_type(SkPathFillType fillType)282*c8dee2aaSAndroid Build Coastguard Worker SkPathFillType noninverted_fill_type(SkPathFillType fillType) {
283*c8dee2aaSAndroid Build Coastguard Worker switch (fillType) {
284*c8dee2aaSAndroid Build Coastguard Worker case SkPathFillType::kWinding:
285*c8dee2aaSAndroid Build Coastguard Worker case SkPathFillType::kInverseWinding:
286*c8dee2aaSAndroid Build Coastguard Worker return SkPathFillType::kWinding;
287*c8dee2aaSAndroid Build Coastguard Worker case SkPathFillType::kEvenOdd:
288*c8dee2aaSAndroid Build Coastguard Worker case SkPathFillType::kInverseEvenOdd:
289*c8dee2aaSAndroid Build Coastguard Worker return SkPathFillType::kEvenOdd;
290*c8dee2aaSAndroid Build Coastguard Worker }
291*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
292*c8dee2aaSAndroid Build Coastguard Worker }
293*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
294*c8dee2aaSAndroid Build Coastguard Worker
stateKey(bool includeInverted) const295*c8dee2aaSAndroid Build Coastguard Worker uint32_t Shape::stateKey(bool includeInverted) const {
296*c8dee2aaSAndroid Build Coastguard Worker uint32_t key;
297*c8dee2aaSAndroid Build Coastguard Worker if (includeInverted) {
298*c8dee2aaSAndroid Build Coastguard Worker // Use the path's full fill type instead of just whether or not it's inverted.
299*c8dee2aaSAndroid Build Coastguard Worker key = this->isPath() ? static_cast<uint32_t>(fPath.getFillType())
300*c8dee2aaSAndroid Build Coastguard Worker : (fInverted ? 1 : 0);
301*c8dee2aaSAndroid Build Coastguard Worker } else {
302*c8dee2aaSAndroid Build Coastguard Worker // Use the path's noninverted fill type.
303*c8dee2aaSAndroid Build Coastguard Worker key = this->isPath() ? static_cast<uint32_t>(noninverted_fill_type(fPath.getFillType()))
304*c8dee2aaSAndroid Build Coastguard Worker : 0;
305*c8dee2aaSAndroid Build Coastguard Worker }
306*c8dee2aaSAndroid Build Coastguard Worker key |= ((uint32_t) fType) << 2; // fill type was 2 bits
307*c8dee2aaSAndroid Build Coastguard Worker return key;
308*c8dee2aaSAndroid Build Coastguard Worker }
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
311