/src/skia/src/gpu/geometry/GrStyledShape.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2016 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 GrStyledShape_DEFINED |
9 | | #define GrStyledShape_DEFINED |
10 | | |
11 | | #include "include/core/SkPath.h" |
12 | | #include "include/core/SkRRect.h" |
13 | | #include "include/private/SkTemplates.h" |
14 | | #include "src/core/SkPathPriv.h" |
15 | | #include "src/core/SkTLazy.h" |
16 | | #include "src/gpu/GrStyle.h" |
17 | | #include "src/gpu/geometry/GrShape.h" |
18 | | #include <new> |
19 | | |
20 | | class SkIDChangeListener; |
21 | | |
22 | | /** |
23 | | * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. |
24 | | * It is possible to apply the style to the GrStyledShape to produce a new GrStyledShape where the |
25 | | * geometry reflects the styling information (e.g. is stroked). It is also possible to apply just |
26 | | * the path effect from the style. In this case the resulting shape will include any remaining |
27 | | * stroking information that is to be applied after the path effect. |
28 | | * |
29 | | * Shapes can produce keys that represent only the geometry information, not the style. Note that |
30 | | * when styling information is applied to produce a new shape then the style has been converted |
31 | | * to geometric information and is included in the new shape's key. When the same style is applied |
32 | | * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes |
33 | | * will be the same. |
34 | | * |
35 | | * Currently this can only be constructed from a path, rect, or rrect though it can become a path |
36 | | * applying style to the geometry. The idea is to expand this to cover most or all of the geometries |
37 | | * that have fast paths in the GPU backend. |
38 | | */ |
39 | | class GrStyledShape { |
40 | | public: |
41 | | // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed |
42 | | // to have to worry about this. This value is exposed for unit tests. |
43 | | static constexpr int kMaxKeyFromDataVerbCnt = 10; |
44 | | |
45 | 0 | GrStyledShape() {} |
46 | | |
47 | | enum class DoSimplify : bool { kNo = false, kYes }; |
48 | | |
49 | | explicit GrStyledShape(const SkPath& path, DoSimplify doSimplify = DoSimplify::kYes) |
50 | 0 | : GrStyledShape(path, GrStyle::SimpleFill(), doSimplify) {} |
51 | | |
52 | | explicit GrStyledShape(const SkRRect& rrect, DoSimplify doSimplify = DoSimplify::kYes) |
53 | 0 | : GrStyledShape(rrect, GrStyle::SimpleFill(), doSimplify) {} |
54 | | |
55 | | explicit GrStyledShape(const SkRect& rect, DoSimplify doSimplify = DoSimplify::kYes) |
56 | 0 | : GrStyledShape(rect, GrStyle::SimpleFill(), doSimplify) {} |
57 | | |
58 | | GrStyledShape(const SkPath& path, const SkPaint& paint, |
59 | | DoSimplify doSimplify = DoSimplify::kYes) |
60 | 54.4k | : GrStyledShape(path, GrStyle(paint), doSimplify) {} |
61 | | |
62 | | GrStyledShape(const SkRRect& rrect, const SkPaint& paint, |
63 | | DoSimplify doSimplify = DoSimplify::kYes) |
64 | 0 | : GrStyledShape(rrect, GrStyle(paint), doSimplify) {} |
65 | | |
66 | | GrStyledShape(const SkRect& rect, const SkPaint& paint, |
67 | | DoSimplify doSimplify = DoSimplify::kYes) |
68 | 0 | : GrStyledShape(rect, GrStyle(paint), doSimplify) {} |
69 | | |
70 | | GrStyledShape(const SkPath& path, const GrStyle& style, |
71 | | DoSimplify doSimplify = DoSimplify::kYes) |
72 | 60.3k | : fShape(path), fStyle(style) { |
73 | 60.3k | if (doSimplify == DoSimplify::kYes) { |
74 | 59.5k | this->simplify(); |
75 | 59.5k | } |
76 | 60.3k | } |
77 | | |
78 | | GrStyledShape(const SkRRect& rrect, const GrStyle& style, |
79 | | DoSimplify doSimplify = DoSimplify::kYes) |
80 | 103 | : fShape(rrect), fStyle(style) { |
81 | 103 | if (doSimplify == DoSimplify::kYes) { |
82 | 5 | this->simplify(); |
83 | 5 | } |
84 | 103 | } |
85 | | |
86 | | GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, |
87 | | const GrStyle& style, DoSimplify doSimplify = DoSimplify::kYes) |
88 | | : fShape(rrect) |
89 | 2 | , fStyle(style) { |
90 | 2 | fShape.setPathWindingParams(dir, start); |
91 | 2 | fShape.setInverted(inverted); |
92 | 2 | if (doSimplify == DoSimplify::kYes) { |
93 | 0 | this->simplify(); |
94 | 0 | } |
95 | 2 | } |
96 | | |
97 | | GrStyledShape(const SkRect& rect, const GrStyle& style, |
98 | | DoSimplify doSimplify = DoSimplify::kYes) |
99 | 535 | : fShape(rect), fStyle(style) { |
100 | 535 | if (doSimplify == DoSimplify::kYes) { |
101 | 109 | this->simplify(); |
102 | 109 | } |
103 | 535 | } |
104 | | |
105 | | GrStyledShape(const GrStyledShape&); |
106 | | |
107 | | static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, |
108 | | SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style, |
109 | | DoSimplify = DoSimplify::kYes); |
110 | | |
111 | | GrStyledShape& operator=(const GrStyledShape& that); |
112 | | |
113 | | /** |
114 | | * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled |
115 | | * version of the shape. |
116 | | */ |
117 | | enum class FillInversion { |
118 | | kPreserve, |
119 | | kFlip, |
120 | | kForceNoninverted, |
121 | | kForceInverted |
122 | | }; |
123 | | /** |
124 | | * Makes a filled shape from the pre-styled original shape and optionally modifies whether |
125 | | * the fill is inverted or not. It's important to note that the original shape's geometry |
126 | | * may already have been modified if doing so was neutral with respect to its style |
127 | | * (e.g. filled paths are always closed when stored in a shape and dashed paths are always |
128 | | * made non-inverted since dashing ignores inverseness). |
129 | | */ |
130 | | static GrStyledShape MakeFilled(const GrStyledShape& original, |
131 | | FillInversion = FillInversion::kPreserve); |
132 | | |
133 | 500k | const GrStyle& style() const { return fStyle; } |
134 | | |
135 | | // True if the shape and/or style were modified into a simpler, equivalent pairing |
136 | 526 | bool simplified() const { return fSimplified; } |
137 | | |
138 | | /** |
139 | | * Returns a shape that has either applied the path effect or path effect and stroking |
140 | | * information from this shape's style to its geometry. Scale is used when approximating the |
141 | | * output geometry and typically is computed from the view matrix |
142 | | */ |
143 | 23.9k | GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { |
144 | 23.9k | return GrStyledShape(*this, apply, scale); |
145 | 23.9k | } |
146 | | |
147 | 0 | bool isRect() const { |
148 | 0 | // Should have simplified a rrect to a rect if possible already. |
149 | 0 | SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect()); |
150 | 0 | return fShape.isRect(); |
151 | 0 | } |
152 | | |
153 | | /** Returns the unstyled geometry as a rrect if possible. */ |
154 | | bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const; |
155 | | |
156 | | /** |
157 | | * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. |
158 | | * An inverse filled line path is still considered a line. |
159 | | */ |
160 | | bool asLine(SkPoint pts[2], bool* inverted) const; |
161 | | |
162 | | // Can this shape be drawn as a pair of filled nested rectangles? |
163 | | bool asNestedRects(SkRect rects[2]) const; |
164 | | |
165 | | /** Returns the unstyled geometry as a path. */ |
166 | 59.6k | void asPath(SkPath* out) const { |
167 | 59.6k | fShape.asPath(out, fStyle.isSimpleFill()); |
168 | 59.6k | } |
169 | | |
170 | | /** |
171 | | * Returns whether the geometry is empty. Note that applying the style could produce a |
172 | | * non-empty shape. It also may have an inverse fill. |
173 | | */ |
174 | 160k | bool isEmpty() const { return fShape.isEmpty(); } |
175 | | |
176 | | /** |
177 | | * Gets the bounds of the geometry without reflecting the shape's styling. This ignores |
178 | | * the inverse fill nature of the geometry. |
179 | | */ |
180 | 102k | SkRect bounds() const { return fShape.bounds(); } |
181 | | |
182 | | /** |
183 | | * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill |
184 | | * status). |
185 | | */ |
186 | | SkRect styledBounds() const; |
187 | | |
188 | | /** |
189 | | * Is this shape known to be convex, before styling is applied. An unclosed but otherwise |
190 | | * convex path is considered to be closed if they styling reflects a fill and not otherwise. |
191 | | * This is because filling closes all contours in the path. |
192 | | */ |
193 | 107k | bool knownToBeConvex() const { |
194 | 107k | return fShape.convex(fStyle.isSimpleFill()); |
195 | 107k | } |
196 | | |
197 | | /** |
198 | | * Does the shape have a known winding direction. Some degenerate convex shapes may not have |
199 | | * a computable direction, but this is not always a requirement for path renderers so it is |
200 | | * kept separate from knownToBeConvex(). |
201 | | */ |
202 | 1.24k | bool knownDirection() const { |
203 | | // Assuming this is called after knownToBeConvex(), this should just be relying on |
204 | | // cached convexity and direction and will be cheap. |
205 | 1.24k | return !fShape.isPath() || |
206 | 972 | SkPathPriv::ComputeFirstDirection(fShape.path()) != SkPathFirstDirection::kUnknown; |
207 | 1.24k | } |
208 | | |
209 | | /** Is the pre-styled geometry inverse filled? */ |
210 | 115k | bool inverseFilled() const { |
211 | | // Since the path tracks inverted-fillness itself, it should match what was recorded. |
212 | 115k | SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType()); |
213 | | // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 |
214 | 115k | SkASSERT(!(fShape.inverted() && this->style().isDashed())); |
215 | 115k | return fShape.inverted(); |
216 | 115k | } |
217 | | |
218 | | /** |
219 | | * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in |
220 | | * because an arbitrary path effect could produce an inverse filled path. In other cases this |
221 | | * can be thought of as "inverseFilledAfterStyling()". |
222 | | */ |
223 | 0 | bool mayBeInverseFilledAfterStyling() const { |
224 | 0 | // An arbitrary path effect can produce an arbitrary output path, which may be inverse |
225 | 0 | // filled. |
226 | 0 | if (this->style().hasNonDashPathEffect()) { |
227 | 0 | return true; |
228 | 0 | } |
229 | 0 | return this->inverseFilled(); |
230 | 0 | } |
231 | | |
232 | | /** |
233 | | * Is it known that the unstyled geometry has no unclosed contours. This means that it will |
234 | | * not have any caps if stroked (modulo the effect of any path effect). |
235 | | */ |
236 | 1.06k | bool knownToBeClosed() const { |
237 | | // This refers to the base shape and does not depend on invertedness. |
238 | 1.06k | return fShape.closed(); |
239 | 1.06k | } |
240 | | |
241 | 3.64k | uint32_t segmentMask() const { |
242 | | // This refers to the base shape and does not depend on invertedness. |
243 | 3.64k | return fShape.segmentMask(); |
244 | 3.64k | } |
245 | | |
246 | | /** |
247 | | * Gets the size of the key for the shape represented by this GrStyledShape (ignoring its |
248 | | * styling). A negative value is returned if the shape has no key (shouldn't be cached). |
249 | | */ |
250 | | int unstyledKeySize() const; |
251 | | |
252 | 62.4k | bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } |
253 | | |
254 | | /** |
255 | | * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough |
256 | | * space allocated for the key and that unstyledKeySize() does not return a negative value |
257 | | * for this shape. |
258 | | */ |
259 | | void writeUnstyledKey(uint32_t* key) const; |
260 | | |
261 | | /** |
262 | | * Adds a listener to the *original* path. Typically used to invalidate cached resources when |
263 | | * a path is no longer in-use. If the shape started out as something other than a path, this |
264 | | * does nothing. |
265 | | */ |
266 | | void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const; |
267 | | |
268 | | /** |
269 | | * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get |
270 | | * the generation ID of the *original* path. This is the path that will receive |
271 | | * GenIDChangeListeners added to this shape. |
272 | | */ |
273 | | uint32_t testingOnly_getOriginalGenerationID() const; |
274 | | bool testingOnly_isPath() const; |
275 | | bool testingOnly_isNonVolatilePath() const; |
276 | | |
277 | | /** |
278 | | * Similar to GrShape::simplify but also takes into account style and stroking, possibly |
279 | | * applying the style explicitly to produce a new analytic shape with a simpler style. |
280 | | * Unless "doSimplify" is kNo, this method gets called automatically during construction. |
281 | | */ |
282 | | void simplify(); |
283 | | |
284 | | private: |
285 | | /** Constructor used by the applyStyle() function */ |
286 | | GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); |
287 | | |
288 | | /** |
289 | | * Determines the key we should inherit from the input shape's geometry and style when |
290 | | * we are applying the style to create a new shape. |
291 | | */ |
292 | | void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); |
293 | | |
294 | | /** |
295 | | * As part of the simplification process, some shapes can have stroking trivially evaluated |
296 | | * and form a new geometry with just a fill. |
297 | | */ |
298 | | void simplifyStroke(); |
299 | | |
300 | | /** Gets the path that gen id listeners should be added to. */ |
301 | | const SkPath* originalPathForListeners() const; |
302 | | |
303 | | GrShape fShape; |
304 | | GrStyle fStyle; |
305 | | // Gen ID of the original path (path may be modified or simplified away). |
306 | | int32_t fGenID = 0; |
307 | | bool fClosed = false; |
308 | | bool fSimplified = false; |
309 | | |
310 | | SkTLazy<SkPath> fInheritedPathForListeners; |
311 | | SkAutoSTArray<8, uint32_t> fInheritedKey; |
312 | | }; |
313 | | #endif |