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