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