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 | GrStyledShape() {} |
46 | |
47 | explicit GrStyledShape(const SkPath& path) : GrStyledShape(path, GrStyle::SimpleFill()) {} |
48 | |
49 | explicit GrStyledShape(const SkRRect& rrect) : GrStyledShape(rrect, GrStyle::SimpleFill()) {} |
50 | |
51 | explicit GrStyledShape(const SkRect& rect) : GrStyledShape(rect, GrStyle::SimpleFill()) {} |
52 | |
53 | GrStyledShape(const SkPath& path, const SkPaint& paint) : GrStyledShape(path, GrStyle(paint)) {} |
54 | |
55 | GrStyledShape(const SkRRect& rrect, const SkPaint& paint) |
56 | : GrStyledShape(rrect, GrStyle(paint)) {} |
57 | |
58 | GrStyledShape(const SkRect& rect, const SkPaint& paint) : GrStyledShape(rect, GrStyle(paint)) {} |
59 | |
60 | GrStyledShape(const SkPath& path, const GrStyle& style) : fShape(path), fStyle(style) { |
61 | this->simplify(); |
62 | } |
63 | |
64 | GrStyledShape(const SkRRect& rrect, const GrStyle& style) : fShape(rrect), fStyle(style) { |
65 | this->simplify(); |
66 | } |
67 | |
68 | GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, |
69 | const GrStyle& style) |
70 | : fShape(rrect) |
71 | , fStyle(style) { |
72 | fShape.setPathWindingParams(dir, start); |
73 | fShape.setInverted(inverted); |
74 | this->simplify(); |
75 | } |
76 | |
77 | GrStyledShape(const SkRect& rect, const GrStyle& style) : fShape(rect), fStyle(style) { |
78 | this->simplify(); |
79 | } |
80 | |
81 | GrStyledShape(const GrStyledShape&); |
82 | |
83 | static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, |
84 | SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style); |
85 | |
86 | GrStyledShape& operator=(const GrStyledShape& that); |
87 | |
88 | /** |
89 | * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled |
90 | * version of the shape. |
91 | */ |
92 | enum class FillInversion { |
93 | kPreserve, |
94 | kFlip, |
95 | kForceNoninverted, |
96 | kForceInverted |
97 | }; |
98 | /** |
99 | * Makes a filled shape from the pre-styled original shape and optionally modifies whether |
100 | * the fill is inverted or not. It's important to note that the original shape's geometry |
101 | * may already have been modified if doing so was neutral with respect to its style |
102 | * (e.g. filled paths are always closed when stored in a shape and dashed paths are always |
103 | * made non-inverted since dashing ignores inverseness). |
104 | */ |
105 | static GrStyledShape MakeFilled(const GrStyledShape& original, |
106 | FillInversion = FillInversion::kPreserve); |
107 | |
108 | const GrStyle& style() const { return fStyle; } |
109 | |
110 | // True if the shape and/or style were modified into a simpler, equivalent pairing |
111 | bool simplified() const { return fSimplified; } |
112 | |
113 | /** |
114 | * Returns a shape that has either applied the path effect or path effect and stroking |
115 | * information from this shape's style to its geometry. Scale is used when approximating the |
116 | * output geometry and typically is computed from the view matrix |
117 | */ |
118 | GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { |
119 | return GrStyledShape(*this, apply, scale); |
120 | } |
121 | |
122 | bool isRect() const { |
123 | // Should have simplified a rrect to a rect if possible already. |
124 | SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect()); |
125 | return fShape.isRect(); |
126 | } |
127 | |
128 | /** Returns the unstyled geometry as a rrect if possible. */ |
129 | bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const; |
130 | |
131 | /** |
132 | * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. |
133 | * An inverse filled line path is still considered a line. |
134 | */ |
135 | bool asLine(SkPoint pts[2], bool* inverted) const; |
136 | |
137 | // Can this shape be drawn as a pair of filled nested rectangles? |
138 | bool asNestedRects(SkRect rects[2]) const; |
139 | |
140 | /** Returns the unstyled geometry as a path. */ |
141 | void asPath(SkPath* out) const { |
142 | fShape.asPath(out, fStyle.isSimpleFill()); |
143 | } |
144 | |
145 | /** |
146 | * Returns whether the geometry is empty. Note that applying the style could produce a |
147 | * non-empty shape. It also may have an inverse fill. |
148 | */ |
149 | bool isEmpty() const { return fShape.isEmpty(); } |
150 | |
151 | /** |
152 | * Gets the bounds of the geometry without reflecting the shape's styling. This ignores |
153 | * the inverse fill nature of the geometry. |
154 | */ |
155 | SkRect bounds() const { return fShape.bounds(); } |
156 | |
157 | /** |
158 | * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill |
159 | * status). |
160 | */ |
161 | SkRect styledBounds() const; |
162 | |
163 | /** |
164 | * Is this shape known to be convex, before styling is applied. An unclosed but otherwise |
165 | * convex path is considered to be closed if they styling reflects a fill and not otherwise. |
166 | * This is because filling closes all contours in the path. |
167 | */ |
168 | bool knownToBeConvex() const { |
169 | return fShape.convex(fStyle.isSimpleFill()); |
170 | } |
171 | |
172 | /** |
173 | * Does the shape have a known winding direction. Some degenerate convex shapes may not have |
174 | * a computable direction, but this is not always a requirement for path renderers so it is |
175 | * kept separate from knownToBeConvex(). |
176 | */ |
177 | bool knownDirection() const { |
178 | // Assuming this is called after knownToBeConvex(), this should just be relying on |
179 | // cached convexity and direction and will be cheap. |
180 | return !fShape.isPath() || |
181 | !SkPathPriv::CheapIsFirstDirection(fShape.path(), |
182 | SkPathPriv::kUnknown_FirstDirection); |
183 | } |
184 | |
185 | /** Is the pre-styled geometry inverse filled? */ |
186 | bool inverseFilled() const { |
187 | // Since the path tracks inverted-fillness itself, it should match what was recorded. |
188 | SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType()); |
189 | // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 |
190 | SkASSERT(!(fShape.inverted() && this->style().isDashed())); |
191 | return fShape.inverted(); |
192 | } |
193 | |
194 | /** |
195 | * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in |
196 | * because an arbitrary path effect could produce an inverse filled path. In other cases this |
197 | * can be thought of as "inverseFilledAfterStyling()". |
198 | */ |
199 | bool mayBeInverseFilledAfterStyling() const { |
200 | // An arbitrary path effect can produce an arbitrary output path, which may be inverse |
201 | // filled. |
202 | if (this->style().hasNonDashPathEffect()) { |
203 | return true; |
204 | } |
205 | return this->inverseFilled(); |
206 | } |
207 | |
208 | /** |
209 | * Is it known that the unstyled geometry has no unclosed contours. This means that it will |
210 | * not have any caps if stroked (modulo the effect of any path effect). |
211 | */ |
212 | bool knownToBeClosed() const { |
213 | // This refers to the base shape and does not depend on invertedness. |
214 | return fShape.closed(); |
215 | } |
216 | |
217 | uint32_t segmentMask() const { |
218 | // This refers to the base shape and does not depend on invertedness. |
219 | return fShape.segmentMask(); |
220 | } |
221 | |
222 | /** |
223 | * Gets the size of the key for the shape represented by this GrStyledShape (ignoring its |
224 | * styling). A negative value is returned if the shape has no key (shouldn't be cached). |
225 | */ |
226 | int unstyledKeySize() const; |
227 | |
228 | bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } |
229 | |
230 | /** |
231 | * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough |
232 | * space allocated for the key and that unstyledKeySize() does not return a negative value |
233 | * for this shape. |
234 | */ |
235 | void writeUnstyledKey(uint32_t* key) const; |
236 | |
237 | /** |
238 | * Adds a listener to the *original* path. Typically used to invalidate cached resources when |
239 | * a path is no longer in-use. If the shape started out as something other than a path, this |
240 | * does nothing. |
241 | */ |
242 | void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const; |
243 | |
244 | /** |
245 | * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get |
246 | * the generation ID of the *original* path. This is the path that will receive |
247 | * GenIDChangeListeners added to this shape. |
248 | */ |
249 | uint32_t testingOnly_getOriginalGenerationID() const; |
250 | bool testingOnly_isPath() const; |
251 | bool testingOnly_isNonVolatilePath() const; |
252 | |
253 | private: |
254 | /** Constructor used by the applyStyle() function */ |
255 | GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); |
256 | |
257 | /** |
258 | * Determines the key we should inherit from the input shape's geometry and style when |
259 | * we are applying the style to create a new shape. |
260 | */ |
261 | void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); |
262 | |
263 | // Similar to GrShape::simplify but also takes into account style and stroking, possibly |
264 | // applying the style explicitly to produce a new analytic shape with a simpler style. |
265 | void simplify(); |
266 | // As part of the simplification process, some shapes can have stroking trivially evaluated |
267 | // and form a new geometry with just a fill. |
268 | bool simplifyStroke(bool originallyClosed); |
269 | |
270 | /** Gets the path that gen id listeners should be added to. */ |
271 | const SkPath* originalPathForListeners() const; |
272 | |
273 | GrShape fShape; |
274 | GrStyle fStyle; |
275 | // Gen ID of the original path (path may be modified or simplified away). |
276 | int32_t fGenID = 0; |
277 | bool fSimplified = false; |
278 | |
279 | SkTLazy<SkPath> fInheritedPathForListeners; |
280 | SkAutoSTArray<8, uint32_t> fInheritedKey; |
281 | }; |
282 | #endif |
283 | |