OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #ifndef GrShape_DEFINED | 8 #ifndef GrShape_DEFINED |
9 #define GrShape_DEFINED | 9 #define GrShape_DEFINED |
10 | 10 |
11 #include "GrStyle.h" | 11 #include "GrStyle.h" |
12 #include "SkPath.h" | 12 #include "SkPath.h" |
| 13 #include "SkPathPriv.h" |
13 #include "SkRRect.h" | 14 #include "SkRRect.h" |
14 #include "SkTemplates.h" | 15 #include "SkTemplates.h" |
15 #include "SkTLazy.h" | 16 #include "SkTLazy.h" |
16 | 17 |
17 /** | 18 /** |
18 * Represents a geometric shape (rrect or path) and the GrStyle that it should b
e rendered with. | 19 * Represents a geometric shape (rrect or path) and the GrStyle that it should b
e rendered with. |
19 * It is possible to apply the style to the GrShape to produce a new GrShape whe
re the geometry | 20 * It is possible to apply the style to the GrShape to produce a new GrShape whe
re the geometry |
20 * reflects the styling information (e.g. is stroked). It is also possible to ap
ply just the | 21 * reflects the styling information (e.g. is stroked). It is also possible to ap
ply just the |
21 * path effect from the style. In this case the resulting shape will include any
remaining | 22 * 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 * stroking information that is to be applied after the path effect. |
23 * | 24 * |
24 * Shapes can produce keys that represent only the geometry information, not the
style. Note that | 25 * 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 * 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 sam
e style is applied | 27 * to geometric information and is included in the new shape's key. When the sam
e style is applied |
27 * to two shapes that reflect the same underlying geometry the computed keys of
the stylized shapes | 28 * to two shapes that reflect the same underlying geometry the computed keys of
the stylized shapes |
28 * will be the same. | 29 * will be the same. |
29 * | 30 * |
30 * Currently this can only be constructed from a rrect, though it can become a p
ath by applying | 31 * Currently this can only be constructed from a path, rect, or rrect though it
can become a path |
31 * style to the geometry. The idea is to expand this to cover most or all of the
geometries that | 32 * applying style to the geometry. The idea is to expand this to cover most or a
ll of the geometries |
32 * have SkCanvas::draw APIs. | 33 * that have SkCanvas::draw APIs. |
33 */ | 34 */ |
34 class GrShape { | 35 class GrShape { |
35 public: | 36 public: |
36 GrShape() : fType(Type::kEmpty) {} | 37 GrShape() : fType(Type::kEmpty) {} |
37 | 38 |
38 explicit GrShape(const SkPath& path) | 39 explicit GrShape(const SkPath& path) |
39 : fType(Type::kPath) | 40 : fType(Type::kPath) |
40 , fPath(&path) { | 41 , fPath(&path) { |
41 this->attemptToReduceFromPath(); | 42 this->attemptToReduceFromPath(); |
42 } | 43 } |
43 | 44 |
44 explicit GrShape(const SkRRect& rrect) | 45 explicit GrShape(const SkRRect& rrect) |
45 : fType(Type::kRRect) | 46 : fType(Type::kRRect) |
46 , fRRect(rrect) { | 47 , fRRect(rrect) { |
| 48 fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir); |
47 this->attemptToReduceFromRRect(); | 49 this->attemptToReduceFromRRect(); |
48 } | 50 } |
49 | 51 |
50 explicit GrShape(const SkRect& rect) | 52 explicit GrShape(const SkRect& rect) |
51 : fType(Type::kRRect) | 53 : fType(Type::kRRect) |
52 , fRRect(SkRRect::MakeRect(rect)) { | 54 , fRRect(SkRRect::MakeRect(rect)) { |
| 55 fRRectStart = DefaultRectDirAndStartIndex(rect, false, &fRRectDir); |
53 this->attemptToReduceFromRRect(); | 56 this->attemptToReduceFromRRect(); |
54 } | 57 } |
55 | 58 |
56 GrShape(const SkPath& path, const GrStyle& style) | 59 GrShape(const SkPath& path, const GrStyle& style) |
57 : fType(Type::kPath) | 60 : fType(Type::kPath) |
58 , fPath(&path) | 61 , fPath(&path) |
59 , fStyle(style) { | 62 , fStyle(style) { |
60 this->attemptToReduceFromPath(); | 63 this->attemptToReduceFromPath(); |
61 } | 64 } |
62 | 65 |
63 GrShape(const SkRRect& rrect, const GrStyle& style) | 66 GrShape(const SkRRect& rrect, const GrStyle& style) |
64 : fType(Type::kRRect) | 67 : fType(Type::kRRect) |
65 , fRRect(rrect) | 68 , fRRect(rrect) |
66 , fStyle(style) { | 69 , fStyle(style) { |
| 70 fRRectStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
&fRRectDir); |
67 this->attemptToReduceFromRRect(); | 71 this->attemptToReduceFromRRect(); |
68 } | 72 } |
69 | 73 |
| 74 GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, const G
rStyle& style) |
| 75 : fType(Type::kRRect) |
| 76 , fRRect(rrect) |
| 77 , fStyle(style) { |
| 78 if (style.pathEffect()) { |
| 79 fRRectDir = dir; |
| 80 fRRectStart = start; |
| 81 } else { |
| 82 fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir)
; |
| 83 } |
| 84 this->attemptToReduceFromRRect(); |
| 85 } |
| 86 |
70 GrShape(const SkRect& rect, const GrStyle& style) | 87 GrShape(const SkRect& rect, const GrStyle& style) |
71 : fType(Type::kRRect) | 88 : fType(Type::kRRect) |
72 , fRRect(SkRRect::MakeRect(rect)) | 89 , fRRect(SkRRect::MakeRect(rect)) |
73 , fStyle(style) { | 90 , fStyle(style) { |
| 91 fRRectStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(), &
fRRectDir); |
74 this->attemptToReduceFromRRect(); | 92 this->attemptToReduceFromRRect(); |
75 } | 93 } |
76 | 94 |
77 GrShape(const SkPath& path, const SkPaint& paint) | 95 GrShape(const SkPath& path, const SkPaint& paint) |
78 : fType(Type::kPath) | 96 : fType(Type::kPath) |
79 , fPath(&path) | 97 , fPath(&path) |
80 , fStyle(paint) { | 98 , fStyle(paint) { |
81 this->attemptToReduceFromPath(); | 99 this->attemptToReduceFromPath(); |
82 } | 100 } |
83 | 101 |
84 GrShape(const SkRRect& rrect, const SkPaint& paint) | 102 GrShape(const SkRRect& rrect, const SkPaint& paint) |
85 : fType(Type::kRRect) | 103 : fType(Type::kRRect) |
86 , fRRect(rrect) | 104 , fRRect(rrect) |
87 , fStyle(paint) { | 105 , fStyle(paint) { |
| 106 fRRectStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect()
, &fRRectDir); |
88 this->attemptToReduceFromRRect(); | 107 this->attemptToReduceFromRRect(); |
89 } | 108 } |
90 | 109 |
91 GrShape(const SkRect& rect, const SkPaint& paint) | 110 GrShape(const SkRect& rect, const SkPaint& paint) |
92 : fType(Type::kRRect) | 111 : fType(Type::kRRect) |
93 , fRRect(SkRRect::MakeRect(rect)) | 112 , fRRect(SkRRect::MakeRect(rect)) |
94 , fStyle(paint) { | 113 , fStyle(paint) { |
| 114 fRRectStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
&fRRectDir); |
95 this->attemptToReduceFromRRect(); | 115 this->attemptToReduceFromRRect(); |
96 } | 116 } |
97 | 117 |
98 GrShape(const GrShape&); | 118 GrShape(const GrShape&); |
99 GrShape& operator=(const GrShape& that); | 119 GrShape& operator=(const GrShape& that); |
100 | 120 |
101 ~GrShape() { | 121 ~GrShape() { |
102 if (Type::kPath == fType) { | 122 if (Type::kPath == fType) { |
103 fPath.reset(); | 123 fPath.reset(); |
104 } | 124 } |
105 } | 125 } |
106 | 126 |
107 const GrStyle& style() const { return fStyle; } | 127 const GrStyle& style() const { return fStyle; } |
108 | 128 |
109 /** | 129 /** |
110 * Returns a shape that has either applied the path effect or path effect an
d stroking | 130 * Returns a shape that has either applied the path effect or path effect an
d stroking |
111 * information from this shape's style to its geometry. Scale is used when a
pproximating the | 131 * information from this shape's style to its geometry. Scale is used when a
pproximating the |
112 * output geometry and typically is computed from the view matrix | 132 * output geometry and typically is computed from the view matrix |
113 */ | 133 */ |
114 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) { | 134 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) { |
115 return GrShape(*this, apply, scale); | 135 return GrShape(*this, apply, scale); |
116 } | 136 } |
117 | 137 |
118 /** Returns the unstyled geometry as a rrect if possible. */ | 138 /** Returns the unstyled geometry as a rrect if possible. */ |
119 bool asRRect(SkRRect* rrect) const { | 139 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start) const
{ |
120 if (Type::kRRect != fType) { | 140 if (Type::kRRect != fType) { |
121 return false; | 141 return false; |
122 } | 142 } |
123 if (rrect) { | 143 if (rrect) { |
124 *rrect = fRRect; | 144 *rrect = fRRect; |
125 } | 145 } |
| 146 if (dir) { |
| 147 *dir = fRRectDir; |
| 148 } |
| 149 if (start) { |
| 150 *start = fRRectStart; |
| 151 } |
126 return true; | 152 return true; |
127 } | 153 } |
128 | 154 |
129 /** Returns the unstyled geometry as a path. */ | 155 /** Returns the unstyled geometry as a path. */ |
130 void asPath(SkPath* out) const { | 156 void asPath(SkPath* out) const { |
131 switch (fType) { | 157 switch (fType) { |
132 case Type::kEmpty: | 158 case Type::kEmpty: |
133 out->reset(); | 159 out->reset(); |
134 break; | 160 break; |
135 case Type::kRRect: | 161 case Type::kRRect: |
136 out->reset(); | 162 out->reset(); |
137 out->addRRect(fRRect); | 163 out->addRRect(fRRect, fRRectDir, fRRectStart); |
138 break; | 164 break; |
139 case Type::kPath: | 165 case Type::kPath: |
140 *out = *fPath.get(); | 166 *out = *fPath.get(); |
141 break; | 167 break; |
142 } | 168 } |
143 } | 169 } |
144 | 170 |
145 /** | 171 /** |
146 * Returns whether the geometry is empty. Note that applying the style could
produce a | 172 * Returns whether the geometry is empty. Note that applying the style could
produce a |
147 * non-empty shape. | 173 * non-empty shape. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 */ | 209 */ |
184 void writeUnstyledKey(uint32_t* key) const; | 210 void writeUnstyledKey(uint32_t* key) const; |
185 | 211 |
186 private: | 212 private: |
187 enum class Type { | 213 enum class Type { |
188 kEmpty, | 214 kEmpty, |
189 kRRect, | 215 kRRect, |
190 kPath, | 216 kPath, |
191 }; | 217 }; |
192 | 218 |
193 | |
194 /** Constructor used by the applyStyle() function */ | 219 /** Constructor used by the applyStyle() function */ |
195 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); | 220 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); |
196 | 221 |
197 /** | 222 /** |
198 * Determines the key we should inherit from the input shape's geometry and
style when | 223 * Determines the key we should inherit from the input shape's geometry and
style when |
199 * we are applying the style to create a new shape. | 224 * we are applying the style to create a new shape. |
200 */ | 225 */ |
201 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar sc
ale); | 226 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar sc
ale); |
202 | 227 |
203 void attemptToReduceFromPath() { | 228 void attemptToReduceFromPath() { |
204 SkASSERT(Type::kPath == fType); | 229 SkASSERT(Type::kPath == fType); |
205 fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, fStyle.pathEf
fect(), | 230 fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, &fRRectDir, &
fRRectStart, |
206 fStyle.strokeRec()); | 231 fStyle.pathEffect(), fStyle.strokeRe
c()); |
207 if (Type::kPath != fType) { | 232 if (Type::kPath != fType) { |
208 fPath.reset(); | 233 fPath.reset(); |
209 fInheritedKey.reset(0); | 234 fInheritedKey.reset(0); |
210 } | 235 } |
211 } | 236 } |
212 | 237 |
213 void attemptToReduceFromRRect() { | 238 void attemptToReduceFromRRect() { |
214 SkASSERT(Type::kRRect == fType); | 239 SkASSERT(Type::kRRect == fType); |
215 SkASSERT(!fInheritedKey.count()); | 240 SkASSERT(!fInheritedKey.count()); |
216 if (fRRect.isEmpty()) { | 241 if (fRRect.isEmpty()) { |
217 fType = Type::kEmpty; | 242 fType = Type::kEmpty; |
218 } | 243 } |
219 } | 244 } |
220 | 245 |
221 static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect, | 246 static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect, |
222 const SkPathEffect* pe, const SkStro
keRec& strokeRec) { | 247 SkPath::Direction* rrectDir, unsigne
d* rrectStart, |
223 if (path.isEmpty()) { | 248 const SkPathEffect* pe, const SkStro
keRec& strokeRec); |
224 return Type::kEmpty; | 249 |
| 250 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction; |
| 251 static constexpr unsigned kDefaultRRectStart = 0; |
| 252 |
| 253 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPath
Effect, |
| 254 SkPath::Direction* dir) { |
| 255 *dir = kDefaultRRectDir; |
| 256 // This comes from SkPath's interface. The default for adding a SkRect i
s counter clockwise |
| 257 // beginning at index 0 (which happens to correspond to rrect index 0 or
7). |
| 258 if (!hasPathEffect) { |
| 259 // It doesn't matter what start we use, just be consistent to avoid
redundant keys. |
| 260 return kDefaultRRectStart; |
225 } | 261 } |
226 if (path.isRRect(rrect)) { | 262 // In SkPath a rect starts at index 0 by default. This is the top left c
orner. However, |
227 SkASSERT(!rrect->isEmpty()); | 263 // we store rects as rrects. RRects don't preserve the invertedness, but
rather sort the |
228 return Type::kRRect; | 264 // rect edges. Thus, we may need to modify the rrect's start index to ac
count for the sort. |
| 265 bool swapX = rect.fLeft > rect.fRight; |
| 266 bool swapY = rect.fTop > rect.fBottom; |
| 267 if (swapX && swapY) { |
| 268 // 0 becomes start index 2 and times 2 to convert from rect the rrec
t indices. |
| 269 return 2 * 2; |
| 270 } else if (swapX) { |
| 271 *dir = SkPath::kCCW_Direction; |
| 272 // 0 becomes start index 1 and times 2 to convert from rect the rrec
t indices. |
| 273 return 2 * 1; |
| 274 } else if (swapY) { |
| 275 *dir = SkPath::kCCW_Direction; |
| 276 // 0 becomes start index 3 and times 2 to convert from rect the rrec
t indices. |
| 277 return 2 * 3; |
229 } | 278 } |
230 SkRect rect; | 279 return 0; |
231 if (path.isOval(&rect)) { | 280 } |
232 rrect->setOval(rect); | 281 |
233 return Type::kRRect; | 282 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasP
athEffect, |
| 283 SkPath::Direction* dir) { |
| 284 // This comes from SkPath's interface. The default for adding a SkRRect
to a path is |
| 285 // clockwise beginning at starting index 6. |
| 286 static constexpr unsigned kPathRRectStartIdx = 6; |
| 287 *dir = kDefaultRRectDir; |
| 288 if (!hasPathEffect) { |
| 289 // It doesn't matter what start we use, just be consistent to avoid
redundant keys. |
| 290 return kDefaultRRectStart; |
234 } | 291 } |
235 bool closed; | 292 return kPathRRectStartIdx; |
236 if (path.isRect(&rect, &closed, nullptr)) { | |
237 if (closed || (!pe && strokeRec.isFillStyle())) { | |
238 rrect->setRect(rect); | |
239 return Type::kRRect; | |
240 } | |
241 } | |
242 return Type::kPath; | |
243 } | 293 } |
244 | 294 |
245 Type fType; | 295 Type fType; |
246 SkRRect fRRect; | 296 SkRRect fRRect; |
| 297 SkPath::Direction fRRectDir; |
| 298 unsigned fRRectStart; |
247 SkTLazy<SkPath> fPath; | 299 SkTLazy<SkPath> fPath; |
248 GrStyle fStyle; | 300 GrStyle fStyle; |
249 SkAutoSTArray<8, uint32_t> fInheritedKey; | 301 SkAutoSTArray<8, uint32_t> fInheritedKey; |
250 }; | 302 }; |
251 #endif | 303 #endif |
OLD | NEW |