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