xref: /aosp_15_r20/external/skia/src/gpu/ganesh/geometry/GrQuadUtils.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 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 GrQuadUtils_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define GrQuadUtils_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrQuad.h"
13*c8dee2aaSAndroid Build Coastguard Worker 
14*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker enum class GrAA : bool;
17*c8dee2aaSAndroid Build Coastguard Worker enum class GrAAType : unsigned int;
18*c8dee2aaSAndroid Build Coastguard Worker enum class GrQuadAAFlags;
19*c8dee2aaSAndroid Build Coastguard Worker struct SkRect;
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker namespace GrQuadUtils {
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker     // Resolve disagreements between the overall requested AA type and the per-edge quad AA flags.
24*c8dee2aaSAndroid Build Coastguard Worker     // Both outAAType and outEdgeFlags will be updated.
25*c8dee2aaSAndroid Build Coastguard Worker     void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
26*c8dee2aaSAndroid Build Coastguard Worker                        const GrQuad& quad, GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags);
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker     /**
29*c8dee2aaSAndroid Build Coastguard Worker      * Clip the device vertices of 'quad' to be in front of the W = 0 plane (w/in epsilon). The
30*c8dee2aaSAndroid Build Coastguard Worker      * local coordinates will be updated to match the new clipped vertices. This returns the number
31*c8dee2aaSAndroid Build Coastguard Worker      * of clipped quads that need to be drawn: 0 if 'quad' was entirely behind the plane, 1 if
32*c8dee2aaSAndroid Build Coastguard Worker      * 'quad' did not need to be clipped or if 2 or 3 vertices were clipped, or 2 if 'quad' had one
33*c8dee2aaSAndroid Build Coastguard Worker      * vertex clipped (producing a pentagonal shape spanned by 'quad' and 'extraVertices').
34*c8dee2aaSAndroid Build Coastguard Worker      */
35*c8dee2aaSAndroid Build Coastguard Worker     int ClipToW0(DrawQuad* quad, DrawQuad* extraVertices);
36*c8dee2aaSAndroid Build Coastguard Worker 
37*c8dee2aaSAndroid Build Coastguard Worker     /**
38*c8dee2aaSAndroid Build Coastguard Worker      * Crops quad to the provided device-space axis-aligned rectangle. If the intersection of this
39*c8dee2aaSAndroid Build Coastguard Worker      * quad (projected) and cropRect results in a quadrilateral, this returns true. If not, this
40*c8dee2aaSAndroid Build Coastguard Worker      * quad may be updated to be a smaller quad of the same type such that its intersection with
41*c8dee2aaSAndroid Build Coastguard Worker      * cropRect is visually the same. This function assumes that the 'quad' coordinates are finite.
42*c8dee2aaSAndroid Build Coastguard Worker      *
43*c8dee2aaSAndroid Build Coastguard Worker      * The provided edge flags are updated to reflect edges clipped by cropRect (toggling on or off
44*c8dee2aaSAndroid Build Coastguard Worker      * based on cropAA policy). If provided, the local coordinates will be updated to reflect the
45*c8dee2aaSAndroid Build Coastguard Worker      * updated device coordinates of this quad.
46*c8dee2aaSAndroid Build Coastguard Worker      *
47*c8dee2aaSAndroid Build Coastguard Worker      * If 'computeLocal' is false, the local coordinates in 'quad' will not be modified.
48*c8dee2aaSAndroid Build Coastguard Worker      */
49*c8dee2aaSAndroid Build Coastguard Worker     bool CropToRect(const SkRect& cropRect, GrAA cropAA, DrawQuad* quad, bool computeLocal=true);
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker     inline void Outset(const skvx::float4& edgeDistances, GrQuad* quad);
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker     bool WillUseHairline(const GrQuad& quad, GrAAType aaType, GrQuadAAFlags edgeFlags);
54*c8dee2aaSAndroid Build Coastguard Worker 
55*c8dee2aaSAndroid Build Coastguard Worker     class TessellationHelper {
56*c8dee2aaSAndroid Build Coastguard Worker     public:
57*c8dee2aaSAndroid Build Coastguard Worker         // Set the original device and (optional) local coordinates that are inset or outset
58*c8dee2aaSAndroid Build Coastguard Worker         // by the requested edge distances. Use nullptr if there are no local coordinates to update.
59*c8dee2aaSAndroid Build Coastguard Worker         // This assumes all device coordinates have been clipped to W > 0.
60*c8dee2aaSAndroid Build Coastguard Worker         void reset(const GrQuad& deviceQuad, const GrQuad* localQuad);
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker         // Calculates a new quadrilateral with edges parallel to the original except that they
63*c8dee2aaSAndroid Build Coastguard Worker         // have been moved inwards by edgeDistances (which should be positive). Distances are
64*c8dee2aaSAndroid Build Coastguard Worker         // ordered L, B, T, R to match CCW tristrip ordering of GrQuad vertices. Edges that are
65*c8dee2aaSAndroid Build Coastguard Worker         // not moved (i.e. distance == 0) will not be used in calculations and the corners will
66*c8dee2aaSAndroid Build Coastguard Worker         // remain on that edge.
67*c8dee2aaSAndroid Build Coastguard Worker         //
68*c8dee2aaSAndroid Build Coastguard Worker         // The per-vertex coverage will be returned. When the inset geometry does not collapse to
69*c8dee2aaSAndroid Build Coastguard Worker         // a point or line, this will be 1.0 for every vertex. When it does collapse, the per-vertex
70*c8dee2aaSAndroid Build Coastguard Worker         // coverages represent estimated pixel coverage to simulate drawing the subpixel-sized
71*c8dee2aaSAndroid Build Coastguard Worker         // original quad.
72*c8dee2aaSAndroid Build Coastguard Worker         //
73*c8dee2aaSAndroid Build Coastguard Worker         // Note: the edge distances are in device pixel units, so after rendering the new quad
74*c8dee2aaSAndroid Build Coastguard Worker         // edge's shortest distance to the original quad's edge would be equal to provided edge dist
75*c8dee2aaSAndroid Build Coastguard Worker         skvx::float4 inset(const skvx::float4& edgeDistances,
76*c8dee2aaSAndroid Build Coastguard Worker                            GrQuad* deviceInset, GrQuad* localInset);
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker         // Calculates a new quadrilateral that outsets the original edges by the given distances.
79*c8dee2aaSAndroid Build Coastguard Worker         // Other than moving edges outwards, this function is equivalent to inset(). If the exact
80*c8dee2aaSAndroid Build Coastguard Worker         // same edge distances are provided, certain internal computations can be reused across
81*c8dee2aaSAndroid Build Coastguard Worker         // consecutive calls to inset() and outset() (in any order).
82*c8dee2aaSAndroid Build Coastguard Worker         void outset(const skvx::float4& edgeDistances,
83*c8dee2aaSAndroid Build Coastguard Worker                     GrQuad* deviceOutset, GrQuad* localOutset);
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker         // Compute the edge equations of the original device space quad passed to 'reset()'. The
86*c8dee2aaSAndroid Build Coastguard Worker         // coefficients are stored per-edge in 'a', 'b', and 'c', such that ax + by + c = 0, and
87*c8dee2aaSAndroid Build Coastguard Worker         // a positive distance indicates the interior of the quad. Edges are ordered L, B, T, R,
88*c8dee2aaSAndroid Build Coastguard Worker         // matching edge distances passed to inset() and outset().
89*c8dee2aaSAndroid Build Coastguard Worker         void getEdgeEquations(skvx::float4* a,
90*c8dee2aaSAndroid Build Coastguard Worker                               skvx::float4* b,
91*c8dee2aaSAndroid Build Coastguard Worker                               skvx::float4* c);
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker         // Compute the edge lengths of the original device space quad passed to 'reset()'. The
94*c8dee2aaSAndroid Build Coastguard Worker         // edge lengths are ordered LBTR to match distances passed to inset() and outset().
95*c8dee2aaSAndroid Build Coastguard Worker         skvx::float4 getEdgeLengths();
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker         // Determine if the original device space quad has vertices closer than 1px to its opposing
98*c8dee2aaSAndroid Build Coastguard Worker         // edges, without going through the full work of computing the insets (assuming that the
99*c8dee2aaSAndroid Build Coastguard Worker         // inset distances would be 0.5px).
100*c8dee2aaSAndroid Build Coastguard Worker         bool isSubpixel();
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker     private:
103*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: This struct is named 'EdgeVectors' because it holds a lot of cached calculations
104*c8dee2aaSAndroid Build Coastguard Worker         // pertaining to the edge vectors of the input quad, projected into 2D device coordinates.
105*c8dee2aaSAndroid Build Coastguard Worker         // While they are not direction vectors, this struct represents a convenient storage space
106*c8dee2aaSAndroid Build Coastguard Worker         // for the projected corners of the quad.
107*c8dee2aaSAndroid Build Coastguard Worker         struct EdgeVectors {
108*c8dee2aaSAndroid Build Coastguard Worker             // Projected corners (x/w and y/w); these are the 2D coordinates that determine the
109*c8dee2aaSAndroid Build Coastguard Worker             // actual edge direction vectors, dx, dy, and invLengths
110*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fX2D, fY2D;
111*c8dee2aaSAndroid Build Coastguard Worker             // Normalized edge vectors of the device space quad, ordered L, B, T, R
112*c8dee2aaSAndroid Build Coastguard Worker             // (i.e. next_ccw(x) - x).
113*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fDX, fDY;
114*c8dee2aaSAndroid Build Coastguard Worker             // Reciprocal of edge length of the device space quad, i.e. 1 / sqrt(dx*dx + dy*dy)
115*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fInvLengths;
116*c8dee2aaSAndroid Build Coastguard Worker             // Theta represents the angle formed by the two edges connected at each corner.
117*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fCosTheta;
118*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fInvSinTheta; // 1 / sin(theta)
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker             void reset(const skvx::float4& xs, const skvx::float4& ys,
121*c8dee2aaSAndroid Build Coastguard Worker                        const skvx::float4& ws, GrQuad::Type quadType);
122*c8dee2aaSAndroid Build Coastguard Worker         };
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker         struct EdgeEquations {
125*c8dee2aaSAndroid Build Coastguard Worker             // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
126*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fA, fB, fC;
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker             void reset(const EdgeVectors& edgeVectors);
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 estimateCoverage(const skvx::float4& x2d,
131*c8dee2aaSAndroid Build Coastguard Worker                                           const skvx::float4& y2d) const;
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker             bool isSubpixel(const skvx::float4& x2d, const skvx::float4& y2d) const;
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker             // Outsets or insets 'x2d' and 'y2d' in place. To be used when the interior is very
136*c8dee2aaSAndroid Build Coastguard Worker             // small, edges are near parallel, or edges are very short/zero-length. Returns number
137*c8dee2aaSAndroid Build Coastguard Worker             // of effective vertices in the degenerate quad.
138*c8dee2aaSAndroid Build Coastguard Worker             int computeDegenerateQuad(const skvx::float4& signedEdgeDistances,
139*c8dee2aaSAndroid Build Coastguard Worker                                       skvx::float4* x2d, skvx::float4* y2d,
140*c8dee2aaSAndroid Build Coastguard Worker                                       skvx::Vec<4, int32_t>* aaMask) const;
141*c8dee2aaSAndroid Build Coastguard Worker         };
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker         struct OutsetRequest {
144*c8dee2aaSAndroid Build Coastguard Worker             // Positive edge distances to move each edge of the quad. These distances represent the
145*c8dee2aaSAndroid Build Coastguard Worker             // shortest (perpendicular) distance between the original edge and the inset or outset
146*c8dee2aaSAndroid Build Coastguard Worker             // edge. If the distance is 0, then the edge will not move.
147*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fEdgeDistances;
148*c8dee2aaSAndroid Build Coastguard Worker             // True if the new corners cannot be calculated by simply adding scaled edge vectors.
149*c8dee2aaSAndroid Build Coastguard Worker             // The quad may be degenerate because of the original geometry (near colinear edges), or
150*c8dee2aaSAndroid Build Coastguard Worker             // be because of the requested edge distances (collapse of inset, etc.)
151*c8dee2aaSAndroid Build Coastguard Worker             bool fInsetDegenerate;
152*c8dee2aaSAndroid Build Coastguard Worker             bool fOutsetDegenerate;
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker             void reset(const EdgeVectors& edgeVectors, GrQuad::Type quadType,
155*c8dee2aaSAndroid Build Coastguard Worker                        const skvx::float4& edgeDistances);
156*c8dee2aaSAndroid Build Coastguard Worker         };
157*c8dee2aaSAndroid Build Coastguard Worker 
158*c8dee2aaSAndroid Build Coastguard Worker         struct Vertices {
159*c8dee2aaSAndroid Build Coastguard Worker             // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
160*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fX, fY, fW;
161*c8dee2aaSAndroid Build Coastguard Worker             // U, V, and R coordinates representing local quad.
162*c8dee2aaSAndroid Build Coastguard Worker             // Ignored depending on uvrCount (0, 1, 2).
163*c8dee2aaSAndroid Build Coastguard Worker             skvx::float4 fU, fV, fR;
164*c8dee2aaSAndroid Build Coastguard Worker             int fUVRCount;
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker             void reset(const GrQuad& deviceQuad, const GrQuad* localQuad);
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker             void asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType,
169*c8dee2aaSAndroid Build Coastguard Worker                            GrQuad* localOut, GrQuad::Type localType) const;
170*c8dee2aaSAndroid Build Coastguard Worker 
171*c8dee2aaSAndroid Build Coastguard Worker             // Update the device and optional local coordinates by moving the corners along their
172*c8dee2aaSAndroid Build Coastguard Worker             // edge vectors such that the new edges have moved 'signedEdgeDistances' from their
173*c8dee2aaSAndroid Build Coastguard Worker             // original lines. This should only be called if the 'edgeVectors' fInvSinTheta data is
174*c8dee2aaSAndroid Build Coastguard Worker             // numerically sound.
175*c8dee2aaSAndroid Build Coastguard Worker             void moveAlong(const EdgeVectors& edgeVectors,
176*c8dee2aaSAndroid Build Coastguard Worker                            const skvx::float4& signedEdgeDistances);
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker             // Update the device coordinates by deriving (x,y,w) that project to (x2d, y2d), with
179*c8dee2aaSAndroid Build Coastguard Worker             // optional local coordinates updated to match the new vertices. It is assumed that
180*c8dee2aaSAndroid Build Coastguard Worker             // 'mask' was respected when determining (x2d, y2d), but it is used to ensure that only
181*c8dee2aaSAndroid Build Coastguard Worker             // unmasked unprojected edge vectors are used when computing device and local coords.
182*c8dee2aaSAndroid Build Coastguard Worker             void moveTo(const skvx::float4& x2d,
183*c8dee2aaSAndroid Build Coastguard Worker                         const skvx::float4& y2d,
184*c8dee2aaSAndroid Build Coastguard Worker                         const skvx::Vec<4, int32_t>& mask);
185*c8dee2aaSAndroid Build Coastguard Worker         };
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker         Vertices            fOriginal;
188*c8dee2aaSAndroid Build Coastguard Worker         EdgeVectors         fEdgeVectors;
189*c8dee2aaSAndroid Build Coastguard Worker         GrQuad::Type        fDeviceType;
190*c8dee2aaSAndroid Build Coastguard Worker         GrQuad::Type        fLocalType;
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker         // Lazily computed as needed; use accessor functions instead of direct access.
193*c8dee2aaSAndroid Build Coastguard Worker         OutsetRequest       fOutsetRequest;
194*c8dee2aaSAndroid Build Coastguard Worker         EdgeEquations       fEdgeEquations;
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker         // Validity of Vertices/EdgeVectors (always true after first call to set()).
197*c8dee2aaSAndroid Build Coastguard Worker         bool fVerticesValid      = false;
198*c8dee2aaSAndroid Build Coastguard Worker         // Validity of outset request (true after calling getOutsetRequest() until next set() call
199*c8dee2aaSAndroid Build Coastguard Worker         // or next inset/outset() with different edge distances).
200*c8dee2aaSAndroid Build Coastguard Worker         bool fOutsetRequestValid = false;
201*c8dee2aaSAndroid Build Coastguard Worker         // Validity of edge equations (true after calling getEdgeEquations() until next set() call).
202*c8dee2aaSAndroid Build Coastguard Worker         bool fEdgeEquationsValid = false;
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker         // The requested edge distances must be positive so that they can be reused between inset
205*c8dee2aaSAndroid Build Coastguard Worker         // and outset calls.
206*c8dee2aaSAndroid Build Coastguard Worker         const OutsetRequest& getOutsetRequest(const skvx::float4& edgeDistances);
207*c8dee2aaSAndroid Build Coastguard Worker         const EdgeEquations& getEdgeEquations();
208*c8dee2aaSAndroid Build Coastguard Worker 
209*c8dee2aaSAndroid Build Coastguard Worker         // Outsets or insets 'vertices' by the given perpendicular 'signedEdgeDistances' (inset or
210*c8dee2aaSAndroid Build Coastguard Worker         // outset is determined implicitly by the sign of the distances).
211*c8dee2aaSAndroid Build Coastguard Worker         void adjustVertices(const skvx::float4& signedEdgeDistances, Vertices* vertices);
212*c8dee2aaSAndroid Build Coastguard Worker         // Like adjustVertices() but handles empty edges, collapsed quads, numerical issues, and
213*c8dee2aaSAndroid Build Coastguard Worker         // returns the number of effective vertices in the adjusted shape.
214*c8dee2aaSAndroid Build Coastguard Worker         int adjustDegenerateVertices(const skvx::float4& signedEdgeDistances,
215*c8dee2aaSAndroid Build Coastguard Worker                                      Vertices* vertices);
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker         friend int ClipToW0(DrawQuad*, DrawQuad*); // To reuse Vertices struct
218*c8dee2aaSAndroid Build Coastguard Worker     };
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker } // namespace GrQuadUtils
221*c8dee2aaSAndroid Build Coastguard Worker 
Outset(const skvx::float4 & edgeDistances,GrQuad * quad)222*c8dee2aaSAndroid Build Coastguard Worker void GrQuadUtils::Outset(const skvx::float4& edgeDistances, GrQuad* quad) {
223*c8dee2aaSAndroid Build Coastguard Worker     TessellationHelper outsetter;
224*c8dee2aaSAndroid Build Coastguard Worker     outsetter.reset(*quad, nullptr);
225*c8dee2aaSAndroid Build Coastguard Worker     outsetter.outset(edgeDistances, quad, nullptr);
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker 
228*c8dee2aaSAndroid Build Coastguard Worker #endif
229