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