• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  #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 (!this->path().isNestedFillRects(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      /** Is the pre-styled geometry inverse filled? */
inverseFilled()340      bool inverseFilled() const {
341          bool ret = false;
342          switch (fType) {
343              case Type::kEmpty:
344                  ret = false;
345                  break;
346              case Type::kInvertedEmpty:
347                  ret = true;
348                  break;
349              case Type::kRRect:
350                  ret = fRRectData.fInverted;
351                  break;
352              case Type::kArc:
353                  ret = fArcData.fInverted;
354                  break;
355              case Type::kLine:
356                  ret = fLineData.fInverted;
357                  break;
358              case Type::kPath:
359                  ret = this->path().isInverseFillType();
360                  break;
361          }
362          // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
363          SkASSERT(!(ret && this->style().isDashed()));
364          return ret;
365      }
366  
367      /**
368       * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
369       * because an arbitrary path effect could produce an inverse filled path. In other cases this
370       * can be thought of as "inverseFilledAfterStyling()".
371       */
mayBeInverseFilledAfterStyling()372      bool mayBeInverseFilledAfterStyling() const {
373           // An arbitrary path effect can produce an arbitrary output path, which may be inverse
374           // filled.
375          if (this->style().hasNonDashPathEffect()) {
376              return true;
377          }
378          return this->inverseFilled();
379      }
380  
381      /**
382       * Is it known that the unstyled geometry has no unclosed contours. This means that it will
383       * not have any caps if stroked (modulo the effect of any path effect).
384       */
knownToBeClosed()385      bool knownToBeClosed() const {
386          switch (fType) {
387              case Type::kEmpty:
388                  return true;
389              case Type::kInvertedEmpty:
390                  return true;
391              case Type::kRRect:
392                  return true;
393              case Type::kArc:
394                  return fArcData.fUseCenter;
395              case Type::kLine:
396                  return false;
397              case Type::kPath:
398                  // SkPath doesn't keep track of the closed status of each contour.
399                  return SkPathPriv::IsClosedSingleContour(this->path());
400          }
401          return false;
402      }
403  
segmentMask()404      uint32_t segmentMask() const {
405          switch (fType) {
406              case Type::kEmpty:
407                  return 0;
408              case Type::kInvertedEmpty:
409                  return 0;
410              case Type::kRRect:
411                  if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
412                      return SkPath::kConic_SegmentMask;
413                  } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
414                             fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
415                      return SkPath::kLine_SegmentMask;
416                  }
417                  return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
418              case Type::kArc:
419                  if (fArcData.fUseCenter) {
420                      return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
421                  }
422                  return SkPath::kConic_SegmentMask;
423              case Type::kLine:
424                  return SkPath::kLine_SegmentMask;
425              case Type::kPath:
426                  return this->path().getSegmentMasks();
427          }
428          return 0;
429      }
430  
431      /**
432       * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
433       * A negative value is returned if the shape has no key (shouldn't be cached).
434       */
435      int unstyledKeySize() const;
436  
hasUnstyledKey()437      bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
438  
439      /**
440       * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
441       * space allocated for the key and that unstyledKeySize() does not return a negative value
442       * for this shape.
443       */
444      void writeUnstyledKey(uint32_t* key) const;
445  
446      /**
447       * Adds a listener to the *original* path. Typically used to invalidate cached resources when
448       * a path is no longer in-use. If the shape started out as something other than a path, this
449       * does nothing.
450       */
451      void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
452  
453      /**
454       * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
455       * the generation ID of the *original* path. This is the path that will receive
456       * GenIDChangeListeners added to this shape.
457       */
458      uint32_t testingOnly_getOriginalGenerationID() const;
459      bool testingOnly_isPath() const;
460      bool testingOnly_isNonVolatilePath() const;
461  
462  private:
463      enum class Type {
464          kEmpty,
465          kInvertedEmpty,
466          kRRect,
467          kArc,
468          kLine,
469          kPath,
470      };
471  
472      void initType(Type type, const SkPath* path = nullptr) {
473          fType = Type::kEmpty;
474          this->changeType(type, path);
475      }
476  
477      void changeType(Type type, const SkPath* path = nullptr) {
478          bool wasPath = Type::kPath == fType;
479          fType = type;
480          bool isPath = Type::kPath == type;
481          SkASSERT(!path || isPath);
482          if (wasPath && !isPath) {
483              fPathData.fPath.~SkPath();
484          } else if (!wasPath && isPath) {
485              if (path) {
486                  new (&fPathData.fPath) SkPath(*path);
487              } else {
488                  new (&fPathData.fPath) SkPath();
489              }
490          } else if (isPath && path) {
491              fPathData.fPath = *path;
492          }
493          // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
494          fPathData.fGenID = 0;
495      }
496  
path()497      SkPath& path() {
498          SkASSERT(Type::kPath == fType);
499          return fPathData.fPath;
500      }
501  
path()502      const SkPath& path() const {
503          SkASSERT(Type::kPath == fType);
504          return fPathData.fPath;
505      }
506  
507      /** Constructor used by the applyStyle() function */
508      GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
509  
510      /**
511       * Determines the key we should inherit from the input shape's geometry and style when
512       * we are applying the style to create a new shape.
513       */
514      void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
515  
516      void attemptToSimplifyPath();
517      void attemptToSimplifyRRect();
518      void attemptToSimplifyLine();
519      void attemptToSimplifyArc();
520  
521      bool attemptToSimplifyStrokedLineToRRect();
522  
523      /** Gets the path that gen id listeners should be added to. */
524      const SkPath* originalPathForListeners() const;
525  
526      // Defaults to use when there is no distinction between even/odd and winding fills.
527      static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
528      static constexpr SkPath::FillType kDefaultPathInverseFillType =
529              SkPath::kInverseEvenOdd_FillType;
530  
531      static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
532      static constexpr unsigned kDefaultRRectStart = 0;
533  
DefaultRectDirAndStartIndex(const SkRect & rect,bool hasPathEffect,SkPath::Direction * dir)534      static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
535                                                  SkPath::Direction* dir) {
536          *dir = kDefaultRRectDir;
537          // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
538          // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
539          if (!hasPathEffect) {
540              // It doesn't matter what start we use, just be consistent to avoid redundant keys.
541              return kDefaultRRectStart;
542          }
543          // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
544          // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
545          // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
546          bool swapX = rect.fLeft > rect.fRight;
547          bool swapY = rect.fTop > rect.fBottom;
548          if (swapX && swapY) {
549              // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
550              return 2 * 2;
551          } else if (swapX) {
552              *dir = SkPath::kCCW_Direction;
553              // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
554              return 2 * 1;
555          } else if (swapY) {
556              *dir = SkPath::kCCW_Direction;
557              // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
558              return 2 * 3;
559          }
560          return 0;
561      }
562  
DefaultRRectDirAndStartIndex(const SkRRect & rrect,bool hasPathEffect,SkPath::Direction * dir)563      static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
564                                                   SkPath::Direction* dir) {
565          // This comes from SkPath's interface. The default for adding a SkRRect to a path is
566          // clockwise beginning at starting index 6.
567          static constexpr unsigned kPathRRectStartIdx = 6;
568          *dir = kDefaultRRectDir;
569          if (!hasPathEffect) {
570              // It doesn't matter what start we use, just be consistent to avoid redundant keys.
571              return kDefaultRRectStart;
572          }
573          return kPathRRectStartIdx;
574      }
575  
576      union {
577          struct {
578              SkRRect fRRect;
579              SkPath::Direction fDir;
580              unsigned fStart;
581              bool fInverted;
582          } fRRectData;
583          struct {
584              SkRect fOval;
585              SkScalar fStartAngleDegrees;
586              SkScalar fSweepAngleDegrees;
587              int16_t fUseCenter;
588              int16_t fInverted;
589          } fArcData;
590          struct {
591              SkPath fPath;
592              // Gen ID of the original path (fPath may be modified)
593              int32_t fGenID;
594          } fPathData;
595          struct {
596              SkPoint fPts[2];
597              bool fInverted;
598          } fLineData;
599      };
600      GrStyle fStyle;
601      SkTLazy<SkPath> fInheritedPathForListeners;
602      SkAutoSTArray<8, uint32_t>  fInheritedKey;
603      Type fType;
604  };
605  #endif
606