Line data Source code
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 : */
35 : class GrShape {
36 : public:
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 0 : 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 0 : GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
50 0 : this->initType(Type::kPath, &path);
51 0 : this->attemptToSimplifyPath();
52 0 : }
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 0 : ~GrShape() { this->changeType(Type::kEmpty); }
123 :
124 0 : 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 0 : GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
132 0 : 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 0 : bool asLine(SkPoint pts[2], bool* inverted) const {
160 0 : if (fType != Type::kLine) {
161 0 : return false;
162 : }
163 0 : if (pts) {
164 0 : pts[0] = fLineData.fPts[0];
165 0 : pts[1] = fLineData.fPts[1];
166 : }
167 0 : if (inverted) {
168 0 : *inverted = fLineData.fInverted;
169 : }
170 0 : return true;
171 : }
172 :
173 : /** Returns the unstyled geometry as a path. */
174 0 : void asPath(SkPath* out) const {
175 0 : switch (fType) {
176 : case Type::kEmpty:
177 0 : out->reset();
178 0 : break;
179 : case Type::kRRect:
180 0 : out->reset();
181 0 : out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
182 : // Below matches the fill type that attemptToSimplifyPath uses.
183 0 : if (fRRectData.fInverted) {
184 0 : out->setFillType(kDefaultPathInverseFillType);
185 : } else {
186 0 : out->setFillType(kDefaultPathFillType);
187 : }
188 0 : break;
189 : case Type::kLine:
190 0 : out->reset();
191 0 : out->moveTo(fLineData.fPts[0]);
192 0 : out->lineTo(fLineData.fPts[1]);
193 0 : if (fLineData.fInverted) {
194 0 : out->setFillType(kDefaultPathInverseFillType);
195 : } else {
196 0 : out->setFillType(kDefaultPathFillType);
197 : }
198 0 : break;
199 : case Type::kPath:
200 0 : *out = this->path();
201 0 : break;
202 : }
203 0 : }
204 :
205 : /**
206 : * Returns whether the geometry is empty. Note that applying the style could produce a
207 : * non-empty shape.
208 : */
209 0 : 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 0 : bool knownToBeConvex() const {
229 0 : switch (fType) {
230 : case Type::kEmpty:
231 0 : return true;
232 : case Type::kRRect:
233 0 : return true;
234 : case Type::kLine:
235 0 : 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 0 : return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
242 0 : this->path().isConvex();
243 : }
244 0 : return false;
245 : }
246 :
247 : /** Is the pre-styled geometry inverse filled? */
248 0 : bool inverseFilled() const {
249 0 : bool ret = false;
250 0 : switch (fType) {
251 : case Type::kEmpty:
252 0 : ret = false;
253 0 : break;
254 : case Type::kRRect:
255 0 : ret = fRRectData.fInverted;
256 0 : break;
257 : case Type::kLine:
258 0 : ret = fLineData.fInverted;
259 0 : break;
260 : case Type::kPath:
261 0 : ret = this->path().isInverseFillType();
262 0 : break;
263 : }
264 : // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
265 0 : SkASSERT(!(ret && this->style().isDashed()));
266 0 : 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 0 : bool mayBeInverseFilledAfterStyling() const {
275 : // An arbitrary path effect can produce an arbitrary output path, which may be inverse
276 : // filled.
277 0 : if (this->style().hasNonDashPathEffect()) {
278 0 : return true;
279 : }
280 0 : 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 0 : bool knownToBeClosed() const {
288 0 : switch (fType) {
289 : case Type::kEmpty:
290 0 : return true;
291 : case Type::kRRect:
292 0 : return true;
293 : case Type::kLine:
294 0 : return false;
295 : case Type::kPath:
296 : // SkPath doesn't keep track of the closed status of each contour.
297 0 : return SkPathPriv::IsClosedSingleContour(this->path());
298 : }
299 0 : return false;
300 : }
301 :
302 0 : uint32_t segmentMask() const {
303 0 : switch (fType) {
304 : case Type::kEmpty:
305 0 : return 0;
306 : case Type::kRRect:
307 0 : if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
308 0 : return SkPath::kConic_SegmentMask;
309 0 : } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
310 0 : return SkPath::kLine_SegmentMask;
311 : }
312 0 : return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
313 : case Type::kLine:
314 0 : return SkPath::kLine_SegmentMask;
315 : case Type::kPath:
316 0 : return this->path().getSegmentMasks();
317 : }
318 0 : 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 0 : 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 :
336 : private:
337 : enum class Type {
338 : kEmpty,
339 : kRRect,
340 : kLine,
341 : kPath,
342 : };
343 :
344 0 : void initType(Type type, const SkPath* path = nullptr) {
345 0 : fType = Type::kEmpty;
346 0 : this->changeType(type, path);
347 0 : }
348 :
349 0 : void changeType(Type type, const SkPath* path = nullptr) {
350 0 : bool wasPath = Type::kPath == fType;
351 0 : fType = type;
352 0 : bool isPath = Type::kPath == type;
353 0 : SkASSERT(!path || isPath);
354 0 : if (wasPath && !isPath) {
355 0 : fPathData.fPath.~SkPath();
356 0 : } else if (!wasPath && isPath) {
357 0 : if (path) {
358 0 : new (&fPathData.fPath) SkPath(*path);
359 : } else {
360 0 : new (&fPathData.fPath) SkPath();
361 : }
362 0 : } else if (isPath && path) {
363 0 : fPathData.fPath = *path;
364 : }
365 : // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
366 0 : fPathData.fGenID = 0;
367 0 : }
368 :
369 0 : SkPath& path() {
370 0 : SkASSERT(Type::kPath == fType);
371 0 : return fPathData.fPath;
372 : }
373 :
374 0 : const SkPath& path() const {
375 0 : SkASSERT(Type::kPath == fType);
376 0 : 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
|