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 "include/core/SkPath.h"
12#include "include/core/SkRRect.h"
13#include "include/private/SkTemplates.h"
14#include "src/core/SkPathPriv.h"
15#include "src/core/SkTLazy.h"
16#include "src/gpu/GrStyle.h"
17#include <new>
18
19class SkIDChangeListener;
20
21/**
22 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
23 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
24 * reflects the styling information (e.g. is stroked). It is also possible to apply just the
25 * path effect from the style. In this case the resulting shape will include any remaining
26 * stroking information that is to be applied after the path effect.
27 *
28 * Shapes can produce keys that represent only the geometry information, not the style. Note that
29 * when styling information is applied to produce a new shape then the style has been converted
30 * to geometric information and is included in the new shape's key. When the same style is applied
31 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
32 * will be the same.
33 *
34 * Currently this can only be constructed from a path, rect, or rrect though it can become a path
35 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
36 * that have fast paths in the GPU backend.
37 */
38class GrShape {
39public:
40 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
41 // to have to worry about this. This value is exposed for unit tests.
42 static constexpr int kMaxKeyFromDataVerbCnt = 10;
43
44 GrShape() { this->initType(Type::kEmpty); }
45
46 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
47
48 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
49
50 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
51
52 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
53 this->initType(Type::kPath, &path);
54 this->attemptToSimplifyPath();
55 }
56
57 GrShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) {
58 this->initType(Type::kRRect);
59 fRRectData.fRRect = rrect;
60 fRRectData.fInverted = false;
61 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
62 &fRRectData.fDir);
63 this->attemptToSimplifyRRect();
64 }
65
66 GrShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
67 const GrStyle& style)
68 : fStyle(style) {
69 this->initType(Type::kRRect);
70 fRRectData.fRRect = rrect;
71 fRRectData.fInverted = inverted;
72 if (style.pathEffect()) {
73 fRRectData.fDir = dir;
74 fRRectData.fStart = start;
75 if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
76 fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
77 } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
78 fRRectData.fStart &= 0b110;
79 }
80 } else {
81 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
82 }
83 this->attemptToSimplifyRRect();
84 }
85
86 GrShape(const SkRect& rect, const GrStyle& style) : fStyle(style) {
87 this->initType(Type::kRRect);
88 fRRectData.fRRect = SkRRect::MakeRect(rect);
89 fRRectData.fInverted = false;
90 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
91 &fRRectData.fDir);
92 this->attemptToSimplifyRRect();
93 }
94
95 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
96 this->initType(Type::kPath, &path);
97 this->attemptToSimplifyPath();
98 }
99
100 GrShape(const SkRRect& rrect, const SkPaint& paint) : 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) : fStyle(paint) {
110 this->initType(Type::kRRect);
111 fRRectData.fRRect = SkRRect::MakeRect(rect);
112 fRRectData.fInverted = false;
113 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
114 &fRRectData.fDir);
115 this->attemptToSimplifyRRect();
116 }
117
118 static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
119 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
120
121 GrShape(const GrShape&);
122 GrShape& operator=(const GrShape& that);
123
124 ~GrShape() { this->changeType(Type::kEmpty); }
125
126 /**
127 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
128 * version of the shape.
129 */
130 enum class FillInversion {
131 kPreserve,
132 kFlip,
133 kForceNoninverted,
134 kForceInverted
135 };
136 /**
137 * Makes a filled shape from the pre-styled original shape and optionally modifies whether
138 * the fill is inverted or not. It's important to note that the original shape's geometry
139 * may already have been modified if doing so was neutral with respect to its style
140 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
141 * made non-inverted since dashing ignores inverseness).
142 */
143 static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
144
145 const GrStyle& style() const { return fStyle; }
146
147 /**
148 * Returns a shape that has either applied the path effect or path effect and stroking
149 * information from this shape's style to its geometry. Scale is used when approximating the
150 * output geometry and typically is computed from the view matrix
151 */
152 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
153 return GrShape(*this, apply, scale);
154 }
155
156 bool isRect() const {
157 if (Type::kRRect != fType) {
158 return false;
159 }
160
161 return fRRectData.fRRect.isRect();
162 }
163
164 /** Returns the unstyled geometry as a rrect if possible. */
165 bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const {
166 if (Type::kRRect != fType) {
167 return false;
168 }
169 if (rrect) {
170 *rrect = fRRectData.fRRect;
171 }
172 if (dir) {
173 *dir = fRRectData.fDir;
174 }
175 if (start) {
176 *start = fRRectData.fStart;
177 }
178 if (inverted) {
179 *inverted = fRRectData.fInverted;
180 }
181 return true;
182 }
183
184 /**
185 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
186 * An inverse filled line path is still considered a line.
187 */
188 bool asLine(SkPoint pts[2], bool* inverted) const {
189 if (fType != Type::kLine) {
190 return false;
191 }
192 if (pts) {
193 pts[0] = fLineData.fPts[0];
194 pts[1] = fLineData.fPts[1];
195 }
196 if (inverted) {
197 *inverted = fLineData.fInverted;
198 }
199 return true;
200 }
201
202 /** Returns the unstyled geometry as a path. */
203 void asPath(SkPath* out) const {
204 switch (fType) {
205 case Type::kEmpty:
206 out->reset();
207 break;
208 case Type::kInvertedEmpty:
209 out->reset();
210 out->setFillType(kDefaultPathInverseFillType);
211 break;
212 case Type::kRRect:
213 out->reset();
214 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
215 // Below matches the fill type that attemptToSimplifyPath uses.
216 if (fRRectData.fInverted) {
217 out->setFillType(kDefaultPathInverseFillType);
218 } else {
219 out->setFillType(kDefaultPathFillType);
220 }
221 break;
222 case Type::kArc:
223 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
224 fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
225 fStyle.isSimpleFill());
226 if (fArcData.fInverted) {
227 out->setFillType(kDefaultPathInverseFillType);
228 } else {
229 out->setFillType(kDefaultPathFillType);
230 }
231 break;
232 case Type::kLine:
233 out->reset();
234 out->moveTo(fLineData.fPts[0]);
235 out->lineTo(fLineData.fPts[1]);
236 if (fLineData.fInverted) {
237 out->setFillType(kDefaultPathInverseFillType);
238 } else {
239 out->setFillType(kDefaultPathFillType);
240 }
241 break;
242 case Type::kPath:
243 *out = this->path();
244 break;
245 }
246 }
247
248 // Can this shape be drawn as a pair of filled nested rectangles?
249 bool asNestedRects(SkRect rects[2]) const {
250 if (Type::kPath != fType) {
251 return false;
252 }
253
254 // TODO: it would be better two store DRRects natively in the shape rather than converting
255 // them to a path and then reextracting the nested rects
256 if (this->path().isInverseFillType()) {
257 return false;
258 }
259
260 SkPathDirection dirs[2];
261 if (!SkPathPriv::IsNestedFillRects(this->path(), rects, dirs)) {
262 return false;
263 }
264
265 if (SkPathFillType::kWinding == this->path().getFillType() && dirs[0] == dirs[1]) {
266 // The two rects need to be wound opposite to each other
267 return false;
268 }
269
270 // Right now, nested rects where the margin is not the same width
271 // all around do not render correctly
272 const SkScalar* outer = rects[0].asScalars();
273 const SkScalar* inner = rects[1].asScalars();
274
275 bool allEq = true;
276
277 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
278 bool allGoE1 = margin >= SK_Scalar1;
279
280 for (int i = 1; i < 4; ++i) {
281 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
282 if (temp < SK_Scalar1) {
283 allGoE1 = false;
284 }
285 if (!SkScalarNearlyEqual(margin, temp)) {
286 allEq = false;
287 }
288 }
289
290 return allEq || allGoE1;
291 }
292
293 /**
294 * Returns whether the geometry is empty. Note that applying the style could produce a
295 * non-empty shape. It also may have an inverse fill.
296 */
297 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
298
299 /**
300 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
301 * the inverse fill nature of the geometry.
302 */
303 SkRect bounds() const;
304
305 /**
306 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
307 * status).
308 */
309 SkRect styledBounds() const;
310
311 /**
312 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
313 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
314 * This is because filling closes all contours in the path.
315 */
316 bool knownToBeConvex() const {
317 switch (fType) {
318 case Type::kEmpty:
319 return true;
320 case Type::kInvertedEmpty:
321 return true;
322 case Type::kRRect:
323 return true;
324 case Type::kArc:
325 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
326 SkToBool(fArcData.fUseCenter),
327 fStyle.isSimpleFill());
328 case Type::kLine:
329 return true;
330 case Type::kPath:
331 // SkPath.isConvex() really means "is this path convex were it to be closed" and
332 // thus doesn't give the correct answer for stroked paths, hence we also check
333 // whether the path is either filled or closed. Convex paths may only have one
334 // contour hence isLastContourClosed() is a sufficient for a convex path.
335 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
336 this->path().isConvex();
337 }
338 return false;
339 }
340
341 /**
342 * Does the shape have a known winding direction. Some degenerate convex shapes may not have
343 * a computable direction, but this is not always a requirement for path renderers so it is
344 * kept separate from knownToBeConvex().
345 */
346 bool knownDirection() const {
347 switch (fType) {
348 case Type::kEmpty:
349 return true;
350 case Type::kInvertedEmpty:
351 return true;
352 case Type::kRRect:
353 return true;
354 case Type::kArc:
355 return true;
356 case Type::kLine:
357 return true;
358 case Type::kPath:
359 // Assuming this is called after knownToBeConvex(), this should just be relying on
360 // cached convexity and direction and will be cheap.
361 return !SkPathPriv::CheapIsFirstDirection(this->path(),
362 SkPathPriv::kUnknown_FirstDirection);
363 }
364 return false;
365 }
366
367 /** Is the pre-styled geometry inverse filled? */
368 bool inverseFilled() const {
369 bool ret = false;
370 switch (fType) {
371 case Type::kEmpty:
372 ret = false;
373 break;
374 case Type::kInvertedEmpty:
375 ret = true;
376 break;
377 case Type::kRRect:
378 ret = fRRectData.fInverted;
379 break;
380 case Type::kArc:
381 ret = fArcData.fInverted;
382 break;
383 case Type::kLine:
384 ret = fLineData.fInverted;
385 break;
386 case Type::kPath:
387 ret = this->path().isInverseFillType();
388 break;
389 }
390 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
391 SkASSERT(!(ret && this->style().isDashed()));
392 return ret;
393 }
394
395 /**
396 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
397 * because an arbitrary path effect could produce an inverse filled path. In other cases this
398 * can be thought of as "inverseFilledAfterStyling()".
399 */
400 bool mayBeInverseFilledAfterStyling() const {
401 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
402 // filled.
403 if (this->style().hasNonDashPathEffect()) {
404 return true;
405 }
406 return this->inverseFilled();
407 }
408
409 /**
410 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
411 * not have any caps if stroked (modulo the effect of any path effect).
412 */
413 bool knownToBeClosed() const {
414 switch (fType) {
415 case Type::kEmpty:
416 return true;
417 case Type::kInvertedEmpty:
418 return true;
419 case Type::kRRect:
420 return true;
421 case Type::kArc:
422 return fArcData.fUseCenter;
423 case Type::kLine:
424 return false;
425 case Type::kPath:
426 // SkPath doesn't keep track of the closed status of each contour.
427 return SkPathPriv::IsClosedSingleContour(this->path());
428 }
429 return false;
430 }
431
432 uint32_t segmentMask() const {
433 switch (fType) {
434 case Type::kEmpty:
435 return 0;
436 case Type::kInvertedEmpty:
437 return 0;
438 case Type::kRRect:
439 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
440 return SkPath::kConic_SegmentMask;
441 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
442 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
443 return SkPath::kLine_SegmentMask;
444 }
445 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
446 case Type::kArc:
447 if (fArcData.fUseCenter) {
448 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
449 }
450 return SkPath::kConic_SegmentMask;
451 case Type::kLine:
452 return SkPath::kLine_SegmentMask;
453 case Type::kPath:
454 return this->path().getSegmentMasks();
455 }
456 return 0;
457 }
458
459 /**
460 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
461 * A negative value is returned if the shape has no key (shouldn't be cached).
462 */
463 int unstyledKeySize() const;
464
465 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
466
467 /**
468 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
469 * space allocated for the key and that unstyledKeySize() does not return a negative value
470 * for this shape.
471 */
472 void writeUnstyledKey(uint32_t* key) const;
473
474 /**
475 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
476 * a path is no longer in-use. If the shape started out as something other than a path, this
477 * does nothing.
478 */
479 void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const;
480
481 /**
482 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
483 * the generation ID of the *original* path. This is the path that will receive
484 * GenIDChangeListeners added to this shape.
485 */
486 uint32_t testingOnly_getOriginalGenerationID() const;
487 bool testingOnly_isPath() const;
488 bool testingOnly_isNonVolatilePath() const;
489
490private:
491 enum class Type {
492 kEmpty,
493 kInvertedEmpty,
494 kRRect,
495 kArc,
496 kLine,
497 kPath,
498 };
499
500 void initType(Type type, const SkPath* path = nullptr) {
501 fType = Type::kEmpty;
502 this->changeType(type, path);
503 }
504
505 void changeType(Type type, const SkPath* path = nullptr) {
506 bool wasPath = Type::kPath == fType;
507 fType = type;
508 bool isPath = Type::kPath == type;
509 SkASSERT(!path || isPath);
510 if (wasPath && !isPath) {
511 fPathData.fPath.~SkPath();
512 } else if (!wasPath && isPath) {
513 if (path) {
514 new (&fPathData.fPath) SkPath(*path);
515 } else {
516 new (&fPathData.fPath) SkPath();
517 }
518 } else if (isPath && path) {
519 fPathData.fPath = *path;
520 }
521 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
522 fPathData.fGenID = 0;
523 }
524
525 SkPath& path() {
526 SkASSERT(Type::kPath == fType);
527 return fPathData.fPath;
528 }
529
530 const SkPath& path() const {
531 SkASSERT(Type::kPath == fType);
532 return fPathData.fPath;
533 }
534
535 /** Constructor used by the applyStyle() function */
536 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
537
538 /**
539 * Determines the key we should inherit from the input shape's geometry and style when
540 * we are applying the style to create a new shape.
541 */
542 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
543
544 void attemptToSimplifyPath();
545 void attemptToSimplifyRRect();
546 void attemptToSimplifyLine();
547 void attemptToSimplifyArc();
548
549 bool attemptToSimplifyStrokedLineToRRect();
550
551 /** Gets the path that gen id listeners should be added to. */
552 const SkPath* originalPathForListeners() const;
553
554 // Defaults to use when there is no distinction between even/odd and winding fills.
555 static constexpr SkPathFillType kDefaultPathFillType = SkPathFillType::kEvenOdd;
556 static constexpr SkPathFillType kDefaultPathInverseFillType = SkPathFillType::kInverseEvenOdd;
557
558 static constexpr SkPathDirection kDefaultRRectDir = SkPathDirection::kCW;
559 static constexpr unsigned kDefaultRRectStart = 0;
560
561 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
562 SkPathDirection* dir) {
563 *dir = kDefaultRRectDir;
564 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
565 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
566 if (!hasPathEffect) {
567 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
568 return kDefaultRRectStart;
569 }
570 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
571 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
572 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
573 bool swapX = rect.fLeft > rect.fRight;
574 bool swapY = rect.fTop > rect.fBottom;
575 if (swapX && swapY) {
576 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
577 return 2 * 2;
578 } else if (swapX) {
579 *dir = SkPathDirection::kCCW;
580 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
581 return 2 * 1;
582 } else if (swapY) {
583 *dir = SkPathDirection::kCCW;
584 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
585 return 2 * 3;
586 }
587 return 0;
588 }
589
590 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
591 SkPathDirection* dir) {
592 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
593 // clockwise beginning at starting index 6.
594 static constexpr unsigned kPathRRectStartIdx = 6;
595 *dir = kDefaultRRectDir;
596 if (!hasPathEffect) {
597 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
598 return kDefaultRRectStart;
599 }
600 return kPathRRectStartIdx;
601 }
602
603 union {
604 struct {
605 SkRRect fRRect;
606 SkPathDirection fDir;
607 unsigned fStart;
608 bool fInverted;
609 } fRRectData;
610 struct {
611 SkRect fOval;
612 SkScalar fStartAngleDegrees;
613 SkScalar fSweepAngleDegrees;
614 int16_t fUseCenter;
615 int16_t fInverted;
616 } fArcData;
617 struct {
618 SkPath fPath;
619 // Gen ID of the original path (fPath may be modified)
620 int32_t fGenID;
621 } fPathData;
622 struct {
623 SkPoint fPts[2];
624 bool fInverted;
625 } fLineData;
626 };
627 GrStyle fStyle;
628 SkTLazy<SkPath> fInheritedPathForListeners;
629 SkAutoSTArray<8, uint32_t> fInheritedKey;
630 Type fType;
631};
632#endif
633