text: /*
   * Copyright 2016 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #ifndef GrStyledShape_DEFINED
  #define GrStyledShape_DEFINED
  
  #include "include/core/SkPath.h"
  #include "include/core/SkRRect.h"
  #include "include/private/SkTemplates.h"
  #include "src/core/SkPathPriv.h"
  #include "src/core/SkTLazy.h"
  #include "src/gpu/GrStyle.h"
  #include "src/gpu/geometry/GrShape.h"
  #include <new>
  
  class SkIDChangeListener;
  
  /**
   * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
   * It is possible to apply the style to the GrStyledShape to produce a new GrStyledShape where the
   * geometry reflects the styling information (e.g. is stroked). It is also possible to apply just
   * the path effect from the style. In this case the resulting shape will include any remaining
   * stroking information that is to be applied after the path effect.
   *
   * Shapes can produce keys that represent only the geometry information, not the style. Note that
   * when styling information is applied to produce a new shape then the style has been converted
   * to geometric information and is included in the new shape's key. When the same style is applied
   * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
   * will be the same.
   *
   * Currently this can only be constructed from a path, rect, or rrect though it can become a path
   * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
   * that have fast paths in the GPU backend.
   */
  class GrStyledShape {
  public:
      // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
      // to have to worry about this. This value is exposed for unit tests.
      inline static constexpr int kMaxKeyFromDataVerbCnt = 10;
  
      GrStyledShape() {}
  
      enum class DoSimplify : bool { kNo = false, kYes };
  
      explicit GrStyledShape(const SkPath& path, DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(path, GrStyle::SimpleFill(), doSimplify) {}
  
      explicit GrStyledShape(const SkRRect& rrect, DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(rrect, GrStyle::SimpleFill(), doSimplify) {}
  
      explicit GrStyledShape(const SkRect& rect, DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(rect, GrStyle::SimpleFill(), doSimplify) {}
  
      GrStyledShape(const SkPath& path, const SkPaint& paint,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(path, GrStyle(paint), doSimplify) {}
  
      GrStyledShape(const SkRRect& rrect, const SkPaint& paint,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(rrect, GrStyle(paint), doSimplify) {}
  
      GrStyledShape(const SkRect& rect, const SkPaint& paint,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : GrStyledShape(rect, GrStyle(paint), doSimplify) {}
  
      GrStyledShape(const SkPath& path, const GrStyle& style,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : fShape(path), fStyle(style) {
          if (doSimplify == DoSimplify::kYes) {
              this->simplify();
          }
      }
  
      GrStyledShape(const SkRRect& rrect, const GrStyle& style,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : fShape(rrect), fStyle(style) {
          if (doSimplify == DoSimplify::kYes) {
              this->simplify();
          }
      }
  
      GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
                    const GrStyle& style, DoSimplify doSimplify = DoSimplify::kYes)
              : fShape(rrect)
              , fStyle(style) {
          fShape.setPathWindingParams(dir, start);
          fShape.setInverted(inverted);
          if (doSimplify == DoSimplify::kYes) {
              this->simplify();
          }
      }
  
      GrStyledShape(const SkRect& rect, const GrStyle& style,
                    DoSimplify doSimplify = DoSimplify::kYes)
              : fShape(rect), fStyle(style) {
          if (doSimplify == DoSimplify::kYes) {
              this->simplify();
          }
      }
  
      GrStyledShape(const GrStyledShape&);
  
      static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
                                   SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style,
                                   DoSimplify = DoSimplify::kYes);
  
      GrStyledShape& operator=(const GrStyledShape& that);
  
      /**
       * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
       * version of the shape.
       */
      enum class FillInversion {
          kPreserve,
          kFlip,
          kForceNoninverted,
          kForceInverted
      };
      /**
       * Makes a filled shape from the pre-styled original shape and optionally modifies whether
       * the fill is inverted or not. It's important to note that the original shape's geometry
       * may already have been modified if doing so was neutral with respect to its style
       * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
       * made non-inverted since dashing ignores inverseness).
       */
      static GrStyledShape MakeFilled(const GrStyledShape& original,
                                      FillInversion = FillInversion::kPreserve);
  
      const GrStyle& style() const { return fStyle; }
  
      // True if the shape and/or style were modified into a simpler, equivalent pairing
      bool simplified() const { return fSimplified; }
  
      /**
       * Returns a shape that has either applied the path effect or path effect and stroking
       * information from this shape's style to its geometry. Scale is used when approximating the
       * output geometry and typically is computed from the view matrix
       */
      GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
          return GrStyledShape(*this, apply, scale);
      }
  
      bool isRect() const {
          // Should have simplified a rrect to a rect if possible already.
          SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect());
          return fShape.isRect();
      }
  
      /** Returns the unstyled geometry as a rrect if possible. */
      bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const;
  
      /**
       * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
       * An inverse filled line path is still considered a line.
       */
      bool asLine(SkPoint pts[2], bool* inverted) const;
  
      // Can this shape be drawn as a pair of filled nested rectangles?
      bool asNestedRects(SkRect rects[2]) const;
  
      /** Returns the unstyled geometry as a path. */
      void asPath(SkPath* out) const {
          fShape.asPath(out, fStyle.isSimpleFill());
      }
  
      /**
       * Returns whether the geometry is empty. Note that applying the style could produce a
       * non-empty shape. It also may have an inverse fill.
       */
      bool isEmpty() const { return fShape.isEmpty(); }
  
      /**
       * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
       * the inverse fill nature of the geometry.
       */
      SkRect bounds() const { return fShape.bounds(); }
  
      /**
       * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
       * status).
       */
      SkRect styledBounds() const;
  
      /**
       * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
       * convex path is considered to be closed if they styling reflects a fill and not otherwise.
       * This is because filling closes all contours in the path.
       */
      bool knownToBeConvex() const {
          return fShape.convex(fStyle.isSimpleFill());
      }
  
      /**
       * Does the shape have a known winding direction. Some degenerate convex shapes may not have
       * a computable direction, but this is not always a requirement for path renderers so it is
       * kept separate from knownToBeConvex().
       */
      bool knownDirection() const {
          // Assuming this is called after knownToBeConvex(), this should just be relying on
          // cached convexity and direction and will be cheap.
          return !fShape.isPath() ||
                 SkPathPriv::ComputeFirstDirection(fShape.path()) != SkPathFirstDirection::kUnknown;
      }
  
      /** Is the pre-styled geometry inverse filled? */
      bool inverseFilled() const {
          // Since the path tracks inverted-fillness itself, it should match what was recorded.
          SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType());
          // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
          SkASSERT(!(fShape.inverted() && this->style().isDashed()));
          return fShape.inverted();
      }
  
      /**
       * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
       * because an arbitrary path effect could produce an inverse filled path. In other cases this
       * can be thought of as "inverseFilledAfterStyling()".
       */
      bool mayBeInverseFilledAfterStyling() const {
           // An arbitrary path effect can produce an arbitrary output path, which may be inverse
           // filled.
          if (this->style().hasNonDashPathEffect()) {
              return true;
          }
          return this->inverseFilled();
      }
  
      /**
       * Is it known that the unstyled geometry has no unclosed contours. This means that it will
       * not have any caps if stroked (modulo the effect of any path effect).
       */
      bool knownToBeClosed() const {
          // This refers to the base shape and does not depend on invertedness.
          return fShape.closed();
      }
  
      uint32_t segmentMask() const {
          // This refers to the base shape and does not depend on invertedness.
          return fShape.segmentMask();
      }
  
      /**
       * Gets the size of the key for the shape represented by this GrStyledShape (ignoring its
       * styling). A negative value is returned if the shape has no key (shouldn't be cached).
       */
      int unstyledKeySize() const;
  
      bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
  
      /**
       * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
       * space allocated for the key and that unstyledKeySize() does not return a negative value
       * for this shape.
       */
      void writeUnstyledKey(uint32_t* key) const;
  
      /**
       * Adds a listener to the *original* path. Typically used to invalidate cached resources when
       * a path is no longer in-use. If the shape started out as something other than a path, this
       * does nothing.
       */
      void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const;
  
      /**
       * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
       * the generation ID of the *original* path. This is the path that will receive
       * GenIDChangeListeners added to this shape.
       */
      uint32_t testingOnly_getOriginalGenerationID() const;
      bool testingOnly_isPath() const;
      bool testingOnly_isNonVolatilePath() const;
  
      /**
       * Similar to GrShape::simplify but also takes into account style and stroking, possibly
       * applying the style explicitly to produce a new analytic shape with a simpler style.
       * Unless "doSimplify" is kNo, this method gets called automatically during construction.
       */
      void simplify();
  
  private:
      /** Constructor used by the applyStyle() function */
      GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
  
      /**
       * Determines the key we should inherit from the input shape's geometry and style when
       * we are applying the style to create a new shape.
       */
      void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
  
      /**
       * As part of the simplification process, some shapes can have stroking trivially evaluated
       * and form a new geometry with just a fill.
       */
      void simplifyStroke();
  
      /** Gets the path that gen id listeners should be added to. */
      const SkPath* originalPathForListeners() const;
  
      GrShape fShape;
      GrStyle fStyle;
      // Gen ID of the original path (path may be modified or simplified away).
      int32_t fGenID      = 0;
      bool    fClosed     = false;
      bool    fSimplified = false;
  
      SkTLazy<SkPath>            fInheritedPathForListeners;
      SkAutoSTArray<8, uint32_t> fInheritedKey;
  };
  #endif
  