1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_geom_Shape_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_geom_Shape_DEFINED 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkArc.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h" 17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h" 18*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h" 19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Rect.h" 20*c8dee2aaSAndroid Build Coastguard Worker 21*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint> 22*c8dee2aaSAndroid Build Coastguard Worker #include <new> 23*c8dee2aaSAndroid Build Coastguard Worker 24*c8dee2aaSAndroid Build Coastguard Worker struct SkRect; 25*c8dee2aaSAndroid Build Coastguard Worker 26*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite { 27*c8dee2aaSAndroid Build Coastguard Worker 28*c8dee2aaSAndroid Build Coastguard Worker /** 29*c8dee2aaSAndroid Build Coastguard Worker * Shape is effectively a std::variant over different geometric shapes, with the most complex 30*c8dee2aaSAndroid Build Coastguard Worker * being an SkPath. It provides a consistent way to query geometric properties, such as convexity, 31*c8dee2aaSAndroid Build Coastguard Worker * point containment, or iteration. 32*c8dee2aaSAndroid Build Coastguard Worker */ 33*c8dee2aaSAndroid Build Coastguard Worker class Shape { 34*c8dee2aaSAndroid Build Coastguard Worker public: 35*c8dee2aaSAndroid Build Coastguard Worker enum class Type : uint8_t { 36*c8dee2aaSAndroid Build Coastguard Worker kEmpty, kLine, kRect, kRRect, kArc, kPath 37*c8dee2aaSAndroid Build Coastguard Worker }; 38*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr int kTypeCount = static_cast<int>(Type::kPath) + 1; 39*c8dee2aaSAndroid Build Coastguard Worker Shape()40*c8dee2aaSAndroid Build Coastguard Worker Shape() {} Shape(const Shape & shape)41*c8dee2aaSAndroid Build Coastguard Worker Shape(const Shape& shape) { *this = shape; } 42*c8dee2aaSAndroid Build Coastguard Worker Shape(Shape&&) = delete; 43*c8dee2aaSAndroid Build Coastguard Worker Shape(SkPoint p0,SkPoint p1)44*c8dee2aaSAndroid Build Coastguard Worker Shape(SkPoint p0, SkPoint p1) { this->setLine(p0, p1); } Shape(SkV2 p0,SkV2 p1)45*c8dee2aaSAndroid Build Coastguard Worker Shape(SkV2 p0, SkV2 p1) { this->setLine(p0, p1); } Shape(skvx::float2 p0,skvx::float2 p1)46*c8dee2aaSAndroid Build Coastguard Worker Shape(skvx::float2 p0, skvx::float2 p1) { this->setLine(p0, p1); } Shape(const Rect & rect)47*c8dee2aaSAndroid Build Coastguard Worker explicit Shape(const Rect& rect) { this->setRect(rect); } Shape(const SkRect & rect)48*c8dee2aaSAndroid Build Coastguard Worker explicit Shape(const SkRect& rect) { this->setRect(rect); } Shape(const SkRRect & rrect)49*c8dee2aaSAndroid Build Coastguard Worker explicit Shape(const SkRRect& rrect) { this->setRRect(rrect); } Shape(const SkArc & arc)50*c8dee2aaSAndroid Build Coastguard Worker explicit Shape(const SkArc& arc) { this->setArc(arc); } Shape(const SkPath & path)51*c8dee2aaSAndroid Build Coastguard Worker explicit Shape(const SkPath& path) { this->setPath(path); } 52*c8dee2aaSAndroid Build Coastguard Worker ~Shape()53*c8dee2aaSAndroid Build Coastguard Worker ~Shape() { this->reset(); } 54*c8dee2aaSAndroid Build Coastguard Worker 55*c8dee2aaSAndroid Build Coastguard Worker // NOTE: None of the geometry types benefit from move semantics, so we don't bother 56*c8dee2aaSAndroid Build Coastguard Worker // defining a move assignment operator for Shape. 57*c8dee2aaSAndroid Build Coastguard Worker Shape& operator=(Shape&&) = delete; 58*c8dee2aaSAndroid Build Coastguard Worker Shape& operator=(const Shape&); 59*c8dee2aaSAndroid Build Coastguard Worker 60*c8dee2aaSAndroid Build Coastguard Worker // Return the type of the data last stored in the Shape, which does not incorporate any possible 61*c8dee2aaSAndroid Build Coastguard Worker // simplifications that could be applied to it (e.g. a degenerate round rect with 0 radius 62*c8dee2aaSAndroid Build Coastguard Worker // corners is kRRect and not kRect). type()63*c8dee2aaSAndroid Build Coastguard Worker Type type() const { return fType; } 64*c8dee2aaSAndroid Build Coastguard Worker isEmpty()65*c8dee2aaSAndroid Build Coastguard Worker bool isEmpty() const { return fType == Type::kEmpty; } isLine()66*c8dee2aaSAndroid Build Coastguard Worker bool isLine() const { return fType == Type::kLine; } isRect()67*c8dee2aaSAndroid Build Coastguard Worker bool isRect() const { return fType == Type::kRect; } isRRect()68*c8dee2aaSAndroid Build Coastguard Worker bool isRRect() const { return fType == Type::kRRect; } isArc()69*c8dee2aaSAndroid Build Coastguard Worker bool isArc() const { return fType == Type::kArc; } isPath()70*c8dee2aaSAndroid Build Coastguard Worker bool isPath() const { return fType == Type::kPath; } 71*c8dee2aaSAndroid Build Coastguard Worker inverted()72*c8dee2aaSAndroid Build Coastguard Worker bool inverted() const { 73*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fType != Type::kPath || fInverted == fPath.isInverseFillType()); 74*c8dee2aaSAndroid Build Coastguard Worker return fInverted; 75*c8dee2aaSAndroid Build Coastguard Worker } 76*c8dee2aaSAndroid Build Coastguard Worker setInverted(bool inverted)77*c8dee2aaSAndroid Build Coastguard Worker void setInverted(bool inverted) { 78*c8dee2aaSAndroid Build Coastguard Worker if (fType == Type::kPath && inverted != fPath.isInverseFillType()) { 79*c8dee2aaSAndroid Build Coastguard Worker fPath.toggleInverseFillType(); 80*c8dee2aaSAndroid Build Coastguard Worker } 81*c8dee2aaSAndroid Build Coastguard Worker fInverted = inverted; 82*c8dee2aaSAndroid Build Coastguard Worker } 83*c8dee2aaSAndroid Build Coastguard Worker fillType()84*c8dee2aaSAndroid Build Coastguard Worker SkPathFillType fillType() const { 85*c8dee2aaSAndroid Build Coastguard Worker if (fType == Type::kPath) { 86*c8dee2aaSAndroid Build Coastguard Worker return fPath.getFillType(); // already incorporates invertedness 87*c8dee2aaSAndroid Build Coastguard Worker } else { 88*c8dee2aaSAndroid Build Coastguard Worker return fInverted ? SkPathFillType::kInverseEvenOdd : SkPathFillType::kEvenOdd; 89*c8dee2aaSAndroid Build Coastguard Worker } 90*c8dee2aaSAndroid Build Coastguard Worker } 91*c8dee2aaSAndroid Build Coastguard Worker 92*c8dee2aaSAndroid Build Coastguard Worker // True if the given bounding box is completely inside the shape, if it's conservatively treated 93*c8dee2aaSAndroid Build Coastguard Worker // as a filled, closed shape. 94*c8dee2aaSAndroid Build Coastguard Worker bool conservativeContains(const Rect& rect) const; 95*c8dee2aaSAndroid Build Coastguard Worker bool conservativeContains(skvx::float2 point) const; 96*c8dee2aaSAndroid Build Coastguard Worker 97*c8dee2aaSAndroid Build Coastguard Worker // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill' 98*c8dee2aaSAndroid Build Coastguard Worker // is true, it is assumed the contours will be implicitly closed when drawn or used. 99*c8dee2aaSAndroid Build Coastguard Worker bool convex(bool simpleFill = true) const; 100*c8dee2aaSAndroid Build Coastguard Worker 101*c8dee2aaSAndroid Build Coastguard Worker // The bounding box of the shape. 102*c8dee2aaSAndroid Build Coastguard Worker Rect bounds() const; 103*c8dee2aaSAndroid Build Coastguard Worker 104*c8dee2aaSAndroid Build Coastguard Worker // Convert the shape into a path that describes the same geometry. 105*c8dee2aaSAndroid Build Coastguard Worker SkPath asPath() const; 106*c8dee2aaSAndroid Build Coastguard Worker 107*c8dee2aaSAndroid Build Coastguard Worker // Access the actual geometric description of the shape. May only access the appropriate type 108*c8dee2aaSAndroid Build Coastguard Worker // based on what was last set. p0()109*c8dee2aaSAndroid Build Coastguard Worker skvx::float2 p0() const { SkASSERT(this->isLine()); return fRect.topLeft(); } p1()110*c8dee2aaSAndroid Build Coastguard Worker skvx::float2 p1() const { SkASSERT(this->isLine()); return fRect.botRight(); } line()111*c8dee2aaSAndroid Build Coastguard Worker skvx::float4 line() const { SkASSERT(this->isLine()); return fRect.ltrb(); } rect()112*c8dee2aaSAndroid Build Coastguard Worker const Rect& rect() const { SkASSERT(this->isRect()); return fRect; } rrect()113*c8dee2aaSAndroid Build Coastguard Worker const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; } arc()114*c8dee2aaSAndroid Build Coastguard Worker const SkArc& arc() const { SkASSERT(this->isArc()); return fArc; } path()115*c8dee2aaSAndroid Build Coastguard Worker const SkPath& path() const { SkASSERT(this->isPath()); return fPath; } 116*c8dee2aaSAndroid Build Coastguard Worker 117*c8dee2aaSAndroid Build Coastguard Worker // Update the geometry stored in the Shape and update its associated type to match. This 118*c8dee2aaSAndroid Build Coastguard Worker // performs no simplification, so calling setRRect() with a round rect that has isRect() return 119*c8dee2aaSAndroid Build Coastguard Worker // true will still be considered an rrect by Shape. 120*c8dee2aaSAndroid Build Coastguard Worker // 121*c8dee2aaSAndroid Build Coastguard Worker // These reset inversion to the default for the geometric type. setLine(SkPoint p0,SkPoint p1)122*c8dee2aaSAndroid Build Coastguard Worker void setLine(SkPoint p0, SkPoint p1) { 123*c8dee2aaSAndroid Build Coastguard Worker this->setLine(skvx::float2{p0.fX, p0.fY}, skvx::float2{p1.fX, p1.fY}); 124*c8dee2aaSAndroid Build Coastguard Worker } setLine(SkV2 p0,SkV2 p1)125*c8dee2aaSAndroid Build Coastguard Worker void setLine(SkV2 p0, SkV2 p1) { 126*c8dee2aaSAndroid Build Coastguard Worker this->setLine(skvx::float2{p0.x, p0.y}, skvx::float2{p1.x, p1.y}); 127*c8dee2aaSAndroid Build Coastguard Worker } setLine(skvx::float2 p0,skvx::float2 p1)128*c8dee2aaSAndroid Build Coastguard Worker void setLine(skvx::float2 p0, skvx::float2 p1) { 129*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kLine); 130*c8dee2aaSAndroid Build Coastguard Worker fRect = Rect(p0, p1); 131*c8dee2aaSAndroid Build Coastguard Worker fInverted = false; 132*c8dee2aaSAndroid Build Coastguard Worker } setRect(const SkRect & rect)133*c8dee2aaSAndroid Build Coastguard Worker void setRect(const SkRect& rect) { this->setRect(Rect(rect)); } setRect(const Rect & rect)134*c8dee2aaSAndroid Build Coastguard Worker void setRect(const Rect& rect) { 135*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kRect); 136*c8dee2aaSAndroid Build Coastguard Worker fRect = rect; 137*c8dee2aaSAndroid Build Coastguard Worker fInverted = false; 138*c8dee2aaSAndroid Build Coastguard Worker } setRRect(const SkRRect & rrect)139*c8dee2aaSAndroid Build Coastguard Worker void setRRect(const SkRRect& rrect) { 140*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kRRect); 141*c8dee2aaSAndroid Build Coastguard Worker fRRect = rrect; 142*c8dee2aaSAndroid Build Coastguard Worker fInverted = false; 143*c8dee2aaSAndroid Build Coastguard Worker } setArc(const SkArc & arc)144*c8dee2aaSAndroid Build Coastguard Worker void setArc(const SkArc& arc) { 145*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kArc); 146*c8dee2aaSAndroid Build Coastguard Worker fArc = arc; 147*c8dee2aaSAndroid Build Coastguard Worker fInverted = false; 148*c8dee2aaSAndroid Build Coastguard Worker } setPath(const SkPath & path)149*c8dee2aaSAndroid Build Coastguard Worker void setPath(const SkPath& path) { 150*c8dee2aaSAndroid Build Coastguard Worker if (fType == Type::kPath) { 151*c8dee2aaSAndroid Build Coastguard Worker // Assign directly 152*c8dee2aaSAndroid Build Coastguard Worker fPath = path; 153*c8dee2aaSAndroid Build Coastguard Worker } else { 154*c8dee2aaSAndroid Build Coastguard Worker // In-place initialize 155*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kPath); 156*c8dee2aaSAndroid Build Coastguard Worker new (&fPath) SkPath(path); 157*c8dee2aaSAndroid Build Coastguard Worker } 158*c8dee2aaSAndroid Build Coastguard Worker fInverted = path.isInverseFillType(); 159*c8dee2aaSAndroid Build Coastguard Worker } 160*c8dee2aaSAndroid Build Coastguard Worker reset()161*c8dee2aaSAndroid Build Coastguard Worker void reset() { 162*c8dee2aaSAndroid Build Coastguard Worker this->setType(Type::kEmpty); 163*c8dee2aaSAndroid Build Coastguard Worker fInverted = false; 164*c8dee2aaSAndroid Build Coastguard Worker } 165*c8dee2aaSAndroid Build Coastguard Worker 166*c8dee2aaSAndroid Build Coastguard Worker /** 167*c8dee2aaSAndroid Build Coastguard Worker * Gets the size of the key for the shape represented by this Shape. 168*c8dee2aaSAndroid Build Coastguard Worker * A negative value is returned if the shape has no key (shouldn't be cached). 169*c8dee2aaSAndroid Build Coastguard Worker */ 170*c8dee2aaSAndroid Build Coastguard Worker int keySize() const; 171*c8dee2aaSAndroid Build Coastguard Worker hasKey()172*c8dee2aaSAndroid Build Coastguard Worker bool hasKey() const { return this->keySize() >= 0; } 173*c8dee2aaSAndroid Build Coastguard Worker 174*c8dee2aaSAndroid Build Coastguard Worker /** 175*c8dee2aaSAndroid Build Coastguard Worker * Writes keySize() bytes into the provided pointer. Assumes that there is enough 176*c8dee2aaSAndroid Build Coastguard Worker * space allocated for the key and that keySize() does not return a negative value 177*c8dee2aaSAndroid Build Coastguard Worker * for this shape. If includeInverted is false, non-inverted state will be written 178*c8dee2aaSAndroid Build Coastguard Worker * into the key regardless of the Shape's state. 179*c8dee2aaSAndroid Build Coastguard Worker */ 180*c8dee2aaSAndroid Build Coastguard Worker void writeKey(uint32_t* key, bool includeInverted) const; 181*c8dee2aaSAndroid Build Coastguard Worker 182*c8dee2aaSAndroid Build Coastguard Worker private: setType(Type type)183*c8dee2aaSAndroid Build Coastguard Worker void setType(Type type) { 184*c8dee2aaSAndroid Build Coastguard Worker if (this->isPath() && type != Type::kPath) { 185*c8dee2aaSAndroid Build Coastguard Worker fPath.~SkPath(); 186*c8dee2aaSAndroid Build Coastguard Worker } 187*c8dee2aaSAndroid Build Coastguard Worker fType = type; 188*c8dee2aaSAndroid Build Coastguard Worker } 189*c8dee2aaSAndroid Build Coastguard Worker 190*c8dee2aaSAndroid Build Coastguard Worker /** 191*c8dee2aaSAndroid Build Coastguard Worker * Key for the state data in the shape. This includes path fill type, 192*c8dee2aaSAndroid Build Coastguard Worker * and any tracked inversion, as well as the class of geometry. 193*c8dee2aaSAndroid Build Coastguard Worker * If includeInverted is false, non-inverted state will be written into 194*c8dee2aaSAndroid Build Coastguard Worker * the key regardless of the Shape's state. 195*c8dee2aaSAndroid Build Coastguard Worker */ 196*c8dee2aaSAndroid Build Coastguard Worker uint32_t stateKey(bool includeInverted) const; 197*c8dee2aaSAndroid Build Coastguard Worker 198*c8dee2aaSAndroid Build Coastguard Worker union { 199*c8dee2aaSAndroid Build Coastguard Worker Rect fRect; // p0 = top-left, p1 = bot-right if type is kLine (may be unsorted) 200*c8dee2aaSAndroid Build Coastguard Worker SkRRect fRRect; 201*c8dee2aaSAndroid Build Coastguard Worker SkArc fArc; 202*c8dee2aaSAndroid Build Coastguard Worker SkPath fPath; 203*c8dee2aaSAndroid Build Coastguard Worker }; 204*c8dee2aaSAndroid Build Coastguard Worker 205*c8dee2aaSAndroid Build Coastguard Worker Type fType = Type::kEmpty; 206*c8dee2aaSAndroid Build Coastguard Worker bool fInverted = false; 207*c8dee2aaSAndroid Build Coastguard Worker }; 208*c8dee2aaSAndroid Build Coastguard Worker 209*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite 210*c8dee2aaSAndroid Build Coastguard Worker 211*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_geom_Shape_DEFINED 212