GrShape.h revision 72dc51c288169f38177c71081090581c5ff415b1
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 "SkRRect.h"
14#include "SkTemplates.h"
15#include "SkTLazy.h"
16
17/**
18 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
19 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
20 * reflects the styling information (e.g. is stroked). It is also possible to apply just the
21 * path effect from the style. In this case the resulting shape will include any remaining
22 * stroking information that is to be applied after the path effect.
23 *
24 * Shapes can produce keys that represent only the geometry information, not the style. Note that
25 * when styling information is applied to produce a new shape then the style has been converted
26 * to geometric information and is included in the new shape's key. When the same style is applied
27 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
28 * will be the same.
29 *
30 * Currently this can only be constructed from a rrect, though it can become a path by applying
31 * style to the geometry. The idea is to expand this to cover most or all of the geometries that
32 * have SkCanvas::draw APIs.
33 */
34class GrShape {
35public:
36    GrShape(const SkPath& path)
37            : fType(Type::kPath)
38            , fPath(&path) {
39        this->attemptToReduceFromPath();
40    }
41
42    GrShape() : fType(Type::kEmpty) {}
43
44    explicit GrShape(const SkRRect& rrect) : fType(Type::kRRect), fRRect(rrect) {}
45    explicit GrShape(const SkRect& rect) : fType(Type::kRRect), fRRect(SkRRect::MakeRect(rect)) {}
46
47    GrShape(const SkPath& path, const GrStyle& style)
48        : fType(Type::kPath)
49        , fPath(&path)
50        , fStyle(style) {
51        this->attemptToReduceFromPath();
52    }
53
54    GrShape(const SkRRect& rrect, const GrStyle& style)
55        : fType(Type::kRRect)
56        , fRRect(rrect)
57        , fStyle(style) {}
58
59    GrShape(const SkRect& rect, const GrStyle& style)
60        : fType(Type::kRRect)
61        , fRRect(SkRRect::MakeRect(rect))
62        , fStyle(style) {}
63
64    GrShape(const SkPath& path, const SkPaint& paint)
65        : fType(Type::kPath)
66        , fPath(&path)
67        , fStyle(paint) {
68        this->attemptToReduceFromPath();
69    }
70
71    GrShape(const SkRRect& rrect, const SkPaint& paint)
72        : fType(Type::kRRect)
73        , fRRect(rrect)
74        , fStyle(paint) {}
75
76    GrShape(const SkRect& rect, const SkPaint& paint)
77        : fType(Type::kRRect)
78        , fRRect(SkRRect::MakeRect(rect))
79        , fStyle(paint) {}
80
81    GrShape(const GrShape&);
82    GrShape& operator=(const GrShape& that);
83
84    ~GrShape() {
85        if (Type::kPath == fType) {
86            fPath.reset();
87        }
88    }
89
90    const GrStyle& style() const { return fStyle; }
91
92    /**
93     * Returns a GrShape where the shape's geometry fully reflects the original shape's GrStyle.
94     * The GrStyle of the returned shape will either be fill or hairline.
95     */
96    GrShape applyFullStyle() { return GrShape(*this, false); }
97
98    /**
99     * Similar to above but applies only the path effect. Path effects take the original geometry
100     * and fill/stroking information and compute a new geometry and residual fill/stroking
101     * information to be applied. The path effect's output geometry and stroking will be captured
102     * in the returned GrShape.
103     */
104    GrShape applyPathEffect() { return GrShape(*this, true); }
105
106    bool asRRect(SkRRect* rrect) const {
107        if (Type::kRRect != fType) {
108            return false;
109        }
110        if (rrect) {
111            *rrect = fRRect;
112        }
113        return true;
114    }
115
116    void asPath(SkPath* out) const {
117        switch (fType) {
118            case Type::kRRect:
119                out->reset();
120                out->addRRect(fRRect);
121                break;
122            case Type::kPath:
123                *out = *fPath.get();
124                break;
125            case Type::kEmpty:
126                out->reset();
127                break;
128        }
129    }
130
131    /**
132     * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
133     * A negative value is returned if the shape has no key (shouldn't be cached).
134     */
135    int unstyledKeySize() const;
136
137    /**
138     * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
139     * space allocated for the key and that unstyledKeySize() does not return a negative value
140     * for this shape.
141     */
142    void writeUnstyledKey(uint32_t* key) const;
143
144private:
145    enum class Type {
146        kEmpty,
147        kRRect,
148        kPath,
149    };
150
151    /**
152     * Computes the key length for a GrStyle. The return will be negative if it cannot be turned
153     * into a key.
154     */
155    static int StyleKeySize(const GrStyle& , bool stopAfterPE);
156
157    /**
158     * Writes a unique key for the style into the provided buffer. This function assumes the buffer
159     * has room for at least StyleKeySize() values. It assumes that StyleKeySize() returns a
160     * positive value for the style and stopAfterPE param. This is written so that the key for just
161     * dash application followed by the key for the remaining SkStrokeRec is the same as the
162     * key for applying dashing and SkStrokeRec all at once.
163     */
164    static void StyleKey(uint32_t*, const GrStyle&, bool stopAfterPE);
165
166    /** Constructor used by Apply* functions */
167    GrShape(const GrShape& parentShape, bool stopAfterPE);
168
169    /**
170     * Determines the key we should inherit from the input shape's geometry and style when
171     * we are applying the style to create a new shape.
172     */
173    void setInheritedKey(const GrShape& parentShape, bool stopAfterPE);
174
175    void attemptToReduceFromPath() {
176        SkASSERT(Type::kPath == fType);
177        fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, fStyle.pathEffect(),
178                                            fStyle.strokeRec());
179        if (Type::kPath != fType) {
180            fPath.reset();
181            fInheritedKey.reset(0);
182        }
183    }
184
185    static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect,
186                                            const SkPathEffect* pe, const SkStrokeRec& strokeRec) {
187        if (path.isEmpty()) {
188            return Type::kEmpty;
189        }
190        if (path.isRRect(rrect)) {
191            return Type::kRRect;
192        }
193        SkRect rect;
194        if (path.isOval(&rect)) {
195            rrect->setOval(rect);
196            return Type::kRRect;
197        }
198        bool closed;
199        if (path.isRect(&rect, &closed, nullptr)) {
200            if (closed || (!pe && strokeRec.isFillStyle())) {
201                rrect->setRect(rect);
202                return Type::kRRect;
203            }
204        }
205        return Type::kPath;
206    }
207
208    Type                        fType;
209    SkRRect                     fRRect;
210    SkTLazy<SkPath>             fPath;
211    GrStyle                     fStyle;
212    SkAutoSTArray<8, uint32_t>  fInheritedKey;
213};
214#endif
215