• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 "GrAAConvexPathRenderer.h"
9 #include "GrCaps.h"
10 #include "GrContext.h"
11 #include "GrDrawOpTest.h"
12 #include "GrGeometryProcessor.h"
13 #include "GrPathUtils.h"
14 #include "GrProcessor.h"
15 #include "GrRenderTargetContext.h"
16 #include "GrShape.h"
17 #include "GrSimpleMeshDrawOpHelper.h"
18 #include "GrVertexWriter.h"
19 #include "SkGeometry.h"
20 #include "SkPathPriv.h"
21 #include "SkPointPriv.h"
22 #include "SkString.h"
23 #include "SkTypes.h"
24 #include "glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "glsl/GrGLSLGeometryProcessor.h"
26 #include "glsl/GrGLSLProgramDataManager.h"
27 #include "glsl/GrGLSLUniformHandler.h"
28 #include "glsl/GrGLSLVarying.h"
29 #include "glsl/GrGLSLVertexGeoBuilder.h"
30 #include "ops/GrMeshDrawOp.h"
31 
GrAAConvexPathRenderer()32 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
33 }
34 
35 struct Segment {
36     enum {
37         // These enum values are assumed in member functions below.
38         kLine = 0,
39         kQuad = 1,
40     } fType;
41 
42     // line uses one pt, quad uses 2 pts
43     SkPoint fPts[2];
44     // normal to edge ending at each pt
45     SkVector fNorms[2];
46     // is the corner where the previous segment meets this segment
47     // sharp. If so, fMid is a normalized bisector facing outward.
48     SkVector fMid;
49 
countPointsSegment50     int countPoints() {
51         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
52         return fType + 1;
53     }
endPtSegment54     const SkPoint& endPt() const {
55         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56         return fPts[fType];
57     }
endNormSegment58     const SkPoint& endNorm() const {
59         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60         return fNorms[fType];
61     }
62 };
63 
64 typedef SkTArray<Segment, true> SegmentArray;
65 
center_of_mass(const SegmentArray & segments,SkPoint * c)66 static bool center_of_mass(const SegmentArray& segments, SkPoint* c) {
67     SkScalar area = 0;
68     SkPoint center = {0, 0};
69     int count = segments.count();
70     SkPoint p0 = {0, 0};
71     if (count > 2) {
72         // We translate the polygon so that the first point is at the origin.
73         // This avoids some precision issues with small area polygons far away
74         // from the origin.
75         p0 = segments[0].endPt();
76         SkPoint pi;
77         SkPoint pj;
78         // the first and last iteration of the below loop would compute
79         // zeros since the starting / ending point is (0,0). So instead we start
80         // at i=1 and make the last iteration i=count-2.
81         pj = segments[1].endPt() - p0;
82         for (int i = 1; i < count - 1; ++i) {
83             pi = pj;
84             pj = segments[i + 1].endPt() - p0;
85 
86             SkScalar t = SkPoint::CrossProduct(pi, pj);
87             area += t;
88             center.fX += (pi.fX + pj.fX) * t;
89             center.fY += (pi.fY + pj.fY) * t;
90         }
91     }
92 
93     // If the poly has no area then we instead return the average of
94     // its points.
95     if (SkScalarNearlyZero(area)) {
96         SkPoint avg;
97         avg.set(0, 0);
98         for (int i = 0; i < count; ++i) {
99             const SkPoint& pt = segments[i].endPt();
100             avg.fX += pt.fX;
101             avg.fY += pt.fY;
102         }
103         SkScalar denom = SK_Scalar1 / count;
104         avg.scale(denom);
105         *c = avg;
106     } else {
107         area *= 3;
108         area = SkScalarInvert(area);
109         center.scale(area);
110         // undo the translate of p0 to the origin.
111         *c = center + p0;
112     }
113     return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
114 }
115 
compute_vectors(SegmentArray * segments,SkPoint * fanPt,SkPathPriv::FirstDirection dir,int * vCount,int * iCount)116 static bool compute_vectors(SegmentArray* segments,
117                             SkPoint* fanPt,
118                             SkPathPriv::FirstDirection dir,
119                             int* vCount,
120                             int* iCount) {
121     if (!center_of_mass(*segments, fanPt)) {
122         return false;
123     }
124     int count = segments->count();
125 
126     // Make the normals point towards the outside
127     SkPointPriv::Side normSide;
128     if (dir == SkPathPriv::kCCW_FirstDirection) {
129         normSide = SkPointPriv::kRight_Side;
130     } else {
131         normSide = SkPointPriv::kLeft_Side;
132     }
133 
134     int64_t vCount64 = 0;
135     int64_t iCount64 = 0;
136     // compute normals at all points
137     for (int a = 0; a < count; ++a) {
138         Segment& sega = (*segments)[a];
139         int b = (a + 1) % count;
140         Segment& segb = (*segments)[b];
141 
142         const SkPoint* prevPt = &sega.endPt();
143         int n = segb.countPoints();
144         for (int p = 0; p < n; ++p) {
145             segb.fNorms[p] = segb.fPts[p] - *prevPt;
146             segb.fNorms[p].normalize();
147             segb.fNorms[p] = SkPointPriv::MakeOrthog(segb.fNorms[p], normSide);
148             prevPt = &segb.fPts[p];
149         }
150         if (Segment::kLine == segb.fType) {
151             vCount64 += 5;
152             iCount64 += 9;
153         } else {
154             vCount64 += 6;
155             iCount64 += 12;
156         }
157     }
158 
159     // compute mid-vectors where segments meet. TODO: Detect shallow corners
160     // and leave out the wedges and close gaps by stitching segments together.
161     for (int a = 0; a < count; ++a) {
162         const Segment& sega = (*segments)[a];
163         int b = (a + 1) % count;
164         Segment& segb = (*segments)[b];
165         segb.fMid = segb.fNorms[0] + sega.endNorm();
166         segb.fMid.normalize();
167         // corner wedges
168         vCount64 += 4;
169         iCount64 += 6;
170     }
171     if (vCount64 > SK_MaxS32 || iCount64 > SK_MaxS32) {
172         return false;
173     }
174     *vCount = vCount64;
175     *iCount = iCount64;
176     return true;
177 }
178 
179 struct DegenerateTestData {
DegenerateTestDataDegenerateTestData180     DegenerateTestData() { fStage = kInitial; }
isDegenerateDegenerateTestData181     bool isDegenerate() const { return kNonDegenerate != fStage; }
182     enum {
183         kInitial,
184         kPoint,
185         kLine,
186         kNonDegenerate
187     }           fStage;
188     SkPoint     fFirstPoint;
189     SkVector    fLineNormal;
190     SkScalar    fLineC;
191 };
192 
193 static const SkScalar kClose = (SK_Scalar1 / 16);
194 static const SkScalar kCloseSqd = kClose * kClose;
195 
update_degenerate_test(DegenerateTestData * data,const SkPoint & pt)196 static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
197     switch (data->fStage) {
198         case DegenerateTestData::kInitial:
199             data->fFirstPoint = pt;
200             data->fStage = DegenerateTestData::kPoint;
201             break;
202         case DegenerateTestData::kPoint:
203             if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
204                 data->fLineNormal = pt - data->fFirstPoint;
205                 data->fLineNormal.normalize();
206                 data->fLineNormal = SkPointPriv::MakeOrthog(data->fLineNormal);
207                 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
208                 data->fStage = DegenerateTestData::kLine;
209             }
210             break;
211         case DegenerateTestData::kLine:
212             if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
213                 data->fStage = DegenerateTestData::kNonDegenerate;
214             }
215         case DegenerateTestData::kNonDegenerate:
216             break;
217         default:
218             SK_ABORT("Unexpected degenerate test stage.");
219     }
220 }
221 
get_direction(const SkPath & path,const SkMatrix & m,SkPathPriv::FirstDirection * dir)222 static inline bool get_direction(const SkPath& path, const SkMatrix& m,
223                                  SkPathPriv::FirstDirection* dir) {
224     if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
225         return false;
226     }
227     // check whether m reverses the orientation
228     SkASSERT(!m.hasPerspective());
229     SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
230                       m.get(SkMatrix::kMSkewX)  * m.get(SkMatrix::kMSkewY);
231     if (det2x2 < 0) {
232         *dir = SkPathPriv::OppositeFirstDirection(*dir);
233     }
234     return true;
235 }
236 
add_line_to_segment(const SkPoint & pt,SegmentArray * segments)237 static inline void add_line_to_segment(const SkPoint& pt,
238                                        SegmentArray* segments) {
239     segments->push_back();
240     segments->back().fType = Segment::kLine;
241     segments->back().fPts[0] = pt;
242 }
243 
add_quad_segment(const SkPoint pts[3],SegmentArray * segments)244 static inline void add_quad_segment(const SkPoint pts[3],
245                                     SegmentArray* segments) {
246     if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
247         SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd) {
248         if (pts[0] != pts[2]) {
249             add_line_to_segment(pts[2], segments);
250         }
251     } else {
252         segments->push_back();
253         segments->back().fType = Segment::kQuad;
254         segments->back().fPts[0] = pts[1];
255         segments->back().fPts[1] = pts[2];
256     }
257 }
258 
add_cubic_segments(const SkPoint pts[4],SkPathPriv::FirstDirection dir,SegmentArray * segments)259 static inline void add_cubic_segments(const SkPoint pts[4],
260                                       SkPathPriv::FirstDirection dir,
261                                       SegmentArray* segments) {
262     SkSTArray<15, SkPoint, true> quads;
263     GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
264     int count = quads.count();
265     for (int q = 0; q < count; q += 3) {
266         add_quad_segment(&quads[q], segments);
267     }
268 }
269 
get_segments(const SkPath & path,const SkMatrix & m,SegmentArray * segments,SkPoint * fanPt,int * vCount,int * iCount)270 static bool get_segments(const SkPath& path,
271                          const SkMatrix& m,
272                          SegmentArray* segments,
273                          SkPoint* fanPt,
274                          int* vCount,
275                          int* iCount) {
276     SkPath::Iter iter(path, true);
277     // This renderer over-emphasizes very thin path regions. We use the distance
278     // to the path from the sample to compute coverage. Every pixel intersected
279     // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
280     // notice that the sample may be close to a very thin area of the path and
281     // thus should be very light. This is particularly egregious for degenerate
282     // line paths. We detect paths that are very close to a line (zero area) and
283     // draw nothing.
284     DegenerateTestData degenerateData;
285     SkPathPriv::FirstDirection dir;
286     // get_direction can fail for some degenerate paths.
287     if (!get_direction(path, m, &dir)) {
288         return false;
289     }
290 
291     for (;;) {
292         SkPoint pts[4];
293         SkPath::Verb verb = iter.next(pts, true, true);
294         switch (verb) {
295             case SkPath::kMove_Verb:
296                 m.mapPoints(pts, 1);
297                 update_degenerate_test(&degenerateData, pts[0]);
298                 break;
299             case SkPath::kLine_Verb: {
300                 m.mapPoints(&pts[1], 1);
301                 update_degenerate_test(&degenerateData, pts[1]);
302                 add_line_to_segment(pts[1], segments);
303                 break;
304             }
305             case SkPath::kQuad_Verb:
306                 m.mapPoints(pts, 3);
307                 update_degenerate_test(&degenerateData, pts[1]);
308                 update_degenerate_test(&degenerateData, pts[2]);
309                 add_quad_segment(pts, segments);
310                 break;
311             case SkPath::kConic_Verb: {
312                 m.mapPoints(pts, 3);
313                 SkScalar weight = iter.conicWeight();
314                 SkAutoConicToQuads converter;
315                 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
316                 for (int i = 0; i < converter.countQuads(); ++i) {
317                     update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
318                     update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
319                     add_quad_segment(quadPts + 2*i, segments);
320                 }
321                 break;
322             }
323             case SkPath::kCubic_Verb: {
324                 m.mapPoints(pts, 4);
325                 update_degenerate_test(&degenerateData, pts[1]);
326                 update_degenerate_test(&degenerateData, pts[2]);
327                 update_degenerate_test(&degenerateData, pts[3]);
328                 add_cubic_segments(pts, dir, segments);
329                 break;
330             }
331             case SkPath::kDone_Verb:
332                 if (degenerateData.isDegenerate()) {
333                     return false;
334                 } else {
335                     return compute_vectors(segments, fanPt, dir, vCount, iCount);
336                 }
337             default:
338                 break;
339         }
340     }
341 }
342 
343 struct Draw {
DrawDraw344     Draw() : fVertexCnt(0), fIndexCnt(0) {}
345     int fVertexCnt;
346     int fIndexCnt;
347 };
348 
349 typedef SkTArray<Draw, true> DrawArray;
350 
create_vertices(const SegmentArray & segments,const SkPoint & fanPt,const GrVertexColor & color,DrawArray * draws,GrVertexWriter & verts,uint16_t * idxs,size_t vertexStride)351 static void create_vertices(const SegmentArray& segments,
352                             const SkPoint& fanPt,
353                             const GrVertexColor& color,
354                             DrawArray* draws,
355                             GrVertexWriter& verts,
356                             uint16_t* idxs,
357                             size_t vertexStride) {
358     Draw* draw = &draws->push_back();
359     // alias just to make vert/index assignments easier to read.
360     int* v = &draw->fVertexCnt;
361     int* i = &draw->fIndexCnt;
362     const size_t uvOffset = sizeof(SkPoint) + color.size();
363 
364     int count = segments.count();
365     for (int a = 0; a < count; ++a) {
366         const Segment& sega = segments[a];
367         int b = (a + 1) % count;
368         const Segment& segb = segments[b];
369 
370         // Check whether adding the verts for this segment to the current draw would cause index
371         // values to overflow.
372         int vCount = 4;
373         if (Segment::kLine == segb.fType) {
374             vCount += 5;
375         } else {
376             vCount += 6;
377         }
378         if (draw->fVertexCnt + vCount > (1 << 16)) {
379             idxs += *i;
380             draw = &draws->push_back();
381             v = &draw->fVertexCnt;
382             i = &draw->fIndexCnt;
383         }
384 
385         const SkScalar negOneDists[2] = { -SK_Scalar1, -SK_Scalar1 };
386 
387         // FIXME: These tris are inset in the 1 unit arc around the corner
388         SkPoint p0 = sega.endPt();
389         // Position, Color, UV, D0, D1
390         verts.write(p0,                  color, SkPoint{0, 0},           negOneDists);
391         verts.write(p0 + sega.endNorm(), color, SkPoint{0, -SK_Scalar1}, negOneDists);
392         verts.write(p0 + segb.fMid,      color, SkPoint{0, -SK_Scalar1}, negOneDists);
393         verts.write(p0 + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
394 
395         idxs[*i + 0] = *v + 0;
396         idxs[*i + 1] = *v + 2;
397         idxs[*i + 2] = *v + 1;
398         idxs[*i + 3] = *v + 0;
399         idxs[*i + 4] = *v + 3;
400         idxs[*i + 5] = *v + 2;
401 
402         *v += 4;
403         *i += 6;
404 
405         if (Segment::kLine == segb.fType) {
406             // we draw the line edge as a degenerate quad (u is 0, v is the
407             // signed distance to the edge)
408             SkPoint v1Pos = sega.endPt();
409             SkPoint v2Pos = segb.fPts[0];
410             SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, v1Pos, v2Pos);
411 
412             verts.write(fanPt,                  color, SkPoint{0, dist},        negOneDists);
413             verts.write(v1Pos,                  color, SkPoint{0, 0},           negOneDists);
414             verts.write(v2Pos,                  color, SkPoint{0, 0},           negOneDists);
415             verts.write(v1Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
416             verts.write(v2Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
417 
418             idxs[*i + 0] = *v + 3;
419             idxs[*i + 1] = *v + 1;
420             idxs[*i + 2] = *v + 2;
421 
422             idxs[*i + 3] = *v + 4;
423             idxs[*i + 4] = *v + 3;
424             idxs[*i + 5] = *v + 2;
425 
426             *i += 6;
427 
428             // Draw the interior fan if it exists.
429             // TODO: Detect and combine colinear segments. This will ensure we catch every case
430             // with no interior, and that the resulting shared edge uses the same endpoints.
431             if (count >= 3) {
432                 idxs[*i + 0] = *v + 0;
433                 idxs[*i + 1] = *v + 2;
434                 idxs[*i + 2] = *v + 1;
435 
436                 *i += 3;
437             }
438 
439             *v += 5;
440         } else {
441             void* quadVertsBegin = verts.fPtr;
442 
443             SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
444 
445             SkScalar c0 = segb.fNorms[0].dot(qpts[0]);
446             SkScalar c1 = segb.fNorms[1].dot(qpts[2]);
447             GrVertexWriter::Skip<SkPoint> skipUVs;
448 
449             verts.write(fanPt,
450                         color, skipUVs,
451                         -segb.fNorms[0].dot(fanPt) + c0,
452                         -segb.fNorms[1].dot(fanPt) + c1);
453 
454             verts.write(qpts[0],
455                         color, skipUVs,
456                         0.0f,
457                         -segb.fNorms[1].dot(qpts[0]) + c1);
458 
459             verts.write(qpts[2],
460                         color, skipUVs,
461                         -segb.fNorms[0].dot(qpts[2]) + c0,
462                         0.0f);
463 
464             verts.write(qpts[0] + segb.fNorms[0],
465                         color, skipUVs,
466                         -SK_ScalarMax/100,
467                         -SK_ScalarMax/100);
468 
469             verts.write(qpts[2] + segb.fNorms[1],
470                         color, skipUVs,
471                         -SK_ScalarMax/100,
472                         -SK_ScalarMax/100);
473 
474             SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
475             midVec.normalize();
476 
477             verts.write(qpts[1] + midVec,
478                         color, skipUVs,
479                         -SK_ScalarMax/100,
480                         -SK_ScalarMax/100);
481 
482             GrPathUtils::QuadUVMatrix toUV(qpts);
483             toUV.apply(quadVertsBegin, 6, vertexStride, uvOffset);
484 
485             idxs[*i + 0] = *v + 3;
486             idxs[*i + 1] = *v + 1;
487             idxs[*i + 2] = *v + 2;
488             idxs[*i + 3] = *v + 4;
489             idxs[*i + 4] = *v + 3;
490             idxs[*i + 5] = *v + 2;
491 
492             idxs[*i + 6] = *v + 5;
493             idxs[*i + 7] = *v + 3;
494             idxs[*i + 8] = *v + 4;
495 
496             *i += 9;
497 
498             // Draw the interior fan if it exists.
499             // TODO: Detect and combine colinear segments. This will ensure we catch every case
500             // with no interior, and that the resulting shared edge uses the same endpoints.
501             if (count >= 3) {
502                 idxs[*i + 0] = *v + 0;
503                 idxs[*i + 1] = *v + 2;
504                 idxs[*i + 2] = *v + 1;
505 
506                 *i += 3;
507             }
508 
509             *v += 6;
510         }
511     }
512 }
513 
514 ///////////////////////////////////////////////////////////////////////////////
515 
516 /*
517  * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
518  * two components of the vertex attribute. Coverage is based on signed
519  * distance with negative being inside, positive outside. The edge is specified in
520  * window space (y-down). If either the third or fourth component of the interpolated
521  * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
522  * attempt to trim to a portion of the infinite quad.
523  * Requires shader derivative instruction support.
524  */
525 
526 class QuadEdgeEffect : public GrGeometryProcessor {
527 public:
Make(const SkMatrix & localMatrix,bool usesLocalCoords,bool wideColor)528     static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords,
529                                            bool wideColor) {
530         return sk_sp<GrGeometryProcessor>(
531                 new QuadEdgeEffect(localMatrix, usesLocalCoords, wideColor));
532     }
533 
~QuadEdgeEffect()534     ~QuadEdgeEffect() override {}
535 
name() const536     const char* name() const override { return "QuadEdge"; }
537 
538     class GLSLProcessor : public GrGLSLGeometryProcessor {
539     public:
GLSLProcessor()540         GLSLProcessor() {}
541 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)542         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
543             const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
544             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
545             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
546             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
547 
548             // emit attributes
549             varyingHandler->emitAttributes(qe);
550 
551             GrGLSLVarying v(kHalf4_GrSLType);
552             varyingHandler->addVarying("QuadEdge", &v);
553             vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge.name());
554 
555             // Setup pass through color
556             varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
557 
558             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
559 
560             // Setup position
561             this->writeOutputPosition(vertBuilder, gpArgs, qe.fInPosition.name());
562 
563             // emit transforms
564             this->emitTransforms(vertBuilder,
565                                  varyingHandler,
566                                  uniformHandler,
567                                  qe.fInPosition.asShaderVar(),
568                                  qe.fLocalMatrix,
569                                  args.fFPCoordTransformHandler);
570 
571             fragBuilder->codeAppendf("half edgeAlpha;");
572 
573             // keep the derivative instructions outside the conditional
574             fragBuilder->codeAppendf("half2 duvdx = dFdx(%s.xy);", v.fsIn());
575             fragBuilder->codeAppendf("half2 duvdy = dFdy(%s.xy);", v.fsIn());
576             fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
577             // today we know z and w are in device space. We could use derivatives
578             fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
579                                      v.fsIn());
580             fragBuilder->codeAppendf ("} else {");
581             fragBuilder->codeAppendf("half2 gF = half2(2.0*%s.x*duvdx.x - duvdx.y,"
582                                      "               2.0*%s.x*duvdy.x - duvdy.y);",
583                                      v.fsIn(), v.fsIn());
584             fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
585                                      v.fsIn());
586             fragBuilder->codeAppendf("edgeAlpha = "
587                                      "saturate(0.5 - edgeAlpha / length(gF));}");
588 
589             fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
590         }
591 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)592         static inline void GenKey(const GrGeometryProcessor& gp,
593                                   const GrShaderCaps&,
594                                   GrProcessorKeyBuilder* b) {
595             const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
596             b->add32(SkToBool(qee.fUsesLocalCoords && qee.fLocalMatrix.hasPerspective()));
597         }
598 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp,FPCoordTransformIter && transformIter)599         void setData(const GrGLSLProgramDataManager& pdman,
600                      const GrPrimitiveProcessor& gp,
601                      FPCoordTransformIter&& transformIter) override {
602             const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
603             this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
604         }
605 
606     private:
607         typedef GrGLSLGeometryProcessor INHERITED;
608     };
609 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const610     void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
611         GLSLProcessor::GenKey(*this, caps, b);
612     }
613 
createGLSLInstance(const GrShaderCaps &) const614     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
615         return new GLSLProcessor();
616     }
617 
618 private:
QuadEdgeEffect(const SkMatrix & localMatrix,bool usesLocalCoords,bool wideColor)619     QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords, bool wideColor)
620             : INHERITED(kQuadEdgeEffect_ClassID)
621             , fLocalMatrix(localMatrix)
622             , fUsesLocalCoords(usesLocalCoords) {
623         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
624         fInColor = MakeColorAttribute("inColor", wideColor);
625         fInQuadEdge = {"inQuadEdge", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
626         this->setVertexAttributes(&fInPosition, 3);
627     }
628 
629     Attribute fInPosition;
630     Attribute fInColor;
631     Attribute fInQuadEdge;
632 
633     SkMatrix fLocalMatrix;
634     bool fUsesLocalCoords;
635 
636     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
637 
638     typedef GrGeometryProcessor INHERITED;
639 };
640 
641 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
642 
643 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)644 sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
645     // Doesn't work without derivative instructions.
646     return d->caps()->shaderCaps()->shaderDerivativeSupport()
647                    ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool(),
648                                           d->fRandom->nextBool())
649                    : nullptr;
650 }
651 #endif
652 
653 ///////////////////////////////////////////////////////////////////////////////
654 
655 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const656 GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
657     if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
658         (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
659         !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()) {
660         return CanDrawPath::kYes;
661     }
662     return CanDrawPath::kNo;
663 }
664 
665 namespace {
666 
667 class AAConvexPathOp final : public GrMeshDrawOp {
668 private:
669     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
670 
671 public:
672     DEFINE_OP_CLASS_ID
673 
Make(GrContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkPath & path,const GrUserStencilSettings * stencilSettings)674     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
675                                           GrPaint&& paint,
676                                           const SkMatrix& viewMatrix,
677                                           const SkPath& path,
678                                           const GrUserStencilSettings* stencilSettings) {
679         return Helper::FactoryHelper<AAConvexPathOp>(context, std::move(paint), viewMatrix, path,
680                                                      stencilSettings);
681     }
682 
AAConvexPathOp(const Helper::MakeArgs & helperArgs,const SkPMColor4f & color,const SkMatrix & viewMatrix,const SkPath & path,const GrUserStencilSettings * stencilSettings)683     AAConvexPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
684                    const SkMatrix& viewMatrix, const SkPath& path,
685                    const GrUserStencilSettings* stencilSettings)
686             : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
687         fPaths.emplace_back(PathData{viewMatrix, path, color});
688         this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
689         fWideColor = !SkPMColor4fFitsInBytes(color);
690     }
691 
name() const692     const char* name() const override { return "AAConvexPathOp"; }
693 
visitProxies(const VisitProxyFunc & func,VisitorType) const694     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
695         fHelper.visitProxies(func);
696     }
697 
698 #ifdef SK_DEBUG
dumpInfo() const699     SkString dumpInfo() const override {
700         SkString string;
701         string.appendf("Count: %d\n", fPaths.count());
702         string += fHelper.dumpInfo();
703         string += INHERITED::dumpInfo();
704         return string;
705     }
706 #endif
707 
fixedFunctionFlags() const708     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
709 
finalize(const GrCaps & caps,const GrAppliedClip * clip)710     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
711         return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
712                                           &fPaths.back().fColor);
713     }
714 
715 private:
onPrepareDraws(Target * target)716     void onPrepareDraws(Target* target) override {
717         auto pipe = fHelper.makePipeline(target);
718         int instanceCount = fPaths.count();
719 
720         SkMatrix invert;
721         if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
722             return;
723         }
724 
725         // Setup GrGeometryProcessor
726         sk_sp<GrGeometryProcessor> quadProcessor(
727                 QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords(), fWideColor));
728         const size_t kVertexStride = quadProcessor->vertexStride();
729 
730         // TODO generate all segments for all paths and use one vertex buffer
731         for (int i = 0; i < instanceCount; i++) {
732             const PathData& args = fPaths[i];
733 
734             // We use the fact that SkPath::transform path does subdivision based on
735             // perspective. Otherwise, we apply the view matrix when copying to the
736             // segment representation.
737             const SkMatrix* viewMatrix = &args.fViewMatrix;
738 
739             // We avoid initializing the path unless we have to
740             const SkPath* pathPtr = &args.fPath;
741             SkTLazy<SkPath> tmpPath;
742             if (viewMatrix->hasPerspective()) {
743                 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
744                 tmpPathPtr->setIsVolatile(true);
745                 tmpPathPtr->transform(*viewMatrix);
746                 viewMatrix = &SkMatrix::I();
747                 pathPtr = tmpPathPtr;
748             }
749 
750             int vertexCount;
751             int indexCount;
752             enum {
753                 kPreallocSegmentCnt = 512 / sizeof(Segment),
754                 kPreallocDrawCnt = 4,
755             };
756             SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
757             SkPoint fanPt;
758 
759             if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
760                               &indexCount)) {
761                 continue;
762             }
763 
764             sk_sp<const GrBuffer> vertexBuffer;
765             int firstVertex;
766 
767             GrVertexWriter verts{target->makeVertexSpace(kVertexStride, vertexCount,
768                                                          &vertexBuffer, &firstVertex)};
769 
770             if (!verts.fPtr) {
771                 SkDebugf("Could not allocate vertices\n");
772                 return;
773             }
774 
775             sk_sp<const GrBuffer> indexBuffer;
776             int firstIndex;
777 
778             uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
779             if (!idxs) {
780                 SkDebugf("Could not allocate indices\n");
781                 return;
782             }
783 
784             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
785             GrVertexColor color(args.fColor, fWideColor);
786             create_vertices(segments, fanPt, color, &draws, verts, idxs, kVertexStride);
787 
788             GrMesh* meshes = target->allocMeshes(draws.count());
789             for (int j = 0; j < draws.count(); ++j) {
790                 const Draw& draw = draws[j];
791                 meshes[j].setPrimitiveType(GrPrimitiveType::kTriangles);
792                 meshes[j].setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0,
793                                      draw.fVertexCnt - 1, GrPrimitiveRestart::kNo);
794                 meshes[j].setVertexData(vertexBuffer, firstVertex);
795                 firstIndex += draw.fIndexCnt;
796                 firstVertex += draw.fVertexCnt;
797             }
798             target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, nullptr, meshes,
799                          draws.count());
800         }
801     }
802 
onCombineIfPossible(GrOp * t,const GrCaps & caps)803     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
804         AAConvexPathOp* that = t->cast<AAConvexPathOp>();
805         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
806             return CombineResult::kCannotCombine;
807         }
808         if (fHelper.usesLocalCoords() &&
809             !fPaths[0].fViewMatrix.cheapEqualTo(that->fPaths[0].fViewMatrix)) {
810             return CombineResult::kCannotCombine;
811         }
812 
813         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
814         fWideColor |= that->fWideColor;
815         return CombineResult::kMerged;
816     }
817 
818     struct PathData {
819         SkMatrix fViewMatrix;
820         SkPath fPath;
821         SkPMColor4f fColor;
822     };
823 
824     Helper fHelper;
825     SkSTArray<1, PathData, true> fPaths;
826     bool fWideColor;
827 
828     typedef GrMeshDrawOp INHERITED;
829 };
830 
831 }  // anonymous namespace
832 
onDrawPath(const DrawPathArgs & args)833 bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
834     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
835                               "GrAAConvexPathRenderer::onDrawPath");
836     SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
837     SkASSERT(!args.fShape->isEmpty());
838 
839     SkPath path;
840     args.fShape->asPath(&path);
841 
842     std::unique_ptr<GrDrawOp> op = AAConvexPathOp::Make(args.fContext, std::move(args.fPaint),
843                                                         *args.fViewMatrix,
844                                                         path, args.fUserStencilSettings);
845     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
846     return true;
847 }
848 
849 ///////////////////////////////////////////////////////////////////////////////////////////////////
850 
851 #if GR_TEST_UTILS
852 
GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp)853 GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
854     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
855     SkPath path = GrTest::TestPathConvex(random);
856     const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
857     return AAConvexPathOp::Make(context, std::move(paint), viewMatrix, path, stencilSettings);
858 }
859 
860 #endif
861