1 /* 2 * Copyright 2015 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 GrQuad_DEFINED 9 #define GrQuad_DEFINED 10 11 #include "include/core/SkPoint.h" 12 #include "include/core/SkPoint3.h" 13 #include "include/core/SkRect.h" 14 #include "include/core/SkScalar.h" 15 #include "include/private/base/SkAssert.h" 16 #include "include/private/base/SkFloatingPoint.h" 17 #include "src/base/SkVx.h" 18 #include "src/gpu/BufferWriter.h" 19 20 #include <algorithm> 21 #include <type_traits> 22 23 class SkMatrix; 24 enum class GrQuadAAFlags; 25 26 /** 27 * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The 28 * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right). 29 */ 30 class GrQuad { 31 public: 32 // Quadrilaterals can be classified in several useful ways that assist AA tessellation and other 33 // analysis when drawing, in particular, knowing if it was originally a rectangle transformed by 34 // certain types of matrices: 35 enum class Type { 36 // The 4 points remain an axis-aligned rectangle; their logical indices may not respect 37 // TL, BL, TR, BR ordering if the transform was a 90 degree rotation or mirror. 38 kAxisAligned, 39 // The 4 points represent a rectangle subjected to a rotation, its corners are right angles. 40 kRectilinear, 41 // Arbitrary 2D quadrilateral; may have been a rectangle transformed with skew or some 42 // clipped polygon. Its w coordinates will all be 1. 43 kGeneral, 44 // Even more general-purpose than kGeneral, this allows the w coordinates to be non-unity. 45 kPerspective, 46 kLast = kPerspective 47 }; 48 static const int kTypeCount = static_cast<int>(Type::kLast) + 1; 49 50 // This enforces W == 1 for non-perspective quads, but does not initialize X or Y. 51 GrQuad() = default; 52 GrQuad(const GrQuad&) = default; 53 GrQuad(const SkRect & rect)54 explicit GrQuad(const SkRect& rect) 55 : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight} 56 , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {} 57 58 static GrQuad MakeFromRect(const SkRect&, const SkMatrix&); 59 60 // Creates a GrQuad from the quadrilateral 'pts', transformed by the matrix. The input 61 // points array is arranged as per SkRect::toQuad (top-left, top-right, bottom-right, 62 // bottom-left). The returned instance's point order will still be CCW tri-strip order. 63 static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&); 64 65 GrQuad& operator=(const GrQuad&) = default; 66 point3(int i)67 SkPoint3 point3(int i) const { return {fX[i], fY[i], fW[i]}; } 68 point(int i)69 SkPoint point(int i) const { 70 if (fType == Type::kPerspective) { 71 return {fX[i] / fW[i], fY[i] / fW[i]}; 72 } else { 73 return {fX[i], fY[i]}; 74 } 75 } 76 writeVertex(int cornerIdx,skgpu::VertexWriter & w)77 void writeVertex(int cornerIdx, skgpu::VertexWriter& w) const { 78 w << this->point(cornerIdx); 79 } 80 bounds()81 SkRect bounds() const { 82 if (fType == GrQuad::Type::kPerspective) { 83 return this->projectedBounds(); 84 } 85 // Calculate min/max directly on the 4 floats, instead of loading/unloading into SIMD. Since 86 // there's no horizontal min/max, it's not worth it. Defining non-perspective case in header 87 // also leads to substantial performance boost due to inlining. 88 auto min = [](const float c[4]) { return std::min(std::min(c[0], c[1]), 89 std::min(c[2], c[3]));}; 90 auto max = [](const float c[4]) { return std::max(std::max(c[0], c[1]), 91 std::max(c[2], c[3]));}; 92 return { min(fX), min(fY), max(fX), max(fY) }; 93 } 94 isFinite()95 bool isFinite() const { 96 // If any coordinate is infinity or NaN, then multiplying it with 0 will make accum NaN 97 float accum = 0; 98 for (int i = 0; i < 4; ++i) { 99 accum *= fX[i]; 100 accum *= fY[i]; 101 accum *= fW[i]; 102 } 103 SkASSERT(0 == accum || SkIsNaN(accum)); 104 105 return accum == 0.0f; 106 } 107 x(int i)108 float x(int i) const { return fX[i]; } y(int i)109 float y(int i) const { return fY[i]; } w(int i)110 float w(int i) const { return fW[i]; } iw(int i)111 float iw(int i) const { return sk_ieee_float_divide(1.f, fW[i]); } 112 x4f()113 skvx::Vec<4, float> x4f() const { return skvx::Vec<4, float>::Load(fX); } y4f()114 skvx::Vec<4, float> y4f() const { return skvx::Vec<4, float>::Load(fY); } w4f()115 skvx::Vec<4, float> w4f() const { return skvx::Vec<4, float>::Load(fW); } iw4f()116 skvx::Vec<4, float> iw4f() const { return 1.f / this->w4f(); } 117 quadType()118 Type quadType() const { return fType; } 119 hasPerspective()120 bool hasPerspective() const { return fType == Type::kPerspective; } 121 122 // True if anti-aliasing affects this quad. Only valid when quadType == kAxisAligned 123 bool aaHasEffectOnRect(GrQuadAAFlags edgeFlags) const; 124 125 // True if this quad is axis-aligned and still has its top-left corner at v0. Equivalently, 126 // quad == GrQuad(quad->bounds()). Axis-aligned quads with flips and rotations may exactly 127 // fill their bounds, but their vertex order will not match TL BL TR BR anymore. 128 bool asRect(SkRect* rect) const; 129 130 // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be 131 // taken to keep its quad type aligned with the geometric nature of the new coordinates. xs()132 const float* xs() const { return fX; } xs()133 float* xs() { return fX; } ys()134 const float* ys() const { return fY; } ys()135 float* ys() { return fY; } ws()136 const float* ws() const { return fW; } ws()137 float* ws() { return fW; } 138 139 // Automatically ensures ws are 1 if new type is not perspective. setQuadType(Type newType)140 void setQuadType(Type newType) { 141 if (newType != Type::kPerspective && fType == Type::kPerspective) { 142 fW[0] = fW[1] = fW[2] = fW[3] = 1.f; 143 } 144 SkASSERT(newType == Type::kPerspective || 145 (SkScalarNearlyEqual(fW[0], 1.f) && SkScalarNearlyEqual(fW[1], 1.f) && 146 SkScalarNearlyEqual(fW[2], 1.f) && SkScalarNearlyEqual(fW[3], 1.f))); 147 148 fType = newType; 149 } 150 private: 151 template<typename T> 152 friend class GrQuadListBase; // for access to fX, fY, fW 153 GrQuad(const skvx::Vec<4,float> & xs,const skvx::Vec<4,float> & ys,Type type)154 GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, Type type) 155 : fType(type) { 156 SkASSERT(type != Type::kPerspective); 157 xs.store(fX); 158 ys.store(fY); 159 } 160 GrQuad(const skvx::Vec<4,float> & xs,const skvx::Vec<4,float> & ys,const skvx::Vec<4,float> & ws,Type type)161 GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, 162 const skvx::Vec<4, float>& ws, Type type) 163 : fW{} // Include fW in member initializer to avoid redundant default initializer 164 , fType(type) { 165 xs.store(fX); 166 ys.store(fY); 167 ws.store(fW); 168 } 169 170 // Defined in GrQuadUtils.cpp to share the coord clipping code 171 SkRect projectedBounds() const; 172 173 float fX[4]; 174 float fY[4]; 175 float fW[4] = {1.f, 1.f, 1.f, 1.f}; 176 177 Type fType = Type::kAxisAligned; 178 }; 179 180 template<> struct skgpu::VertexWriter::is_quad<GrQuad> : std::true_type {}; 181 182 // A simple struct representing the common work unit of a pair of device and local coordinates, as 183 // well as the edge flags controlling anti-aliasing for the quadrilateral when drawn. 184 struct DrawQuad { 185 GrQuad fDevice; 186 GrQuad fLocal; 187 GrQuadAAFlags fEdgeFlags; 188 }; 189 190 #endif 191