| /* |
| * Copyright 2020 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrShape_DEFINED |
| #define GrShape_DEFINED |
| |
| #include "include/core/SkPath.h" |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkRRect.h" |
| #include "include/core/SkRect.h" |
| |
| // Represents an arc along an oval boundary, or a closed wedge of the oval. |
| struct GrArc { |
| SkRect fOval; // The sorted, bounding box defining the oval the arc is traced along |
| SkScalar fStartAngle; // In degrees |
| SkScalar fSweepAngle; // In degrees |
| bool fUseCenter; // True if the arc includes the center point of the oval |
| }; |
| |
| // Represents a line segment between two points. |
| struct GrLineSegment { |
| SkPoint fP1; |
| SkPoint fP2; |
| }; |
| |
| /** |
| * GrShape is a convenience class to represent the many different specialized geometries that |
| * Ganesh can handle, including rects, round rects, lines, as well as paths. It is intended as |
| * a data-only class where any additional complex behavior is handled by an owning type (e.g. |
| * GrStyledShape). However, it does include some basic utilities that unify common functionality |
| * (such as contains()) from the underlying shape types. |
| * |
| * In order to have lossless simplification of the geometry, it also tracks winding direction, start |
| * index, and fill inversion. The direction and index are match the SkPath indexing scheme for |
| * the shape's type (e.g. rect, rrect, or oval). |
| * |
| * Regarding GrShape's empty shape: |
| * - GrShape uses empty to refer to the absence of any geometric data |
| * - SkRect::isEmpty() returns true if the rect is not sorted, even if it has area. GrShape will not |
| * simplify these shapes to an empty GrShape. Rects with actual 0 width and height will simplify |
| * to a point or line, not empty. This is to preserve geometric data for path effects and strokes. |
| * - SkRRect::isEmpty() is true when the bounds have 0 width or height, so GrShape will simplify it |
| * to a point or line, just like a rect. SkRRect does not have the concept of unsorted edges. |
| */ |
| class GrShape { |
| public: |
| // The current set of types GrShape can represent directly |
| enum class Type : uint8_t { |
| kEmpty, kPoint, kRect, kRRect, kPath, kArc, kLine |
| }; |
| inline static constexpr int kTypeCount = static_cast<int>(Type::kLine) + 1; |
| |
| // The direction and start index used when a shape does not have a representable winding, |
| // or when that information was discarded during simplification (kIgnoreWinding_Flag). |
| inline static constexpr SkPathDirection kDefaultDir = SkPathDirection::kCW; |
| inline static constexpr unsigned kDefaultStart = 0; |
| // The fill rule that is used by asPath() for shapes that aren't already a path. |
| inline static constexpr SkPathFillType kDefaultFillType = SkPathFillType::kEvenOdd; |
| |
| GrShape() {} |
| explicit GrShape(const SkPoint& point) { this->setPoint(point); } |
| explicit GrShape(const SkRect& rect) { this->setRect(rect); } |
| explicit GrShape(const SkRRect& rrect) { this->setRRect(rrect); } |
| explicit GrShape(const SkPath& path) { this->setPath(path); } |
| explicit GrShape(const GrArc& arc) { this->setArc(arc); } |
| explicit GrShape(const GrLineSegment& line){ this->setLine(line); } |
| |
| GrShape(const GrShape& shape) { *this = shape; } |
| |
| ~GrShape() { this->reset(); } |
| |
| // NOTE: None of the geometry types benefit from move semantics, so we don't bother |
| // defining a move assignment operator for GrShape. |
| GrShape& operator=(const GrShape& shape); |
| |
| // These type queries reflect the shape type provided when assigned, it does not incorporate |
| // any potential simplification (e.g. if isRRect() is true and rrect().isRect() is true, |
| // isRect() will still be false, until simplify() is called). |
| bool isEmpty() const { return this->type() == Type::kEmpty; } |
| bool isPoint() const { return this->type() == Type::kPoint; } |
| bool isRect() const { return this->type() == Type::kRect; } |
| bool isRRect() const { return this->type() == Type::kRRect; } |
| bool isPath() const { return this->type() == Type::kPath; } |
| bool isArc() const { return this->type() == Type::kArc; } |
| bool isLine() const { return this->type() == Type::kLine; } |
| |
| Type type() const { return fType; } |
| |
| // Report the shape type, winding direction, start index, and invertedness as a value suitable |
| // for use in a resource key. This does not include any geometry coordinates into the key value. |
| uint32_t stateKey() const; |
| |
| // Whether or not the shape is meant to be the inverse of its geometry (i.e. its exterior). |
| bool inverted() const { |
| return this->isPath() ? fPath.isInverseFillType() : SkToBool(fInverted); |
| } |
| |
| // Returns the path direction extracted from the path during simplification, if the shape's |
| // type represents a rrect, rect, or oval. |
| SkPathDirection dir() const { return fCW ? SkPathDirection::kCW : SkPathDirection::kCCW; } |
| // Returns the start index extracted from the path during simplification, if the shape's |
| // type represents a rrect, rect, or oval. |
| unsigned startIndex() const { return fStart; } |
| |
| // Override the direction and start parameters for the simplified contour. These are only |
| // meaningful for rects, rrects, and ovals. |
| void setPathWindingParams(SkPathDirection dir, unsigned start) { |
| SkASSERT((this->isRect() && start < 4) || (this->isRRect() && start < 8) || |
| (dir == kDefaultDir && start == kDefaultStart)); |
| fCW = dir == SkPathDirection::kCW; |
| fStart = static_cast<uint8_t>(start); |
| } |
| |
| void setInverted(bool inverted) { |
| if (this->isPath()) { |
| if (inverted != fPath.isInverseFillType()) { |
| fPath.toggleInverseFillType(); |
| } |
| } else { |
| fInverted = inverted; |
| } |
| } |
| |
| // Access the actual geometric description of the shape. May only access the appropriate type |
| // based on what was last set. The type may change after simplify() is called. |
| SkPoint& point() { SkASSERT(this->isPoint()); return fPoint; } |
| const SkPoint& point() const { SkASSERT(this->isPoint()); return fPoint; } |
| |
| SkRect& rect() { SkASSERT(this->isRect()); return fRect; } |
| const SkRect& rect() const { SkASSERT(this->isRect()); return fRect; } |
| |
| SkRRect& rrect() { SkASSERT(this->isRRect()); return fRRect; } |
| const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; } |
| |
| SkPath& path() { SkASSERT(this->isPath()); return fPath; } |
| const SkPath& path() const { SkASSERT(this->isPath()); return fPath; } |
| |
| GrArc& arc() { SkASSERT(this->isArc()); return fArc; } |
| const GrArc& arc() const { SkASSERT(this->isArc()); return fArc; } |
| |
| GrLineSegment& line() { SkASSERT(this->isLine()); return fLine; } |
| const GrLineSegment& line() const { SkASSERT(this->isLine()); return fLine; } |
| |
| // Update the geometry stored in the GrShape and update its associated type to match. This |
| // performs no simplification, so calling setRRect() with a round rect that has isRect() return |
| // true will still be considered an rrect by this shape until simplify() is called. |
| // |
| // These also reset any extracted direction, start, and inverted state from a prior simplified |
| // path, since these functions ared used to describe a new geometry. |
| void setPoint(const SkPoint& point) { |
| this->reset(Type::kPoint); |
| fPoint = point; |
| } |
| void setRect(const SkRect& rect) { |
| this->reset(Type::kRect); |
| fRect = rect; |
| } |
| void setRRect(const SkRRect& rrect) { |
| this->reset(Type::kRRect); |
| fRRect = rrect; |
| } |
| void setArc(const GrArc& arc) { |
| this->reset(Type::kArc); |
| fArc = arc; |
| } |
| void setLine(const GrLineSegment& line) { |
| this->reset(Type::kLine); |
| fLine = line; |
| } |
| void setPath(const SkPath& path) { |
| if (this->isPath()) { |
| // Assign directly |
| fPath = path; |
| } else { |
| // In-place initialize |
| this->setType(Type::kPath); |
| new (&fPath) SkPath(path); |
| } |
| // Must also set these since we didn't call reset() like other setX functions. |
| this->setPathWindingParams(kDefaultDir, kDefaultStart); |
| fInverted = path.isInverseFillType(); |
| } |
| void reset() { |
| this->reset(Type::kEmpty); |
| } |
| |
| // Flags that enable more aggressive, "destructive" simplifications to the geometry |
| enum SimplifyFlags : unsigned { |
| // If set, it is assumed the original shape would have been implicitly filled when drawn or |
| // clipped, so simpler shape types that are closed can still be considered. Shapes with |
| // 0 area (i.e. points and lines) can be turned into empty. |
| kSimpleFill_Flag = 0b001, |
| // If set, simplifications that would impact a directional stroke or path effect can still |
| // be taken (e.g. dir and start are not required, arcs can be converted to ovals). |
| kIgnoreWinding_Flag = 0b010, |
| // If set, the geometry will be updated to have sorted coordinates (rects, lines), modulated |
| // sweep angles (arcs). |
| kMakeCanonical_Flag = 0b100, |
| |
| kAll_Flags = 0b111 |
| }; |
| // Returns true if the shape was originally closed based on type (or detected type within a |
| // path), even if the final simplification results in a point, line, or empty. |
| bool simplify(unsigned flags = kAll_Flags); |
| |
| // True if the given bounding box is completely inside the shape, if it's conservatively treated |
| // as a filled, closed shape. |
| bool conservativeContains(const SkRect& rect) const; |
| bool conservativeContains(const SkPoint& point) const; |
| |
| // True if the underlying geometry represents a closed shape, without the need for an |
| // implicit close (note that if simplified earlier with 'simpleFill' = true, a shape that was |
| // not closed may become closed). |
| bool closed() const; |
| |
| // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill' |
| // is true, it is assumed the contours will be implicitly closed when drawn or used. |
| bool convex(bool simpleFill = true) const; |
| |
| // The bounding box of the shape. |
| SkRect bounds() const; |
| |
| // The segment masks that describe the shape, were it to be converted to an SkPath |
| uint32_t segmentMask() const; |
| |
| // Convert the shape into a path that describes the same geometry. |
| void asPath(SkPath* out, bool simpleFill = true) const; |
| |
| using sk_is_trivially_relocatable = std::true_type; |
| |
| private: |
| |
| void setType(Type type) { |
| if (this->isPath() && type != Type::kPath) { |
| fInverted = fPath.isInverseFillType(); |
| fPath.~SkPath(); |
| } |
| fType = type; |
| } |
| |
| void reset(Type type) { |
| this->setType(type); |
| this->setPathWindingParams(kDefaultDir, kDefaultStart); |
| this->setInverted(false); |
| } |
| |
| // Paths and arcs are root shapes, another type will never simplify to them, so they do |
| // not take the geometry to simplify as an argument. Since they are root shapes, they also |
| // return whether or not they were originally closed before being simplified. |
| bool simplifyPath(unsigned flags); |
| bool simplifyArc(unsigned flags); |
| |
| // The simpler type classes do take the geometry because it may represent an in-progress |
| // simplification that hasn't been set on the GrShape yet. The simpler types do not report |
| // whether or not they were closed because it's implicit in their type. |
| void simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags); |
| void simplifyPoint(const SkPoint& point, unsigned flags); |
| |
| // RRects and rects care about winding for path effects and will set the path winding state |
| // of the shape as well. |
| void simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, unsigned flags); |
| void simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, unsigned flags); |
| |
| union { |
| SkPoint fPoint; |
| SkRect fRect; |
| SkRRect fRRect; |
| SkPath fPath; |
| GrArc fArc; |
| GrLineSegment fLine; |
| }; |
| |
| Type fType = Type::kEmpty; |
| uint8_t fStart; // Restricted to rrects and simpler, so this will be < 8 |
| bool fCW; |
| bool fInverted; |
| |
| static_assert(::sk_is_trivially_relocatable<decltype(fPoint)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fRect)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fRRect)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fPath)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fArc)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fLine)>::value); |
| static_assert(::sk_is_trivially_relocatable<decltype(fType)>::value); |
| }; |
| |
| #endif |