• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google Inc.
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 #include "GrQuadPerEdgeAA.h"
9 #include "GrQuad.h"
10 #include "GrVertexWriter.h"
11 #include "glsl/GrGLSLColorSpaceXformHelper.h"
12 #include "glsl/GrGLSLGeometryProcessor.h"
13 #include "glsl/GrGLSLPrimitiveProcessor.h"
14 #include "glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "glsl/GrGLSLVarying.h"
16 #include "glsl/GrGLSLVertexGeoBuilder.h"
17 #include "SkNx.h"
18 
19 #define AI SK_ALWAYS_INLINE
20 
21 namespace {
22 
23 // Helper data types since there is a lot of information that needs to be passed around to
24 // avoid recalculation in the different procedures for tessellating an AA quad.
25 
26 struct Vertices {
27     // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
28     Sk4f fX, fY, fW;
29     // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
30     Sk4f fU, fV, fR;
31     int fUVRCount;
32 };
33 
34 struct QuadMetadata {
35     // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
36     Sk4f fDX, fDY;
37     // 1 / edge length of the device space quad
38     Sk4f fInvLengths;
39     // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
40     Sk4f fMask;
41 };
42 
43 struct Edges {
44     // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
45     Sk4f fA, fB, fC;
46     // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
47     bool fFlipped;
48 };
49 
50 static constexpr float kTolerance = 1e-2f;
51 
fma(const Sk4f & f,const Sk4f & m,const Sk4f & a)52 static AI Sk4f fma(const Sk4f& f, const Sk4f& m, const Sk4f& a) {
53     return SkNx_fma<4, float>(f, m, a);
54 }
55 
56 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
57 // order.
nextCW(const Sk4f & v)58 static AI Sk4f nextCW(const Sk4f& v) {
59     return SkNx_shuffle<2, 0, 3, 1>(v);
60 }
61 
nextCCW(const Sk4f & v)62 static AI Sk4f nextCCW(const Sk4f& v) {
63     return SkNx_shuffle<1, 3, 0, 2>(v);
64 }
65 
66 // Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
67 // e3 may be null if only 2D edges need to be corrected for.
correct_bad_edges(const Sk4f & bad,Sk4f * e1,Sk4f * e2,Sk4f * e3)68 static AI void correct_bad_edges(const Sk4f& bad, Sk4f* e1, Sk4f* e2, Sk4f* e3) {
69     if (bad.anyTrue()) {
70         // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
71         *e1 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e1), *e1);
72         *e2 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e2), *e2);
73         if (e3) {
74             *e3 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e3), *e3);
75         }
76     }
77 }
78 
79 // Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
correct_bad_coords(const Sk4f & bad,Sk4f * c1,Sk4f * c2,Sk4f * c3)80 static AI void correct_bad_coords(const Sk4f& bad, Sk4f* c1, Sk4f* c2, Sk4f* c3) {
81     if (bad.anyTrue()) {
82         *c1 = bad.thenElse(nextCCW(*c1), *c1);
83         *c2 = bad.thenElse(nextCCW(*c2), *c2);
84         if (c3) {
85             *c3 = bad.thenElse(nextCCW(*c3), *c3);
86         }
87     }
88 }
89 
get_metadata(const Vertices & vertices,GrQuadAAFlags aaFlags)90 static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
91     Sk4f dx = nextCCW(vertices.fX) - vertices.fX;
92     Sk4f dy = nextCCW(vertices.fY) - vertices.fY;
93     Sk4f invLengths = fma(dx, dx, dy * dy).rsqrt();
94 
95     Sk4f mask = aaFlags == GrQuadAAFlags::kAll ? Sk4f(1.f) :
96             Sk4f((GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
97                  (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
98                  (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
99                  (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f);
100     return { dx * invLengths, dy * invLengths, invLengths, mask };
101 }
102 
get_edge_equations(const QuadMetadata & metadata,const Vertices & vertices)103 static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
104     Sk4f dx = metadata.fDX;
105     Sk4f dy = metadata.fDY;
106     // Correct for bad edges by copying adjacent edge information into the bad component
107     correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
108 
109     Sk4f c = fma(dx, vertices.fY, -dy * vertices.fX);
110     // Make sure normals point into the shape
111     Sk4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
112     if ((test < -kTolerance).anyTrue()) {
113         return {-dy, dx, -c, true};
114     } else {
115         return {dy, -dx, c, false};
116     }
117 }
118 
119 // Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
120 // edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
121 // false is returned and the degenerate slow-path should be used.
get_optimized_outset(const QuadMetadata & metadata,bool rectilinear,Sk4f * outset)122 static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, Sk4f* outset) {
123     if (rectilinear) {
124         *outset = 0.5f;
125         // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
126         return (metadata.fInvLengths <= 1.f).allTrue();
127     }
128 
129     if ((metadata.fInvLengths >= 1.f / kTolerance).anyTrue()) {
130         // Have an empty edge from a degenerate quad, so there's no hope
131         return false;
132     }
133 
134     // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
135     // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
136     Sk4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
137     // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
138     // and subtracting very large vectors in almost opposite directions leads to float errors
139     if ((cosTheta.abs() >= 0.9f).anyTrue()) {
140         return false;
141     }
142     *outset = 0.5f * (1.f - cosTheta * cosTheta).rsqrt(); // 1/2sin(theta)
143 
144     // When outsetting or insetting, the current edge's AA adds to the length:
145     //   cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
146     // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
147     Sk4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
148     Sk4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
149                       nextCCW(metadata.fMask) * nextCCW(*outset) +
150                       nextCW(metadata.fMask) * (*outset);
151     // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
152     // then use the slow path
153     Sk4f threshold = 0.1f - metadata.fInvLengths.invert();
154     return (edgeAdjust > threshold).allTrue() && (edgeAdjust < -threshold).allTrue();
155 }
156 
157 // Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
outset_vertices(const Sk4f & outset,const QuadMetadata & metadata,Vertices * quad)158 static AI void outset_vertices(const Sk4f& outset, const QuadMetadata& metadata, Vertices* quad) {
159     // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
160     // both its points need to be moved along their other edge vectors.
161     auto maskedOutset = -outset * nextCW(metadata.fMask);
162     auto maskedOutsetCW = outset * metadata.fMask;
163     // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
164     quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
165     quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
166     if (quad->fUVRCount > 0) {
167         // We want to extend the texture coords by the same proportion as the positions.
168         maskedOutset *= metadata.fInvLengths;
169         maskedOutsetCW *= nextCW(metadata.fInvLengths);
170         Sk4f du = nextCCW(quad->fU) - quad->fU;
171         Sk4f dv = nextCCW(quad->fV) - quad->fV;
172         quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
173         quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
174         if (quad->fUVRCount == 3) {
175             Sk4f dr = nextCCW(quad->fR) - quad->fR;
176             quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
177         }
178     }
179 }
180 
181 // Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
182 // Gracefully handles 2D content if *w holds all 1s.
outset_projected_vertices(const Sk4f & x2d,const Sk4f & y2d,GrQuadAAFlags aaFlags,Vertices * quad)183 static void outset_projected_vertices(const Sk4f& x2d, const Sk4f& y2d,
184                                       GrQuadAAFlags aaFlags, Vertices* quad) {
185     // Left to right, in device space, for each point
186     Sk4f e1x = SkNx_shuffle<2, 3, 2, 3>(quad->fX) - SkNx_shuffle<0, 1, 0, 1>(quad->fX);
187     Sk4f e1y = SkNx_shuffle<2, 3, 2, 3>(quad->fY) - SkNx_shuffle<0, 1, 0, 1>(quad->fY);
188     Sk4f e1w = SkNx_shuffle<2, 3, 2, 3>(quad->fW) - SkNx_shuffle<0, 1, 0, 1>(quad->fW);
189     correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
190 
191     // // Top to bottom, in device space, for each point
192     Sk4f e2x = SkNx_shuffle<1, 1, 3, 3>(quad->fX) - SkNx_shuffle<0, 0, 2, 2>(quad->fX);
193     Sk4f e2y = SkNx_shuffle<1, 1, 3, 3>(quad->fY) - SkNx_shuffle<0, 0, 2, 2>(quad->fY);
194     Sk4f e2w = SkNx_shuffle<1, 1, 3, 3>(quad->fW) - SkNx_shuffle<0, 0, 2, 2>(quad->fW);
195     correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
196 
197     // Can only move along e1 and e2 to reach the new 2D point, so we have
198     // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
199     // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
200     // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
201     // the cNx and cNy coefficients are:
202     Sk4f c1x = e1w * x2d - e1x;
203     Sk4f c1y = e1w * y2d - e1y;
204     Sk4f c2x = e2w * x2d - e2x;
205     Sk4f c2y = e2w * y2d - e2y;
206     Sk4f c3x = quad->fW * x2d - quad->fX;
207     Sk4f c3y = quad->fW * y2d - quad->fY;
208 
209     // Solve for a and b
210     Sk4f a, b, denom;
211     if (aaFlags == GrQuadAAFlags::kAll) {
212         // When every edge is outset/inset, each corner can use both edge vectors
213         denom = c1x * c2y - c2x * c1y;
214         a = (c2x * c3y - c3x * c2y) / denom;
215         b = (c3x * c1y - c1x * c3y) / denom;
216     } else {
217         // Force a or b to be 0 if that edge cannot be used due to non-AA
218         // FIXME requires the extra > 0.f, since Sk4f's thenElse only works if true values have
219         // all their bits set to 1.
220         Sk4f aMask = Sk4f((aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
221                    (aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
222                    (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f,
223                    (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f) > 0.f;
224         Sk4f bMask = Sk4f((aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
225                    (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f,
226                    (aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
227                    (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f) > 0.f;
228 
229         // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
230         // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
231         // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
232         // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
233         Sk4f useC1x = c1x.abs() > c1y.abs();
234         Sk4f useC2x = c2x.abs() > c2y.abs();
235         //                                    -------- A & B ------      --------- A & !B ---------
236         denom = aMask.thenElse(bMask.thenElse(c1x * c2y - c2x * c1y,     useC1x.thenElse(c1x, c1y)),
237         //                                    ------- !A & B ----------  - !A & !B -
238                                bMask.thenElse(useC2x.thenElse(c2x, c2y), 1.0f));
239         //                                -------- A & B ------  ---------- A & !B ----------
240         a = aMask.thenElse(bMask.thenElse(c2x * c3y - c3x * c2y, useC1x.thenElse(-c3x, -c3y)),
241         //                 - !A -
242                            0.0f) / denom;
243         //                                -------- A & B ------  ---------- !A & B ----------
244         b = bMask.thenElse(aMask.thenElse(c3x * c1y - c1x * c3y, useC2x.thenElse(-c3x, -c3y)),
245         //                 - !B -
246                            0.0f) / denom;
247     }
248 
249     quad->fX += a * e1x + b * e2x;
250     quad->fY += a * e1y + b * e2y;
251     quad->fW += a * e1w + b * e2w;
252     correct_bad_coords(denom.abs() < kTolerance, &quad->fX, &quad->fY, &quad->fW);
253 
254     if (quad->fUVRCount > 0) {
255         // Calculate R here so it can be corrected with U and V in case it's needed later
256         Sk4f e1u = SkNx_shuffle<2, 3, 2, 3>(quad->fU) - SkNx_shuffle<0, 1, 0, 1>(quad->fU);
257         Sk4f e1v = SkNx_shuffle<2, 3, 2, 3>(quad->fV) - SkNx_shuffle<0, 1, 0, 1>(quad->fV);
258         Sk4f e1r = SkNx_shuffle<2, 3, 2, 3>(quad->fR) - SkNx_shuffle<0, 1, 0, 1>(quad->fR);
259         correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
260 
261         Sk4f e2u = SkNx_shuffle<1, 1, 3, 3>(quad->fU) - SkNx_shuffle<0, 0, 2, 2>(quad->fU);
262         Sk4f e2v = SkNx_shuffle<1, 1, 3, 3>(quad->fV) - SkNx_shuffle<0, 0, 2, 2>(quad->fV);
263         Sk4f e2r = SkNx_shuffle<1, 1, 3, 3>(quad->fR) - SkNx_shuffle<0, 0, 2, 2>(quad->fR);
264         correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
265 
266         quad->fU += a * e1u + b * e2u;
267         quad->fV += a * e1v + b * e2v;
268         if (quad->fUVRCount == 3) {
269             quad->fR += a * e1r + b * e2r;
270             correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, &quad->fR);
271         } else {
272             correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, nullptr);
273         }
274     }
275 }
276 
277 // Calculate area of intersection between quad (xs, ys) and a pixel at 'pixelCenter'.
278 // a, b, c are edge equations of the quad, flipped is true if the line equations had their normals
279 // reversed to correct for matrix transforms.
get_exact_coverage(const SkPoint & pixelCenter,const Vertices & quad,const Edges & edges)280 static float get_exact_coverage(const SkPoint& pixelCenter, const Vertices& quad,
281                                 const Edges& edges) {
282      // Ordering of vertices given default tri-strip that produces CCW points
283     static const int kCCW[] = {0, 1, 3, 2};
284     // Ordering of vertices given inverted tri-strip that produces CCW
285     static const int kFlippedCCW[] = {0, 2, 3, 1};
286 
287     // Edge boundaries of the pixel
288     float left = pixelCenter.fX - 0.5f;
289     float right = pixelCenter.fX + 0.5f;
290     float top = pixelCenter.fY - 0.5f;
291     float bot = pixelCenter.fY + 0.5f;
292 
293     // Whether or not the 4 corners of the pixel are inside the quad geometry. Variable names are
294     // intentional to work easily with the helper macros.
295     bool topleftInside = ((edges.fA * left + edges.fB * top + edges.fC) >= 0.f).allTrue();
296     bool botleftInside = ((edges.fA * left + edges.fB * bot + edges.fC) >= 0.f).allTrue();
297     bool botrightInside = ((edges.fA * right + edges.fB * bot + edges.fC) >= 0.f).allTrue();
298     bool toprightInside = ((edges.fA * right + edges.fB * top + edges.fC) >= 0.f).allTrue();
299     if (topleftInside && botleftInside && botrightInside && toprightInside) {
300         // Quad fully contains the pixel, so we know the area will be 1.f
301         return 1.f;
302     }
303 
304     // Track whether or not the quad vertices in (xs, ys) are on the proper sides of l, t, r, and b
305     Sk4i leftValid = SkNx_cast<int32_t>(quad.fX >= left);
306     Sk4i rightValid = SkNx_cast<int32_t>(quad.fX <= right);
307     Sk4i topValid = SkNx_cast<int32_t>(quad.fY >= top);
308     Sk4i botValid = SkNx_cast<int32_t>(quad.fY <= bot);
309 
310     // Intercepts of quad lines with the 4 pixel edges
311     Sk4f leftCross = -(edges.fC + edges.fA * left) / edges.fB;
312     Sk4f rightCross = -(edges.fC + edges.fA * right) / edges.fB;
313     Sk4f topCross = -(edges.fC + edges.fB * top) / edges.fA;
314     Sk4f botCross = -(edges.fC + edges.fB * bot) / edges.fA;
315 
316     // State for implicitly tracking the intersection boundary and area
317     SkPoint firstPoint = {0.f, 0.f};
318     SkPoint lastPoint = {0.f, 0.f};
319     bool intersected = false;
320     float area = 0.f;
321 
322     // Adds a point to the intersection hull, remembering first point (for closing) and the
323     // current point, and updates the running area total.
324     // See http://mathworld.wolfram.com/PolygonArea.html
325     auto accumulate = [&](const SkPoint& p) {
326         if (intersected) {
327             float da = lastPoint.fX * p.fY - p.fX * lastPoint.fY;
328             area += da;
329         } else {
330             firstPoint = p;
331             intersected = true;
332         }
333         lastPoint = p;
334     };
335 
336     // Used during iteration over the quad points to check if edge intersections are valid and
337     // should be accumulated.
338 #define ADD_EDGE_CROSSING_X(SIDE) \
339     do { \
340         if (SIDE##Cross[ei] >= top && SIDE##Cross[ei] <= bot) { \
341             accumulate({SIDE, SIDE##Cross[ei]}); \
342             addedIntersection = true; \
343         } \
344     } while(false)
345 #define ADD_EDGE_CROSSING_Y(SIDE) \
346     do { \
347         if (SIDE##Cross[ei] >= left && SIDE##Cross[ei] <= right) { \
348             accumulate({SIDE##Cross[ei], SIDE}); \
349             addedIntersection = true; \
350         } \
351     } while(false)
352 #define TEST_EDGES(SIDE, AXIS, I, NI) \
353     do { \
354         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
355             ADD_EDGE_CROSSING_##AXIS(SIDE); \
356             crossedEdges = true; \
357         } \
358     } while(false)
359     // Used during iteration over the quad points to check if a pixel corner should be included
360     // in the intersection boundary
361 #define ADD_CORNER(CHECK, SIDE_LR, SIDE_TB) \
362     if (!CHECK##Valid[i] || !CHECK##Valid[ni]) { \
363         if (SIDE_TB##SIDE_LR##Inside) { \
364             accumulate({SIDE_LR, SIDE_TB}); \
365         } \
366     }
367 #define TEST_CORNER_X(SIDE, I, NI) \
368     do { \
369         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
370             ADD_CORNER(top, SIDE, top) else ADD_CORNER(bot, SIDE, bot) \
371         } \
372     } while(false)
373 #define TEST_CORNER_Y(SIDE, I, NI) \
374     do { \
375         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
376             ADD_CORNER(left, left, SIDE) else ADD_CORNER(right, right, SIDE) \
377         } \
378     } while(false)
379 
380     // Iterate over the 4 points of the quad, adding valid intersections with the pixel edges
381     // or adding interior pixel corners as it goes. This automatically keeps all accumulated points
382     // in CCW ordering so the area can be calculated on the fly and there's no need to store the
383     // list of hull points. This is somewhat inspired by the Sutherland-Hodgman algorithm but since
384     // there are only 4 points in each source polygon, there is no point list maintenance.
385     for (int j = 0; j < 4; ++j) {
386         // Current vertex
387         int i = edges.fFlipped ? kFlippedCCW[j] : kCCW[j];
388         // Moving to this vertex
389         int ni = edges.fFlipped ? kFlippedCCW[(j + 1) % 4] : kCCW[(j + 1) % 4];
390         // Index in edge vectors corresponding to move from i to ni
391         int ei = edges.fFlipped ? ni : i;
392 
393         bool crossedEdges = false;
394         bool addedIntersection = false;
395 
396         // First check if there are any outside -> inside edge crossings. There can be 0, 1, or 2.
397         // 2 can occur if one crossing is still outside the pixel, or if they both go through
398         // the corner (in which case a duplicate point is added, but that doesn't change area).
399 
400         // Outside to inside crossing
401         TEST_EDGES(left, X, i, ni);
402         TEST_EDGES(right, X, i, ni);
403         TEST_EDGES(top, Y, i, ni);
404         TEST_EDGES(bot, Y, i, ni);
405         // Inside to outside crossing (swapping ni and i in the boolean test)
406         TEST_EDGES(left, X, ni, i);
407         TEST_EDGES(right, X, ni, i);
408         TEST_EDGES(top, Y, ni, i);
409         TEST_EDGES(bot, Y, ni, i);
410 
411         // If we crossed edges but didn't add any intersections, check the corners of the pixel.
412         // If the pixel corners are inside the quad, include them in the boundary.
413         if (crossedEdges && !addedIntersection) {
414             // This can lead to repeated points, but those just accumulate zero area
415             TEST_CORNER_X(left, i, ni);
416             TEST_CORNER_X(right, i, ni);
417             TEST_CORNER_Y(top, i, ni);
418             TEST_CORNER_Y(bot, i, ni);
419 
420             TEST_CORNER_X(left, ni, i);
421             TEST_CORNER_X(right, ni, i);
422             TEST_CORNER_Y(top, ni, i);
423             TEST_CORNER_Y(bot, ni, i);
424         }
425 
426         // Lastly, if the next point is completely inside the pixel it gets included in the boundary
427         if (leftValid[ni] && rightValid[ni] && topValid[ni] && botValid[ni]) {
428             accumulate({quad.fX[ni], quad.fY[ni]});
429         }
430     }
431 
432 #undef TEST_CORNER_Y
433 #undef TEST_CORNER_X
434 #undef ADD_CORNER
435 
436 #undef TEST_EDGES
437 #undef ADD_EDGE_CROSSING_Y
438 #undef ADD_EDGE_CROSSING_X
439 
440     // After all points have been considered, close the boundary to get final area. If we never
441     // added any points, it means the quad didn't intersect the pixel rectangle.
442     if (intersected) {
443         // Final equation for area of convex polygon is to multiply by -1/2 (minus since the points
444         // were in CCW order).
445         accumulate(firstPoint);
446         return -0.5f * area;
447     } else {
448         return 0.f;
449     }
450 }
451 
452 // Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
453 // parallel, or edges are very short/zero-length. Returns coverage for each vertex.
454 // Requires (dx, dy) to already be fixed for empty edges.
compute_degenerate_quad(GrQuadAAFlags aaFlags,const Sk4f & mask,const Edges & edges,bool outset,Vertices * quad)455 static Sk4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const Sk4f& mask, const Edges& edges,
456                                     bool outset, Vertices* quad) {
457     // Move the edge 1/2 pixel in or out depending on 'outset'.
458     Sk4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
459 
460     // There are 6 points that we care about to determine the final shape of the polygon, which
461     // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
462     // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
463     Sk4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
464     Sk4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
465     Sk4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
466     correct_bad_coords(denom.abs() < kTolerance, &px, &py, nullptr);
467 
468     // Calculate the signed distances from these 4 corners to the other two edges that did not
469     // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
470     // p(3) to e0,e2
471     Sk4f dists1 = px * SkNx_shuffle<3, 3, 0, 0>(edges.fA) +
472                   py * SkNx_shuffle<3, 3, 0, 0>(edges.fB) +
473                   SkNx_shuffle<3, 3, 0, 0>(oc);
474     Sk4f dists2 = px * SkNx_shuffle<1, 2, 1, 2>(edges.fA) +
475                   py * SkNx_shuffle<1, 2, 1, 2>(edges.fB) +
476                   SkNx_shuffle<1, 2, 1, 2>(oc);
477 
478     // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
479     // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
480     // and we need to use a central point to represent it. If all four points are only on the
481     // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
482     // Otherwise, use a triangle that replaces the bad points with the intersections of
483     // (e1, e2) or (e0, e3) as needed.
484     Sk4f d1v0 = dists1 < kTolerance;
485     Sk4f d2v0 = dists2 < kTolerance;
486     // FIXME(michaelludwig): Sk4f has anyTrue() and allTrue(), but not & or |. Sk4i has & or | but
487     // not anyTrue() and allTrue(). Moving to SkVx from SkNx will clean this up.
488     Sk4i d1And2 = SkNx_cast<int32_t>(d1v0) & SkNx_cast<int32_t>(d2v0);
489     Sk4i d1Or2 = SkNx_cast<int32_t>(d1v0) | SkNx_cast<int32_t>(d2v0);
490 
491     Sk4f coverage;
492     if (!d1Or2[0] && !d1Or2[1] && !d1Or2[2] && !d1Or2[3]) {
493         // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
494         // and use full coverage
495         coverage = 1.f;
496     } else if (d1And2[0] || d1And2[1] || d1And2[2] || d1And2[2]) {
497         // A point failed against two edges, so reduce the shape to a single point, which we take as
498         // the center of the original quad to ensure it is contained in the intended geometry. Since
499         // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
500         SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
501                           0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
502         coverage = get_exact_coverage(center, *quad, edges);
503         px = center.fX;
504         py = center.fY;
505     } else if (d1Or2[0] && d1Or2[1] && d1Or2[2] && d1Or2[3]) {
506         // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
507         // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
508         if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
509             // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
510             px = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(px) + SkNx_shuffle<2, 3, 2, 3>(px));
511             py = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(py) + SkNx_shuffle<2, 3, 2, 3>(py));
512             float mc02 = get_exact_coverage({px[0], py[0]}, *quad, edges);
513             float mc13 = get_exact_coverage({px[1], py[1]}, *quad, edges);
514             coverage = Sk4f(mc02, mc13, mc02, mc13);
515         } else {
516             // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
517             px = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(px) + SkNx_shuffle<1, 1, 3, 3>(px));
518             py = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(py) + SkNx_shuffle<1, 1, 3, 3>(py));
519             float mc01 = get_exact_coverage({px[0], py[0]}, *quad, edges);
520             float mc23 = get_exact_coverage({px[2], py[2]}, *quad, edges);
521             coverage = Sk4f(mc01, mc01, mc23, mc23);
522         }
523     } else {
524         // This turns into a triangle. Replace corners as needed with the intersections between
525         // (e0,e3) and (e1,e2), which must now be calculated
526         Sk2f eDenom = SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(edges.fB) -
527                       SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(edges.fA);
528         Sk2f ex = (SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(oc) -
529                    SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fB)) / eDenom;
530         Sk2f ey = (SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fA) -
531                    SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(oc)) / eDenom;
532 
533         if (SkScalarAbs(eDenom[0]) > kTolerance) {
534             px = d1v0.thenElse(ex[0], px);
535             py = d1v0.thenElse(ey[0], py);
536         }
537         if (SkScalarAbs(eDenom[1]) > kTolerance) {
538             px = d2v0.thenElse(ex[1], px);
539             py = d2v0.thenElse(ey[1], py);
540         }
541 
542         coverage = 1.f;
543     }
544 
545     outset_projected_vertices(px, py, aaFlags, quad);
546     return coverage;
547 }
548 
549 // Computes the vertices for the two nested quads used to create AA edges. The original single quad
550 // should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
551 // stored in-place on return. Returns per-vertex coverage for the inner vertices.
compute_nested_quad_vertices(GrQuadAAFlags aaFlags,bool rectilinear,Vertices * inner,Vertices * outer)552 static Sk4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
553                                          Vertices* inner, Vertices* outer) {
554     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
555     SkASSERT(outer->fUVRCount == inner->fUVRCount);
556 
557     QuadMetadata metadata = get_metadata(*inner, aaFlags);
558 
559     // When outsetting, we want the new edge to be .5px away from the old line, which means the
560     // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
561     // is only computed if there are no empty edges, and it may signal going through the slow path.
562     Sk4f outset = 0.5f;
563     if (get_optimized_outset(metadata, rectilinear, &outset)) {
564        // Since it's not subpixel, outsetting and insetting are trivial vector additions.
565         outset_vertices(outset, metadata, outer);
566         outset_vertices(-outset, metadata, inner);
567         return 1.f;
568     }
569 
570     // Only compute edge equations once since they are the same for inner and outer quads
571     Edges edges = get_edge_equations(metadata, *inner);
572 
573     // Calculate both outset and inset, returning the coverage reported for the inset, since the
574     // outset will always have 0.0f.
575     compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
576     return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
577 }
578 
579 // Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
580 // division of the device coordinates, the original local coordinate value is at the original
581 // un-outset device position.
compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags,Vertices * inner,Vertices * outer)582 static Sk4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
583                                                Vertices* outer) {
584     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
585     SkASSERT(outer->fUVRCount == inner->fUVRCount);
586 
587     // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
588     // Don't use Sk4f.invert() here because it does not preserve 1/1 == 1, which creates rendering
589     // mismatches for 2D content that was batched into a 3D op, vs. 2D on its own.
590     Sk4f iw = 1.0f / inner->fW;
591     Sk4f x2d = inner->fX * iw;
592     Sk4f y2d = inner->fY * iw;
593 
594     Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
595     Vertices outer2D = inner2D;
596 
597     Sk4f coverage = compute_nested_quad_vertices(aaFlags, /* rect */ false, &inner2D, &outer2D);
598 
599     // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
600     outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
601     outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
602 
603     return coverage;
604 }
605 
606 enum class CoverageMode {
607     kNone,
608     kWithPosition,
609     kWithColor
610 };
611 
get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec & spec)612 static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
613     if (spec.usesCoverageAA()) {
614         if (spec.compatibleWithAlphaAsCoverage() && spec.hasVertexColors()) {
615             return CoverageMode::kWithColor;
616         } else {
617             return CoverageMode::kWithPosition;
618         }
619     } else {
620         return CoverageMode::kNone;
621     }
622 }
623 
624 // Writes four vertices in triangle strip order, including the additional data for local
625 // coordinates, domain, color, and coverage as needed to satisfy the vertex spec.
write_quad(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,CoverageMode mode,Sk4f coverage,SkPMColor4f color4f,const SkRect & domain,const Vertices & quad)626 static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
627                        CoverageMode mode, Sk4f coverage, SkPMColor4f color4f, const SkRect& domain,
628                        const Vertices& quad) {
629     static constexpr auto If = GrVertexWriter::If<float>;
630 
631     for (int i = 0; i < 4; ++i) {
632         // save position, this is a float2 or float3 or float4 depending on the combination of
633         // perspective and coverage mode.
634         vb->write(quad.fX[i], quad.fY[i],
635                   If(spec.deviceQuadType() == GrQuadType::kPerspective, quad.fW[i]),
636                   If(mode == CoverageMode::kWithPosition, coverage[i]));
637 
638         // save color
639         if (spec.hasVertexColors()) {
640             bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
641             vb->write(GrVertexColor(
642                     color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
643         }
644 
645         // save local position
646         if (spec.hasLocalCoords()) {
647             vb->write(quad.fU[i], quad.fV[i],
648                       If(spec.localQuadType() == GrQuadType::kPerspective, quad.fR[i]));
649         }
650 
651         // save the domain
652         if (spec.hasDomain()) {
653             vb->write(domain);
654         }
655     }
656 }
657 
658 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
659 
660 static const int kVertsPerAAFillRect = 8;
661 static const int kIndicesPerAAFillRect = 30;
662 
get_index_buffer(GrResourceProvider * resourceProvider)663 static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
664     GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
665 
666     // clang-format off
667     static const uint16_t gFillAARectIdx[] = {
668         0, 1, 2, 1, 3, 2,
669         0, 4, 1, 4, 5, 1,
670         0, 6, 4, 0, 2, 6,
671         2, 3, 6, 3, 7, 6,
672         1, 5, 3, 3, 5, 7,
673     };
674     // clang-format on
675 
676     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
677     return resourceProvider->findOrCreatePatternedIndexBuffer(
678             gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
679             kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
680 }
681 
682 } // anonymous namespace
683 
684 namespace GrQuadPerEdgeAA {
685 
MinColorType(SkPMColor4f color)686 ColorType MinColorType(SkPMColor4f color) {
687     if (color == SK_PMColor4fWHITE) {
688         return ColorType::kNone;
689     } else if (color.fitsInBytes()) {
690         return ColorType::kByte;
691     } else {
692         return ColorType::kHalf;
693     }
694 }
695 
696 ////////////////// Tessellate Implementation
697 
Tessellate(void * vertices,const VertexSpec & spec,const GrPerspQuad & deviceQuad,const SkPMColor4f & color4f,const GrPerspQuad & localQuad,const SkRect & domain,GrQuadAAFlags aaFlags)698 void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
699                  const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
700                  GrQuadAAFlags aaFlags) {
701     CoverageMode mode = get_mode_for_spec(spec);
702 
703     // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
704     Vertices outer;
705     outer.fX = deviceQuad.x4f();
706     outer.fY = deviceQuad.y4f();
707     outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
708 
709     // Load local position data into Sk4fs (either none, just u,v or all three)
710     outer.fUVRCount = spec.localDimensionality();
711     if (spec.hasLocalCoords()) {
712         outer.fU = localQuad.x4f();
713         outer.fV = localQuad.y4f();
714         outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
715     }
716 
717     GrVertexWriter vb{vertices};
718     if (spec.usesCoverageAA()) {
719         SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
720         // Must calculate two new quads, an outset and inset by .5 in projected device space, so
721         // duplicate the original quad for the inner space
722         Vertices inner = outer;
723 
724         Sk4f maxCoverage = 1.f;
725         if (spec.deviceQuadType() == GrQuadType::kPerspective) {
726             // For perspective, send quads with all edges non-AA through the tessellation to ensure
727             // their corners are processed the same as adjacent quads. This approach relies on
728             // solving edge equations to reconstruct corners, which can create seams if an inner
729             // fully non-AA quad is not similarly processed.
730             maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer);
731         } else if (aaFlags != GrQuadAAFlags::kNone) {
732             // In 2D, the simpler corner math does not cause issues with seaming against non-AA
733             // inner quads.
734             maxCoverage = compute_nested_quad_vertices(
735                     aaFlags, spec.deviceQuadType() <= GrQuadType::kRectilinear, &inner, &outer);
736         }
737         // NOTE: could provide an even more optimized tessellation function for axis-aligned
738         // rects since the positions can be outset by constants without doing vector math,
739         // except it must handle identifying the winding of the quad vertices if the transform
740         // applied a mirror, etc. The current 2D case is already adequately fast.
741 
742         // Write two quads for inner and outer, inner will use the
743         write_quad(&vb, spec, mode, maxCoverage, color4f, domain, inner);
744         write_quad(&vb, spec, mode, 0.f, color4f, domain, outer);
745     } else {
746         // No outsetting needed, just write a single quad with full coverage
747         SkASSERT(mode == CoverageMode::kNone);
748         write_quad(&vb, spec, mode, 1.f, color4f, domain, outer);
749     }
750 
751     return vb.fPtr;
752 }
753 
ConfigureMeshIndices(GrMeshDrawOp::Target * target,GrMesh * mesh,const VertexSpec & spec,int quadCount)754 bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
755                           int quadCount) {
756     if (spec.usesCoverageAA()) {
757         // AA quads use 8 vertices, basically nested rectangles
758         sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
759         if (!ibuffer) {
760             return false;
761         }
762 
763         mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
764         mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
765                                   quadCount, kNumAAQuadsInIndexBuffer);
766     } else {
767         // Non-AA quads use 4 vertices, and regular triangle strip layout
768         if (quadCount > 1) {
769             sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
770             if (!ibuffer) {
771                 return false;
772             }
773 
774             mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
775             mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
776                                       GrResourceProvider::QuadCountOfQuadBuffer());
777         } else {
778             mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
779             mesh->setNonIndexedNonInstanced(4);
780         }
781     }
782 
783     return true;
784 }
785 
786 ////////////////// VertexSpec Implementation
787 
deviceDimensionality() const788 int VertexSpec::deviceDimensionality() const {
789     return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
790 }
791 
localDimensionality() const792 int VertexSpec::localDimensionality() const {
793     return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
794 }
795 
796 ////////////////// Geometry Processor Implementation
797 
798 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
799 public:
800 
Make(const VertexSpec & spec)801     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
802         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
803     }
804 
Make(const VertexSpec & vertexSpec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)805     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
806                                            GrTextureType textureType, GrPixelConfig textureConfig,
807                                            const GrSamplerState& samplerState,
808                                            uint32_t extraSamplerKey,
809                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
810         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
811                 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
812                 std::move(textureColorSpaceXform)));
813     }
814 
name() const815     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
816 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const817     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
818         // domain, texturing, device-dimensions are single bit flags
819         uint32_t x = fDomain.isInitialized() ? 0 : 1;
820         x |= fSampler.isInitialized() ? 0 : 2;
821         x |= fNeedsPerspective ? 0 : 4;
822         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
823         if (fLocalCoord.isInitialized()) {
824             x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
825         }
826         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
827         if (fColor.isInitialized()) {
828             x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
829         }
830         // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
831         if (fCoverageMode != CoverageMode::kNone) {
832             x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
833         }
834 
835         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
836         b->add32(x);
837     }
838 
createGLSLInstance(const GrShaderCaps & caps) const839     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
840         class GLSLProcessor : public GrGLSLGeometryProcessor {
841         public:
842             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
843                          FPCoordTransformIter&& transformIter) override {
844                 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
845                 if (gp.fLocalCoord.isInitialized()) {
846                     this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
847                 }
848                 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
849             }
850 
851         private:
852             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
853                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
854 
855                 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
856                 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
857                                                        gp.fTextureColorSpaceXform.get());
858 
859                 args.fVaryingHandler->emitAttributes(gp);
860 
861                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
862                     // Strip last channel from the vertex attribute to remove coverage and get the
863                     // actual position
864                     if (gp.fNeedsPerspective) {
865                         args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
866                                                        gp.fPosition.name());
867                     } else {
868                         args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
869                                                        gp.fPosition.name());
870                     }
871                     gpArgs->fPositionVar = {"position",
872                                             gp.fNeedsPerspective ? kFloat3_GrSLType
873                                                                  : kFloat2_GrSLType,
874                                             GrShaderVar::kNone_TypeModifier};
875                 } else {
876                     // No coverage to eliminate
877                     gpArgs->fPositionVar = gp.fPosition.asShaderVar();
878                 }
879 
880                 // Handle local coordinates if they exist
881                 if (gp.fLocalCoord.isInitialized()) {
882                     // NOTE: If the only usage of local coordinates is for the inline texture fetch
883                     // before FPs, then there are no registered FPCoordTransforms and this ends up
884                     // emitting nothing, so there isn't a duplication of local coordinates
885                     this->emitTransforms(args.fVertBuilder,
886                                          args.fVaryingHandler,
887                                          args.fUniformHandler,
888                                          gp.fLocalCoord.asShaderVar(),
889                                          args.fFPCoordTransformHandler);
890                 }
891 
892                 // Solid color before any texturing gets modulated in
893                 if (gp.fColor.isInitialized()) {
894                     // The color cannot be flat if the varying coverage has been modulated into it
895                     args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
896                             gp.fCoverageMode == CoverageMode::kWithColor ?
897                             Interpolation::kInterpolated : Interpolation::kCanBeFlat);
898                 } else {
899                     // Output color must be initialized to something
900                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
901                 }
902 
903                 // If there is a texture, must also handle texture coordinates and reading from
904                 // the texture in the fragment shader before continuing to fragment processors.
905                 if (gp.fSampler.isInitialized()) {
906                     // Texture coordinates clamped by the domain on the fragment shader; if the GP
907                     // has a texture, it's guaranteed to have local coordinates
908                     args.fFragBuilder->codeAppend("float2 texCoord;");
909                     if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
910                         // Can't do a pass through since we need to perform perspective division
911                         GrGLSLVarying v(gp.fLocalCoord.gpuType());
912                         args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
913                         args.fVertBuilder->codeAppendf("%s = %s;",
914                                                        v.vsOut(), gp.fLocalCoord.name());
915                         args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
916                                                        v.fsIn(), v.fsIn());
917                     } else {
918                         args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
919                     }
920 
921                     // Clamp the now 2D localCoordName variable by the domain if it is provided
922                     if (gp.fDomain.isInitialized()) {
923                         args.fFragBuilder->codeAppend("float4 domain;");
924                         args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
925                                                                       Interpolation::kCanBeFlat);
926                         args.fFragBuilder->codeAppend(
927                                 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
928                     }
929 
930                     // Now modulate the starting output color by the texture lookup
931                     args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
932                     args.fFragBuilder->appendTextureLookupAndModulate(
933                         args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
934                         &fTextureColorSpaceXformHelper);
935                     args.fFragBuilder->codeAppend(";");
936                 }
937 
938                 // And lastly, output the coverage calculation code
939                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
940                     GrGLSLVarying coverage(kFloat_GrSLType);
941                     args.fVaryingHandler->addVarying("coverage", &coverage);
942                     if (gp.fNeedsPerspective) {
943                         args.fVertBuilder->codeAppendf("%s = %s.w;",
944                                                        coverage.vsOut(), gp.fPosition.name());
945                     } else {
946                         args.fVertBuilder->codeAppendf("%s = %s.z;",
947                                                        coverage.vsOut(), gp.fPosition.name());
948                     }
949 
950                     args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
951                                                    args.fOutputCoverage, coverage.fsIn());
952                 } else {
953                     // Set coverage to 1, since it's either non-AA or the coverage was already
954                     // folded into the output color
955                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
956                 }
957             }
958             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
959         };
960         return new GLSLProcessor;
961     }
962 
963 private:
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec)964     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
965             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
966             , fTextureColorSpaceXform(nullptr) {
967         SkASSERT(!spec.hasDomain());
968         this->initializeAttrs(spec);
969         this->setTextureSamplerCnt(0);
970     }
971 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)972     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
973                                    GrTextureType textureType, GrPixelConfig textureConfig,
974                                    const GrSamplerState& samplerState,
975                                    uint32_t extraSamplerKey,
976                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform)
977             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
978             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
979             , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
980         SkASSERT(spec.hasLocalCoords());
981         this->initializeAttrs(spec);
982         this->setTextureSamplerCnt(1);
983     }
984 
initializeAttrs(const VertexSpec & spec)985     void initializeAttrs(const VertexSpec& spec) {
986         fNeedsPerspective = spec.deviceDimensionality() == 3;
987         fCoverageMode = get_mode_for_spec(spec);
988 
989         if (fCoverageMode == CoverageMode::kWithPosition) {
990             if (fNeedsPerspective) {
991                 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
992             } else {
993                 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
994             }
995         } else {
996             if (fNeedsPerspective) {
997                 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
998             } else {
999                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1000             }
1001         }
1002 
1003         int localDim = spec.localDimensionality();
1004         if (localDim == 3) {
1005             fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1006         } else if (localDim == 2) {
1007             fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1008         } // else localDim == 0 and attribute remains uninitialized
1009 
1010         if (ColorType::kByte == spec.colorType()) {
1011             fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1012         } else if (ColorType::kHalf == spec.colorType()) {
1013             fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1014         }
1015 
1016         if (spec.hasDomain()) {
1017             fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1018         }
1019 
1020         this->setVertexAttributes(&fPosition, 4);
1021     }
1022 
onTextureSampler(int) const1023     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1024 
1025     Attribute fPosition; // May contain coverage as last channel
1026     Attribute fColor; // May have coverage modulated in if the FPs support it
1027     Attribute fLocalCoord;
1028     Attribute fDomain;
1029 
1030     // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1031     // and may mean 2d with coverage, or 3d with no coverage
1032     bool fNeedsPerspective;
1033     CoverageMode fCoverageMode;
1034 
1035     // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1036     // to skip texturing.
1037     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1038     TextureSampler fSampler;
1039 
1040     typedef GrGeometryProcessor INHERITED;
1041 };
1042 
MakeProcessor(const VertexSpec & spec)1043 sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1044     return QuadPerEdgeAAGeometryProcessor::Make(spec);
1045 }
1046 
MakeTexturedProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)1047 sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1048         GrTextureType textureType, GrPixelConfig textureConfig,
1049         const GrSamplerState& samplerState, uint32_t extraSamplerKey,
1050         sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1051     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
1052                                                 samplerState, extraSamplerKey,
1053                                                 std::move(textureColorSpaceXform));
1054 }
1055 
1056 } // namespace GrQuadPerEdgeAA
1057