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
20class 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 */
39class GrStyledShape {
40public:
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
253private:
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