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* ); |
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 | |