Coverage Report

Created: 2021-08-22 09:07

/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