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