• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GrStyledShape()45     GrStyledShape() {}
46 
47     enum class DoSimplify : bool { kNo = false, kYes };
48 
49     explicit GrStyledShape(const SkPath& path, DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(path,GrStyle::SimpleFill (),doSimplify)50             : GrStyledShape(path, GrStyle::SimpleFill(), doSimplify) {}
51 
52     explicit GrStyledShape(const SkRRect& rrect, DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(rrect,GrStyle::SimpleFill (),doSimplify)53             : GrStyledShape(rrect, GrStyle::SimpleFill(), doSimplify) {}
54 
55     explicit GrStyledShape(const SkRect& rect, DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(rect,GrStyle::SimpleFill (),doSimplify)56             : GrStyledShape(rect, GrStyle::SimpleFill(), doSimplify) {}
57 
58     GrStyledShape(const SkPath& path, const SkPaint& paint,
59                   DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(path,GrStyle (paint),doSimplify)60             : GrStyledShape(path, GrStyle(paint), doSimplify) {}
61 
62     GrStyledShape(const SkRRect& rrect, const SkPaint& paint,
63                   DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(rrect,GrStyle (paint),doSimplify)64             : GrStyledShape(rrect, GrStyle(paint), doSimplify) {}
65 
66     GrStyledShape(const SkRect& rect, const SkPaint& paint,
67                   DoSimplify doSimplify = DoSimplify::kYes)
GrStyledShape(rect,GrStyle (paint),doSimplify)68             : GrStyledShape(rect, GrStyle(paint), doSimplify) {}
69 
70     GrStyledShape(const SkPath& path, const GrStyle& style,
71                   DoSimplify doSimplify = DoSimplify::kYes)
fShape(path)72             : fShape(path), fStyle(style) {
73         if (doSimplify == DoSimplify::kYes) {
74             this->simplify();
75         }
76     }
77 
78     GrStyledShape(const SkRRect& rrect, const GrStyle& style,
79                   DoSimplify doSimplify = DoSimplify::kYes)
fShape(rrect)80             : fShape(rrect), fStyle(style) {
81         if (doSimplify == DoSimplify::kYes) {
82             this->simplify();
83         }
84     }
85 
86     GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
87                   const GrStyle& style, DoSimplify doSimplify = DoSimplify::kYes)
fShape(rrect)88             : fShape(rrect)
89             , fStyle(style) {
90         fShape.setPathWindingParams(dir, start);
91         fShape.setInverted(inverted);
92         if (doSimplify == DoSimplify::kYes) {
93             this->simplify();
94         }
95     }
96 
97     GrStyledShape(const SkRect& rect, const GrStyle& style,
98                   DoSimplify doSimplify = DoSimplify::kYes)
fShape(rect)99             : fShape(rect), fStyle(style) {
100         if (doSimplify == DoSimplify::kYes) {
101             this->simplify();
102         }
103     }
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 
style()133     const GrStyle& style() const { return fStyle; }
134 
135     // True if the shape and/or style were modified into a simpler, equivalent pairing
simplified()136     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      */
applyStyle(GrStyle::Apply apply,SkScalar scale)143     GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
144         return GrStyledShape(*this, apply, scale);
145     }
146 
isRect()147     bool isRect() const {
148         // Should have simplified a rrect to a rect if possible already.
149         SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect());
150         return fShape.isRect();
151     }
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. */
asPath(SkPath * out)166     void asPath(SkPath* out) const {
167         fShape.asPath(out, fStyle.isSimpleFill());
168     }
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      */
isEmpty()174     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      */
bounds()180     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      */
knownToBeConvex()193     bool knownToBeConvex() const {
194         return fShape.convex(fStyle.isSimpleFill());
195     }
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      */
knownDirection()202     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         return !fShape.isPath() ||
206                SkPathPriv::ComputeFirstDirection(fShape.path()) != SkPathFirstDirection::kUnknown;
207     }
208 
209     /** Is the pre-styled geometry inverse filled? */
inverseFilled()210     bool inverseFilled() const {
211         // Since the path tracks inverted-fillness itself, it should match what was recorded.
212         SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType());
213         // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
214         SkASSERT(!(fShape.inverted() && this->style().isDashed()));
215         return fShape.inverted();
216     }
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      */
mayBeInverseFilledAfterStyling()223     bool mayBeInverseFilledAfterStyling() const {
224          // An arbitrary path effect can produce an arbitrary output path, which may be inverse
225          // filled.
226         if (this->style().hasNonDashPathEffect()) {
227             return true;
228         }
229         return this->inverseFilled();
230     }
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      */
knownToBeClosed()236     bool knownToBeClosed() const {
237         // This refers to the base shape and does not depend on invertedness.
238         return fShape.closed();
239     }
240 
segmentMask()241     uint32_t segmentMask() const {
242         // This refers to the base shape and does not depend on invertedness.
243         return fShape.segmentMask();
244     }
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 
hasUnstyledKey()252     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
314