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 class 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  */
38 class GrShape {
39 public:
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 
GrShape()44     GrShape() { this->initType(Type::kEmpty); }
45 
GrShape(const SkPath & path)46     explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
47 
GrShape(const SkRRect & rrect)48     explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
49 
GrShape(const SkRect & rect)50     explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
51 
GrShape(const SkPath & path,const GrStyle & style)52     GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
53         this->initType(Type::kPath, &path);
54         this->attemptToSimplifyPath();
55     }
56 
GrShape(const SkRRect & rrect,const GrStyle & style)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 
GrShape(const SkRRect & rrect,SkPathDirection dir,unsigned start,bool inverted,const GrStyle & style)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 
GrShape(const SkRect & rect,const GrStyle & style)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 
GrShape(const SkPath & path,const SkPaint & paint)95     GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
96         this->initType(Type::kPath, &path);
97         this->attemptToSimplifyPath();
98     }
99 
GrShape(const SkRRect & rrect,const SkPaint & paint)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 
GrShape(const SkRect & rect,const SkPaint & paint)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 
~GrShape()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 
style()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      */
applyStyle(GrStyle::Apply apply,SkScalar scale)152     GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
153         return GrShape(*this, apply, scale);
154     }
155 
isRect()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. */
asRRect(SkRRect * rrect,SkPathDirection * dir,unsigned * start,bool * inverted)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      */
asLine(SkPoint pts[2],bool * inverted)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. */
asPath(SkPath * out)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?
asNestedRects(SkRect rects[2])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      */
isEmpty()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      */
knownToBeConvex()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      */
knownDirection()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? */
inverseFilled()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      */
mayBeInverseFilledAfterStyling()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      */
knownToBeClosed()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 
segmentMask()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 
hasUnstyledKey()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 
490 private:
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 
path()525     SkPath& path() {
526         SkASSERT(Type::kPath == fType);
527         return fPathData.fPath;
528     }
529 
path()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 
DefaultRectDirAndStartIndex(const SkRect & rect,bool hasPathEffect,SkPathDirection * dir)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 
DefaultRRectDirAndStartIndex(const SkRRect & rrect,bool hasPathEffect,SkPathDirection * dir)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