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