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