blob: d72b17965ce4f4eb21e16237263307b19477b13f [file] [log] [blame]
bsalomon47cc7692016-04-26 12:56:00 -07001/*
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:
bsalomon5e410b42016-04-28 09:30:46 -070036 GrShape() : fType(Type::kEmpty) {}
37
38 explicit GrShape(const SkPath& path)
bsalomon72dc51c2016-04-27 06:46:23 -070039 : fType(Type::kPath)
40 , fPath(&path) {
41 this->attemptToReduceFromPath();
42 }
43
bsalomon5e410b42016-04-28 09:30:46 -070044 explicit GrShape(const SkRRect& rrect)
45 : fType(Type::kRRect)
46 , fRRect(rrect) {
47 this->attemptToReduceFromRRect();
48 }
bsalomon47cc7692016-04-26 12:56:00 -070049
bsalomon5e410b42016-04-28 09:30:46 -070050 explicit GrShape(const SkRect& rect)
51 : fType(Type::kRRect)
52 , fRRect(SkRRect::MakeRect(rect)) {
53 this->attemptToReduceFromRRect();
54 }
bsalomon47cc7692016-04-26 12:56:00 -070055
bsalomon72dc51c2016-04-27 06:46:23 -070056 GrShape(const SkPath& path, const GrStyle& style)
57 : fType(Type::kPath)
58 , fPath(&path)
59 , fStyle(style) {
60 this->attemptToReduceFromPath();
61 }
62
bsalomon47cc7692016-04-26 12:56:00 -070063 GrShape(const SkRRect& rrect, const GrStyle& style)
64 : fType(Type::kRRect)
65 , fRRect(rrect)
bsalomon5e410b42016-04-28 09:30:46 -070066 , fStyle(style) {
67 this->attemptToReduceFromRRect();
68 }
bsalomon47cc7692016-04-26 12:56:00 -070069
70 GrShape(const SkRect& rect, const GrStyle& style)
71 : fType(Type::kRRect)
72 , fRRect(SkRRect::MakeRect(rect))
bsalomon5e410b42016-04-28 09:30:46 -070073 , fStyle(style) {
74 this->attemptToReduceFromRRect();
75 }
bsalomon47cc7692016-04-26 12:56:00 -070076
bsalomon72dc51c2016-04-27 06:46:23 -070077 GrShape(const SkPath& path, const SkPaint& paint)
78 : fType(Type::kPath)
79 , fPath(&path)
80 , fStyle(paint) {
81 this->attemptToReduceFromPath();
82 }
83
bsalomon47cc7692016-04-26 12:56:00 -070084 GrShape(const SkRRect& rrect, const SkPaint& paint)
85 : fType(Type::kRRect)
86 , fRRect(rrect)
bsalomon5e410b42016-04-28 09:30:46 -070087 , fStyle(paint) {
88 this->attemptToReduceFromRRect();
89 }
bsalomon47cc7692016-04-26 12:56:00 -070090
91 GrShape(const SkRect& rect, const SkPaint& paint)
92 : fType(Type::kRRect)
93 , fRRect(SkRRect::MakeRect(rect))
bsalomon5e410b42016-04-28 09:30:46 -070094 , fStyle(paint) {
95 this->attemptToReduceFromRRect();
96 }
bsalomon47cc7692016-04-26 12:56:00 -070097
98 GrShape(const GrShape&);
99 GrShape& operator=(const GrShape& that);
100
101 ~GrShape() {
102 if (Type::kPath == fType) {
103 fPath.reset();
104 }
105 }
106
107 const GrStyle& style() const { return fStyle; }
108
bsalomon97fd2d42016-05-09 13:02:01 -0700109 /**
110 * Returns a shape that has either applied the path effect or path effect and stroking
111 * information from this shape's style to its geometry. Scale is used when approximating the
112 * output geometry and typically is computed from the view matrix
113 */
114 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) {
115 return GrShape(*this, apply, scale);
116 }
bsalomon47cc7692016-04-26 12:56:00 -0700117
118 bool asRRect(SkRRect* rrect) const {
119 if (Type::kRRect != fType) {
120 return false;
121 }
122 if (rrect) {
123 *rrect = fRRect;
124 }
125 return true;
126 }
127
128 void asPath(SkPath* out) const {
129 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700130 case Type::kEmpty:
131 out->reset();
132 break;
bsalomon47cc7692016-04-26 12:56:00 -0700133 case Type::kRRect:
134 out->reset();
135 out->addRRect(fRRect);
136 break;
137 case Type::kPath:
138 *out = *fPath.get();
139 break;
bsalomon47cc7692016-04-26 12:56:00 -0700140 }
141 }
142
143 /**
bsalomon06077562016-05-04 13:50:29 -0700144 * Is it known that the shape has no unclosed contours. This means that it will not have
145 * any caps if stroked (modulo the effect of any path effect).
146 */
147 bool knownToBeClosed() const {
148 switch (fType) {
149 case Type::kEmpty:
150 return true;
151 case Type::kRRect:
152 return true;
153 case Type::kPath:
154 return false;
155 }
156 return false;
157 }
158
159 /**
bsalomon47cc7692016-04-26 12:56:00 -0700160 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
161 * A negative value is returned if the shape has no key (shouldn't be cached).
162 */
163 int unstyledKeySize() const;
164
165 /**
166 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
167 * space allocated for the key and that unstyledKeySize() does not return a negative value
168 * for this shape.
169 */
170 void writeUnstyledKey(uint32_t* key) const;
171
172private:
bsalomon72dc51c2016-04-27 06:46:23 -0700173 enum class Type {
174 kEmpty,
175 kRRect,
176 kPath,
177 };
178
bsalomon47cc7692016-04-26 12:56:00 -0700179
bsalomon97fd2d42016-05-09 13:02:01 -0700180 /** Constructor used by the applyStyle() function */
181 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700182
183 /**
184 * Determines the key we should inherit from the input shape's geometry and style when
185 * we are applying the style to create a new shape.
186 */
bsalomon97fd2d42016-05-09 13:02:01 -0700187 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700188
bsalomon72dc51c2016-04-27 06:46:23 -0700189 void attemptToReduceFromPath() {
190 SkASSERT(Type::kPath == fType);
191 fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, fStyle.pathEffect(),
192 fStyle.strokeRec());
193 if (Type::kPath != fType) {
194 fPath.reset();
195 fInheritedKey.reset(0);
196 }
197 }
bsalomon47cc7692016-04-26 12:56:00 -0700198
bsalomon5e410b42016-04-28 09:30:46 -0700199 void attemptToReduceFromRRect() {
200 SkASSERT(Type::kRRect == fType);
201 SkASSERT(!fInheritedKey.count());
202 if (fRRect.isEmpty()) {
203 fType = Type::kEmpty;
204 }
205 }
206
bsalomon72dc51c2016-04-27 06:46:23 -0700207 static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect,
208 const SkPathEffect* pe, const SkStrokeRec& strokeRec) {
209 if (path.isEmpty()) {
210 return Type::kEmpty;
211 }
212 if (path.isRRect(rrect)) {
bsalomon5e410b42016-04-28 09:30:46 -0700213 SkASSERT(!rrect->isEmpty());
bsalomon72dc51c2016-04-27 06:46:23 -0700214 return Type::kRRect;
215 }
216 SkRect rect;
217 if (path.isOval(&rect)) {
218 rrect->setOval(rect);
219 return Type::kRRect;
220 }
221 bool closed;
222 if (path.isRect(&rect, &closed, nullptr)) {
223 if (closed || (!pe && strokeRec.isFillStyle())) {
224 rrect->setRect(rect);
225 return Type::kRRect;
226 }
227 }
228 return Type::kPath;
229 }
230
231 Type fType;
bsalomon47cc7692016-04-26 12:56:00 -0700232 SkRRect fRRect;
233 SkTLazy<SkPath> fPath;
234 GrStyle fStyle;
235 SkAutoSTArray<8, uint32_t> fInheritedKey;
236};
237#endif