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 GrShape_DEFINED
9#define GrShape_DEFINED
10
11#include "GrStyle.h"
12#include "SkPath.h"
13#include "SkPathPriv.h"
14#include "SkRRect.h"
15#include "SkTemplates.h"
16#include "SkTLazy.h"
17
18/**
19 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
20 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
21 * reflects the styling information (e.g. is stroked). It is also possible to apply just the
22 * path effect from the style. In this case the resulting shape will include any remaining
23 * stroking information that is to be applied after the path effect.
24 *
25 * Shapes can produce keys that represent only the geometry information, not the style. Note that
26 * when styling information is applied to produce a new shape then the style has been converted
27 * to geometric information and is included in the new shape's key. When the same style is applied
28 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
29 * will be the same.
30 *
31 * Currently this can only be constructed from a path, rect, or rrect though it can become a path
32 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
33 * that have SkCanvas::draw APIs.
34 */
35class GrShape {
36public:
37    // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
38    // to have to worry about this. This value is exposed for unit tests.
39    static constexpr int kMaxKeyFromDataVerbCnt = 10;
40
41    GrShape() { this->initType(Type::kEmpty); }
42
43    explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
44
45    explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
46
47    explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
48
49    GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
50        this->initType(Type::kPath, &path);
51        this->attemptToSimplifyPath();
52    }
53
54    GrShape(const SkRRect& rrect, const GrStyle& style)
55        : fStyle(style) {
56        this->initType(Type::kRRect);
57        fRRectData.fRRect = rrect;
58        fRRectData.fInverted = false;
59        fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
60                                                         &fRRectData.fDir);
61        this->attemptToSimplifyRRect();
62    }
63
64    GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
65            const GrStyle& style)
66        : fStyle(style) {
67        this->initType(Type::kRRect);
68        fRRectData.fRRect = rrect;
69        fRRectData.fInverted = inverted;
70        if (style.pathEffect()) {
71            fRRectData.fDir = dir;
72            fRRectData.fStart = start;
73            if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
74                fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
75            } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
76                fRRectData.fStart &= 0b110;
77            }
78        } else {
79            fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
80        }
81        this->attemptToSimplifyRRect();
82    }
83
84    GrShape(const SkRect& rect, const GrStyle& style)
85        : fStyle(style) {
86        this->initType(Type::kRRect);
87        fRRectData.fRRect = SkRRect::MakeRect(rect);
88        fRRectData.fInverted = false;
89        fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
90                                                        &fRRectData.fDir);
91        this->attemptToSimplifyRRect();
92    }
93
94    GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
95        this->initType(Type::kPath, &path);
96        this->attemptToSimplifyPath();
97    }
98
99    GrShape(const SkRRect& rrect, const SkPaint& paint)
100        : fStyle(paint) {
101        this->initType(Type::kRRect);
102        fRRectData.fRRect = rrect;
103        fRRectData.fInverted = false;
104        fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
105                                                         &fRRectData.fDir);
106        this->attemptToSimplifyRRect();
107    }
108
109    GrShape(const SkRect& rect, const SkPaint& paint)
110        : fStyle(paint) {
111        this->initType(Type::kRRect);
112        fRRectData.fRRect = SkRRect::MakeRect(rect);
113        fRRectData.fInverted = false;
114        fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
115                                                        &fRRectData.fDir);
116        this->attemptToSimplifyRRect();
117    }
118
119    GrShape(const GrShape&);
120    GrShape& operator=(const GrShape& that);
121
122    ~GrShape() { this->changeType(Type::kEmpty); }
123
124    const GrStyle& style() const { return fStyle; }
125
126    /**
127     * Returns a shape that has either applied the path effect or path effect and stroking
128     * information from this shape's style to its geometry. Scale is used when approximating the
129     * output geometry and typically is computed from the view matrix
130     */
131    GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
132        return GrShape(*this, apply, scale);
133    }
134
135    /** Returns the unstyled geometry as a rrect if possible. */
136    bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
137        if (Type::kRRect != fType) {
138            return false;
139        }
140        if (rrect) {
141            *rrect = fRRectData.fRRect;
142        }
143        if (dir) {
144            *dir = fRRectData.fDir;
145        }
146        if (start) {
147            *start = fRRectData.fStart;
148        }
149        if (inverted) {
150            *inverted = fRRectData.fInverted;
151        }
152        return true;
153    }
154
155    /**
156     * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
157     * An inverse filled line path is still considered a line.
158     */
159    bool asLine(SkPoint pts[2], bool* inverted) const {
160        if (fType != Type::kLine) {
161            return false;
162        }
163        if (pts) {
164            pts[0] = fLineData.fPts[0];
165            pts[1] = fLineData.fPts[1];
166        }
167        if (inverted) {
168            *inverted = fLineData.fInverted;
169        }
170        return true;
171    }
172
173    /** Returns the unstyled geometry as a path. */
174    void asPath(SkPath* out) const {
175        switch (fType) {
176            case Type::kEmpty:
177                out->reset();
178                break;
179            case Type::kRRect:
180                out->reset();
181                out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
182                // Below matches the fill type that attemptToSimplifyPath uses.
183                if (fRRectData.fInverted) {
184                    out->setFillType(kDefaultPathInverseFillType);
185                } else {
186                    out->setFillType(kDefaultPathFillType);
187                }
188                break;
189            case Type::kLine:
190                out->reset();
191                out->moveTo(fLineData.fPts[0]);
192                out->lineTo(fLineData.fPts[1]);
193                if (fLineData.fInverted) {
194                    out->setFillType(kDefaultPathInverseFillType);
195                } else {
196                    out->setFillType(kDefaultPathFillType);
197                }
198                break;
199            case Type::kPath:
200                *out = this->path();
201                break;
202        }
203    }
204
205    /**
206     * Returns whether the geometry is empty. Note that applying the style could produce a
207     * non-empty shape.
208     */
209    bool isEmpty() const { return Type::kEmpty == fType; }
210
211    /**
212     * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
213     * the inverse fill nature of the geometry.
214     */
215    SkRect bounds() const;
216
217    /**
218     * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
219     * status).
220     */
221    SkRect styledBounds() const;
222
223    /**
224     * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
225     * convex path is considered to be closed if they styling reflects a fill and not otherwise.
226     * This is because filling closes all contours in the path.
227     */
228    bool knownToBeConvex() const {
229        switch (fType) {
230            case Type::kEmpty:
231                return true;
232            case Type::kRRect:
233                return true;
234            case Type::kLine:
235                return true;
236            case Type::kPath:
237                // SkPath.isConvex() really means "is this path convex were it to be closed" and
238                // thus doesn't give the correct answer for stroked paths, hence we also check
239                // whether the path is either filled or closed. Convex paths may only have one
240                // contour hence isLastContourClosed() is a sufficient for a convex path.
241                return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
242                        this->path().isConvex();
243        }
244        return false;
245    }
246
247    /** Is the pre-styled geometry inverse filled? */
248    bool inverseFilled() const {
249        bool ret = false;
250        switch (fType) {
251            case Type::kEmpty:
252                ret = false;
253                break;
254            case Type::kRRect:
255                ret = fRRectData.fInverted;
256                break;
257            case Type::kLine:
258                ret = fLineData.fInverted;
259                break;
260            case Type::kPath:
261                ret = this->path().isInverseFillType();
262                break;
263        }
264        // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
265        SkASSERT(!(ret && this->style().isDashed()));
266        return ret;
267    }
268
269    /**
270     * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
271     * because an arbitrary path effect could produce an inverse filled path. In other cases this
272     * can be thought of as "inverseFilledAfterStyling()".
273     */
274    bool mayBeInverseFilledAfterStyling() const {
275         // An arbitrary path effect can produce an arbitrary output path, which may be inverse
276         // filled.
277        if (this->style().hasNonDashPathEffect()) {
278            return true;
279        }
280        return this->inverseFilled();
281    }
282
283    /**
284     * Is it known that the unstyled geometry has no unclosed contours. This means that it will
285     * not have any caps if stroked (modulo the effect of any path effect).
286     */
287    bool knownToBeClosed() const {
288        switch (fType) {
289            case Type::kEmpty:
290                return true;
291            case Type::kRRect:
292                return true;
293            case Type::kLine:
294                return false;
295            case Type::kPath:
296                // SkPath doesn't keep track of the closed status of each contour.
297                return SkPathPriv::IsClosedSingleContour(this->path());
298        }
299        return false;
300    }
301
302    uint32_t segmentMask() const {
303        switch (fType) {
304            case Type::kEmpty:
305                return 0;
306            case Type::kRRect:
307                if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
308                    return SkPath::kConic_SegmentMask;
309                } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
310                    return SkPath::kLine_SegmentMask;
311                }
312                return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
313            case Type::kLine:
314                return SkPath::kLine_SegmentMask;
315            case Type::kPath:
316                return this->path().getSegmentMasks();
317        }
318        return 0;
319    }
320
321    /**
322     * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
323     * A negative value is returned if the shape has no key (shouldn't be cached).
324     */
325    int unstyledKeySize() const;
326
327    bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
328
329    /**
330     * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
331     * space allocated for the key and that unstyledKeySize() does not return a negative value
332     * for this shape.
333     */
334    void writeUnstyledKey(uint32_t* key) const;
335
336private:
337    enum class Type {
338        kEmpty,
339        kRRect,
340        kLine,
341        kPath,
342    };
343
344    void initType(Type type, const SkPath* path = nullptr) {
345        fType = Type::kEmpty;
346        this->changeType(type, path);
347    }
348
349    void changeType(Type type, const SkPath* path = nullptr) {
350        bool wasPath = Type::kPath == fType;
351        fType = type;
352        bool isPath = Type::kPath == type;
353        SkASSERT(!path || isPath);
354        if (wasPath && !isPath) {
355            fPathData.fPath.~SkPath();
356        } else if (!wasPath && isPath) {
357            if (path) {
358                new (&fPathData.fPath) SkPath(*path);
359            } else {
360                new (&fPathData.fPath) SkPath();
361            }
362        } else if (isPath && path) {
363            fPathData.fPath = *path;
364        }
365        // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
366        fPathData.fGenID = 0;
367    }
368
369    SkPath& path() {
370        SkASSERT(Type::kPath == fType);
371        return fPathData.fPath;
372    }
373
374    const SkPath& path() const {
375        SkASSERT(Type::kPath == fType);
376        return fPathData.fPath;
377    }
378
379    /** Constructor used by the applyStyle() function */
380    GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
381
382    /**
383     * Determines the key we should inherit from the input shape's geometry and style when
384     * we are applying the style to create a new shape.
385     */
386    void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
387
388    void attemptToSimplifyPath();
389    void attemptToSimplifyRRect();
390    void attemptToSimplifyLine();
391
392    // Defaults to use when there is no distinction between even/odd and winding fills.
393    static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
394    static constexpr SkPath::FillType kDefaultPathInverseFillType =
395            SkPath::kInverseEvenOdd_FillType;
396
397    static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
398    static constexpr unsigned kDefaultRRectStart = 0;
399
400    static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
401                                                SkPath::Direction* dir) {
402        *dir = kDefaultRRectDir;
403        // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
404        // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
405        if (!hasPathEffect) {
406            // It doesn't matter what start we use, just be consistent to avoid redundant keys.
407            return kDefaultRRectStart;
408        }
409        // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
410        // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
411        // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
412        bool swapX = rect.fLeft > rect.fRight;
413        bool swapY = rect.fTop > rect.fBottom;
414        if (swapX && swapY) {
415            // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
416            return 2 * 2;
417        } else if (swapX) {
418            *dir = SkPath::kCCW_Direction;
419            // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
420            return 2 * 1;
421        } else if (swapY) {
422            *dir = SkPath::kCCW_Direction;
423            // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
424            return 2 * 3;
425        }
426        return 0;
427    }
428
429    static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
430                                                 SkPath::Direction* dir) {
431        // This comes from SkPath's interface. The default for adding a SkRRect to a path is
432        // clockwise beginning at starting index 6.
433        static constexpr unsigned kPathRRectStartIdx = 6;
434        *dir = kDefaultRRectDir;
435        if (!hasPathEffect) {
436            // It doesn't matter what start we use, just be consistent to avoid redundant keys.
437            return kDefaultRRectStart;
438        }
439        return kPathRRectStartIdx;
440    }
441
442    Type                        fType;
443    union {
444        struct {
445            SkRRect                     fRRect;
446            SkPath::Direction           fDir;
447            unsigned                    fStart;
448            bool                        fInverted;
449        } fRRectData;
450        struct {
451            SkPath                      fPath;
452            // Gen ID of the original path (fPath may be modified)
453            int32_t                     fGenID;
454        } fPathData;
455        struct {
456            SkPoint                     fPts[2];
457            bool                        fInverted;
458        } fLineData;
459    };
460    GrStyle                     fStyle;
461    SkAutoSTArray<8, uint32_t>  fInheritedKey;
462};
463#endif
464