| /* | |
| * 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/SkArc.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathTypes.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRRect.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/private/SkAssert.h" | |
| #include "include/private/SkTo.h" | |
| #include "include/private/SkTypeTraits.h" | |
| #include <cstdint> | |
| #include <new> | |
| #include <type_traits> | |
| // 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 SkArc& 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; } | |
| SkArc& arc() { SkASSERT(this->isArc()); return fArc; } | |
| const SkArc& 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 SkArc& 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. | |
| SkPath asPath(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; | |
| SkArc 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 |