1 /*
2  * Copyright 2013 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 "src/gpu/ops/GrOvalOpFactory.h"
9 
10 #include "include/core/SkStrokeRec.h"
11 #include "src/core/SkMatrixPriv.h"
12 #include "src/core/SkRRectPriv.h"
13 #include "src/gpu/BufferWriter.h"
14 #include "src/gpu/GrCaps.h"
15 #include "src/gpu/GrDrawOpTest.h"
16 #include "src/gpu/GrGeometryProcessor.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include "src/gpu/GrProcessor.h"
19 #include "src/gpu/GrProgramInfo.h"
20 #include "src/gpu/GrResourceProvider.h"
21 #include "src/gpu/GrShaderCaps.h"
22 #include "src/gpu/GrStyle.h"
23 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
24 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
25 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
26 #include "src/gpu/glsl/GrGLSLVarying.h"
27 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
28 #include "src/gpu/ops/GrMeshDrawOp.h"
29 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
30 
31 #include <utility>
32 
33 using skgpu::VertexWriter;
34 
35 namespace {
36 
circle_stays_circle(const SkMatrix& m)37 static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
38 
39 // Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
origin_centered_tri_strip(float x, float y)40 static inline VertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
41     return VertexWriter::TriStrip<float>{ -x, -y, x, y };
42 };
43 
44 }  // namespace
45 
46 ///////////////////////////////////////////////////////////////////////////////
47 
48 /**
49  * The output of this effect is a modulation of the input color and coverage for a circle. It
50  * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
51  * with origin at the circle center. Three vertex attributes are used:
52  *    vec2f : position in device space of the bounding geometry vertices
53  *    vec4ub: color
54  *    vec4f : (p.xy, outerRad, innerRad)
55  *             p is the position in the normalized space.
56  *             outerRad is the outerRadius in device space.
57  *             innerRad is the innerRadius in normalized space (ignored if not stroking).
58  * Additional clip planes are supported for rendering circular arcs. The additional planes are
59  * either intersected or unioned together. Up to three planes are supported (an initial plane,
60  * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
61  * are useful for any given arc, but having all three in one instance allows combining different
62  * types of arcs.
63  * Round caps for stroking are allowed as well. The caps are specified as two circle center points
64  * in the same space as p.xy.
65  */
66 
67 class CircleGeometryProcessor : public GrGeometryProcessor {
68 public:
Make(SkArenaAlloc* arena, bool stroke, bool clipPlane, bool isectPlane, bool unionPlane, bool roundCaps, bool wideColor, const SkMatrix& localMatrix)69     static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
70                                      bool isectPlane, bool unionPlane, bool roundCaps,
71                                      bool wideColor, const SkMatrix& localMatrix) {
72         return arena->make([&](void* ptr) {
73             return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
74                                                      roundCaps, wideColor, localMatrix);
75         });
76     }
77 
78     const char* name() const override { return "CircleGeometryProcessor"; }
79 
80     SkString getShaderDfxInfo() const override {
81         SkString format;
82         format.printf("ShaderDfx_CircleGeometry_%d_%d_%d_%d_%d_%d_%d_%d", fStroke, fInClipPlane.isInitialized(),
83             fInIsectPlane.isInitialized(), fInUnionPlane.isInitialized(), fInRoundCapCenters.isInitialized(),
84             fLocalMatrix.isIdentity(), fLocalMatrix.isScaleTranslate(), fLocalMatrix.hasPerspective());
85         return format;
86     }
87 
88     void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
89         b->addBool(fStroke,                             "stroked"        );
90         b->addBool(fInClipPlane.isInitialized(),        "clipPlane"      );
91         b->addBool(fInIsectPlane.isInitialized(),       "isectPlane"     );
92         b->addBool(fInUnionPlane.isInitialized(),       "unionPlane"     );
93         b->addBool(fInRoundCapCenters.isInitialized(),  "roundCapCenters");
94         b->addBits(ProgramImpl::kMatrixKeyBits,
95                    ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
96                    "localMatrixType");
97     }
98 
99     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
100         return std::make_unique<Impl>();
101     }
102 
103 private:
CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane, bool roundCaps, bool wideColor, const SkMatrix& localMatrix)104     CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
105                             bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
106             : INHERITED(kCircleGeometryProcessor_ClassID)
107             , fLocalMatrix(localMatrix)
108             , fStroke(stroke) {
109         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
110         fInColor = MakeColorAttribute("inColor", wideColor);
111         fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
112 
113         if (clipPlane) {
114             fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
115         }
116         if (isectPlane) {
117             fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
118         }
119         if (unionPlane) {
120             fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
121         }
122         if (roundCaps) {
123             SkASSERT(stroke);
124             SkASSERT(clipPlane);
125             fInRoundCapCenters =
126                     {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
127         }
128         this->setVertexAttributes(&fInPosition, 7);
129     }
130 
131     class Impl : public ProgramImpl {
132     public:
133         void setData(const GrGLSLProgramDataManager& pdman,
134                      const GrShaderCaps& shaderCaps,
135                      const GrGeometryProcessor& geomProc) override {
136             SetTransform(pdman,
137                          shaderCaps,
138                          fLocalMatrixUniform,
139                          geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
140                          &fLocalMatrix);
141         }
142 
143     private:
144         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
145             const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
146             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
147             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
148             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
149             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
150 
151             // emit attributes
152             varyingHandler->emitAttributes(cgp);
153             fragBuilder->codeAppend("float4 circleEdge;");
154             varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge.asShaderVar(), "circleEdge");
155             if (cgp.fInClipPlane.isInitialized()) {
156                 fragBuilder->codeAppend("half3 clipPlane;");
157                 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane.asShaderVar(),
158                                                         "clipPlane");
159             }
160             if (cgp.fInIsectPlane.isInitialized()) {
161                 fragBuilder->codeAppend("half3 isectPlane;");
162                 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane.asShaderVar(),
163                                                         "isectPlane");
164             }
165             if (cgp.fInUnionPlane.isInitialized()) {
166                 SkASSERT(cgp.fInClipPlane.isInitialized());
167                 fragBuilder->codeAppend("half3 unionPlane;");
168                 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane.asShaderVar(),
169                                                         "unionPlane");
170             }
171             GrGLSLVarying capRadius(kFloat_GrSLType);
172             if (cgp.fInRoundCapCenters.isInitialized()) {
173                 fragBuilder->codeAppend("float4 roundCapCenters;");
174                 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters.asShaderVar(),
175                                                         "roundCapCenters");
176                 varyingHandler->addVarying("capRadius", &capRadius,
177                                            GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
178                 // This is the cap radius in normalized space where the outer radius is 1 and
179                 // circledEdge.w is the normalized inner radius.
180                 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
181                                          cgp.fInCircleEdge.name());
182             }
183 
184             // setup pass through color
185             fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
186             varyingHandler->addPassThroughAttribute(cgp.fInColor.asShaderVar(), args.fOutputColor);
187 
188             // Setup position
189             WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
190             WriteLocalCoord(vertBuilder,
191                             uniformHandler,
192                             *args.fShaderCaps,
193                             gpArgs,
194                             cgp.fInPosition.asShaderVar(),
195                             cgp.fLocalMatrix,
196                             &fLocalMatrixUniform);
197 
198             fragBuilder->codeAppend("float d = length(circleEdge.xy);");
199             fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
200             fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
201             if (cgp.fStroke) {
202                 fragBuilder->codeAppend(
203                         "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
204                 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
205                 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
206             }
207 
208             if (cgp.fInClipPlane.isInitialized()) {
209                 fragBuilder->codeAppend(
210                         "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
211                         "clipPlane.xy) + clipPlane.z));");
212                 if (cgp.fInIsectPlane.isInitialized()) {
213                     fragBuilder->codeAppend(
214                             "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
215                             "isectPlane.xy) + isectPlane.z));");
216                 }
217                 if (cgp.fInUnionPlane.isInitialized()) {
218                     fragBuilder->codeAppend(
219                             "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
220                             " unionPlane.xy) + unionPlane.z)));");
221                 }
222                 fragBuilder->codeAppend("edgeAlpha *= clip;");
223                 if (cgp.fInRoundCapCenters.isInitialized()) {
224                     // We compute coverage of the round caps as circles at the butt caps produced
225                     // by the clip planes. The inverse of the clip planes is applied so that there
226                     // is no double counting.
227                     fragBuilder->codeAppendf(
228                             "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
229                             "                                              roundCapCenters.xy)));"
230                             "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
231                             "                                              roundCapCenters.zw)));"
232                             "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
233                             "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
234                             capRadius.fsIn(), capRadius.fsIn());
235                 }
236             }
237             fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
238         }
239 
240         SkMatrix      fLocalMatrix = SkMatrix::InvalidMatrix();
241         UniformHandle fLocalMatrixUniform;
242     };
243 
244     SkMatrix fLocalMatrix;
245 
246     Attribute fInPosition;
247     Attribute fInColor;
248     Attribute fInCircleEdge;
249     // Optional attributes.
250     Attribute fInClipPlane;
251     Attribute fInIsectPlane;
252     Attribute fInUnionPlane;
253     Attribute fInRoundCapCenters;
254 
255     bool fStroke;
256     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
257 
258     using INHERITED = GrGeometryProcessor;
259 };
260 
261 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
262 
263 #if GR_TEST_UTILS
264 GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
265     bool stroke = d->fRandom->nextBool();
266     bool roundCaps = stroke ? d->fRandom->nextBool() : false;
267     bool wideColor = d->fRandom->nextBool();
268     bool clipPlane = d->fRandom->nextBool();
269     bool isectPlane = d->fRandom->nextBool();
270     bool unionPlane = d->fRandom->nextBool();
271     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
272     return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
273                                          unionPlane, roundCaps, wideColor, matrix);
274 }
275 #endif
276 
277 class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
278 public:
279     static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
280                                      const SkMatrix& localMatrix) {
281         return arena->make([&](void* ptr) {
282             return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
283         });
284     }
285 
286     ~ButtCapDashedCircleGeometryProcessor() override {}
287 
288     const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
289 
290     SkString getShaderDfxInfo() const override {
291         SkString format;
292         format.printf("ShaderDfx_ButtCapDashedCircle_%d_%d_%d",
293             fLocalMatrix.isIdentity(), fLocalMatrix.isScaleTranslate(), fLocalMatrix.hasPerspective());
294         return format;
295     }
296 
297     void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
298         b->addBits(ProgramImpl::kMatrixKeyBits,
299                    ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
300                    "localMatrixType");
301     }
302 
303     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
304         return std::make_unique<Impl>();
305     }
306 
307 private:
308     ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
309             : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
310             , fLocalMatrix(localMatrix) {
311         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
312         fInColor = MakeColorAttribute("inColor", wideColor);
313         fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
314         fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
315         this->setVertexAttributes(&fInPosition, 4);
316     }
317 
318     class Impl : public ProgramImpl {
319     public:
320         void setData(const GrGLSLProgramDataManager& pdman,
321                      const GrShaderCaps& shaderCaps,
322                      const GrGeometryProcessor& geomProc) override {
323             SetTransform(pdman,
324                          shaderCaps,
325                          fLocalMatrixUniform,
326                          geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
327                          &fLocalMatrix);
328         }
329 
330     private:
331         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
332             const ButtCapDashedCircleGeometryProcessor& bcscgp =
333                     args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
334             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
335             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
336             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
337             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
338 
339             // emit attributes
340             varyingHandler->emitAttributes(bcscgp);
341             fragBuilder->codeAppend("float4 circleEdge;");
342             varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge.asShaderVar(),
343                                                     "circleEdge");
344 
345             fragBuilder->codeAppend("float4 dashParams;");
346             varyingHandler->addPassThroughAttribute(
347                     bcscgp.fInDashParams.asShaderVar(),
348                     "dashParams",
349                     GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
350             GrGLSLVarying wrapDashes(kHalf4_GrSLType);
351             varyingHandler->addVarying("wrapDashes", &wrapDashes,
352                                        GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
353             GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
354             varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
355                                        GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
356             vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
357             // Our fragment shader works in on/off intervals as specified by dashParams.xy:
358             //     x = length of on interval, y = length of on + off.
359             // There are two other parameters in dashParams.zw:
360             //     z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
361             // Each interval has a "corresponding" dash which may be shifted partially or
362             // fully out of its interval by the phase. So there may be up to two "visual"
363             // dashes in an interval.
364             // When computing coverage in an interval we look at three dashes. These are the
365             // "corresponding" dashes from the current, previous, and next intervals. Any of these
366             // may be phase shifted into our interval or even when phase=0 they may be within half a
367             // pixel distance of a pixel center in the interval.
368             // When in the first interval we need to check the dash from the last interval. And
369             // similarly when in the last interval we need to check the dash from the first
370             // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
371             // We compute the dash begin/end angles in the vertex shader and apply them in the
372             // fragment shader when we detect we're in the first/last interval.
373             vertBuilder->codeAppend(R"(
374                     // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
375                     // to the fragment shader as a varying.
376                     float4 wrapDashes;
377                     half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
378                     // We can happen to be perfectly divisible.
379                     if (0 == lastIntervalLength) {
380                         lastIntervalLength = half(dashParams.y);
381                     }
382                     // Let 'l' be the last interval before reaching 2 pi.
383                     // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
384                     // "corresponding" dash appears in the l-th interval and is closest to the 0-th
385                     // interval.
386                     half offset = 0;
387                     if (-dashParams.w >= lastIntervalLength) {
388                          offset = half(-dashParams.y);
389                     } else if (dashParams.w > dashParams.y - lastIntervalLength) {
390                          offset = half(dashParams.y);
391                     }
392                     wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
393                     // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
394                     // min.
395                     wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
396 
397                     // Based on the phase determine whether the -1st, 0th, or 1st interval's
398                     // "corresponding" dash appears in the 0th interval and is closest to l.
399                     offset = 0;
400                     if (dashParams.w >= dashParams.x) {
401                         offset = half(dashParams.y);
402                     } else if (-dashParams.w > dashParams.y - dashParams.x) {
403                         offset = half(-dashParams.y);
404                     }
405                     wrapDashes.z = lastIntervalLength + offset - dashParams.w;
406                     wrapDashes.w = wrapDashes.z + dashParams.x;
407                     // The start of the dash we're considering may be clipped by the start of the
408                     // circle.
409                     wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
410             )");
411             vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
412             vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
413             fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
414             fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
415 
416             // setup pass through color
417             fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
418             varyingHandler->addPassThroughAttribute(
419                     bcscgp.fInColor.asShaderVar(),
420                     args.fOutputColor,
421                     GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
422 
423             // Setup position
424             WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
425             WriteLocalCoord(vertBuilder,
426                             uniformHandler,
427                             *args.fShaderCaps,
428                             gpArgs,
429                             bcscgp.fInPosition.asShaderVar(),
430                             bcscgp.fLocalMatrix,
431                             &fLocalMatrixUniform);
432 
433             GrShaderVar fnArgs[] = {
434                     GrShaderVar("angleToEdge", kFloat_GrSLType),
435                     GrShaderVar("diameter", kFloat_GrSLType),
436             };
437             SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
438             fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
439                                       {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
440                     float linearDist;
441                     angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
442                     linearDist = diameter * sin(angleToEdge / 2);
443                     return saturate(linearDist + 0.5);
444             )");
445             fragBuilder->codeAppend(R"(
446                     float d = length(circleEdge.xy) * circleEdge.z;
447 
448                     // Compute coverage from outer/inner edges of the stroke.
449                     half distanceToOuterEdge = half(circleEdge.z - d);
450                     half edgeAlpha = saturate(distanceToOuterEdge);
451                     half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
452                     half innerAlpha = saturate(distanceToInnerEdge);
453                     edgeAlpha *= innerAlpha;
454 
455                     half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
456                     angleFromStart = mod(angleFromStart, 6.28318530718);
457                     float x = mod(angleFromStart, dashParams.y);
458                     // Convert the radial distance from center to pixel into a diameter.
459                     d *= 2;
460                     half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
461                                                                 half(dashParams.w));
462                     half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
463                                            half(dashParams.y) + half(dashParams.x) -
464                                                                 half(dashParams.w));
465                     half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
466                                            half(-dashParams.y) + half(dashParams.x) -
467                                                                  half(dashParams.w));
468                     half dashAlpha = 0;
469                 )");
470             fragBuilder->codeAppendf(R"(
471                     if (angleFromStart - x + dashParams.y >= 6.28318530718) {
472                          dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
473                          currDash.y = min(currDash.y, lastIntervalLength);
474                          if (nextDash.x >= lastIntervalLength) {
475                              // The next dash is outside the 0..2pi range, throw it away
476                              nextDash.xy = half2(1000);
477                          } else {
478                              // Clip the end of the next dash to the end of the circle
479                              nextDash.y = min(nextDash.y, lastIntervalLength);
480                          }
481                     }
482             )", fnName.c_str(), fnName.c_str());
483             fragBuilder->codeAppendf(R"(
484                     if (angleFromStart - x - dashParams.y < -0.01) {
485                          dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
486                          currDash.x = max(currDash.x, 0);
487                          if (prevDash.y <= 0) {
488                              // The previous dash is outside the 0..2pi range, throw it away
489                              prevDash.xy = half2(1000);
490                          } else {
491                              // Clip the start previous dash to the start of the circle
492                              prevDash.x = max(prevDash.x, 0);
493                          }
494                     }
495             )", fnName.c_str(), fnName.c_str());
496             fragBuilder->codeAppendf(R"(
497                     dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
498                     dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
499                     dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
500                     dashAlpha = min(dashAlpha, 1);
501                     edgeAlpha *= dashAlpha;
502             )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
503                 fnName.c_str());
504             fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
505         }
506 
507         SkMatrix      fLocalMatrix = SkMatrix::InvalidMatrix();
508         UniformHandle fLocalMatrixUniform;
509     };
510 
511     SkMatrix fLocalMatrix;
512     Attribute fInPosition;
513     Attribute fInColor;
514     Attribute fInCircleEdge;
515     Attribute fInDashParams;
516 
517     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
518 
519     using INHERITED = GrGeometryProcessor;
520 };
521 
522 #if GR_TEST_UTILS
523 GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
524     bool wideColor = d->fRandom->nextBool();
525     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
526     return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
527 }
528 #endif
529 
530 ///////////////////////////////////////////////////////////////////////////////
531 
532 /**
533  * The output of this effect is a modulation of the input color and coverage for an axis-aligned
534  * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
535  * in both x and y directions.
536  *
537  * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
538  */
539 
540 class EllipseGeometryProcessor : public GrGeometryProcessor {
541 public:
542     static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
543                                      bool useScale, const SkMatrix& localMatrix) {
544         return arena->make([&](void* ptr) {
545             return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
546         });
547     }
548 
549     ~EllipseGeometryProcessor() override {}
550 
551     const char* name() const override { return "EllipseGeometryProcessor"; }
552 
553     SkString getShaderDfxInfo() const override {
554         SkString format;
555         format.printf("ShaderDfx_EllipseGeometry_%d_%d_%d_%d", fStroke,
556             fLocalMatrix.isIdentity(), fLocalMatrix.isScaleTranslate(), fLocalMatrix.hasPerspective());
557         return format;
558     }
559 
560     void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
561         b->addBool(fStroke, "stroked");
562         b->addBits(ProgramImpl::kMatrixKeyBits,
563                    ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
564                    "localMatrixType");
565     }
566 
567     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
568         return std::make_unique<Impl>();
569     }
570 
571 private:
572     EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
573                              const SkMatrix& localMatrix)
574             : INHERITED(kEllipseGeometryProcessor_ClassID)
575             , fLocalMatrix(localMatrix)
576             , fStroke(stroke)
577             , fUseScale(useScale) {
578         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
579         fInColor = MakeColorAttribute("inColor", wideColor);
580         if (useScale) {
581             fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
582         } else {
583             fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
584         }
585         fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
586         this->setVertexAttributes(&fInPosition, 4);
587     }
588 
589     class Impl : public ProgramImpl {
590     public:
591         void setData(const GrGLSLProgramDataManager& pdman,
592                      const GrShaderCaps& shaderCaps,
593                      const GrGeometryProcessor& geomProc) override {
594             const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
595             SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
596         }
597 
598     private:
599         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
600             const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
601             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
602             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
603             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
604 
605             // emit attributes
606             varyingHandler->emitAttributes(egp);
607 
608             GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
609             GrGLSLVarying ellipseOffsets(offsetType);
610             varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
611             vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
612                                      egp.fInEllipseOffset.name());
613 
614             GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
615             varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
616             vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
617 
618             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
619             // setup pass through color
620             fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
621             varyingHandler->addPassThroughAttribute(egp.fInColor.asShaderVar(), args.fOutputColor);
622 
623             // Setup position
624             WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
625             WriteLocalCoord(vertBuilder,
626                             uniformHandler,
627                             *args.fShaderCaps,
628                             gpArgs,
629                             egp.fInPosition.asShaderVar(),
630                             egp.fLocalMatrix,
631                             &fLocalMatrixUniform);
632 
633             // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
634             // to compute both the edges because we need two separate test equations for
635             // the single offset.
636             // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
637             // the distance by the gradient, non-uniformly scaled by the inverse of the
638             // ellipse size.
639 
640             // On medium precision devices, we scale the denominator of the distance equation
641             // before taking the inverse square root to minimize the chance that we're dividing
642             // by zero, then we scale the result back.
643 
644             // for outer curve
645             fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
646             if (egp.fStroke) {
647                 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
648             }
649             fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
650             if (egp.fUseScale) {
651                 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
652                                          ellipseOffsets.fsIn(), ellipseRadii.fsIn());
653             } else {
654                 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
655             }
656             fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
657 
658             // avoid calling inversesqrt on zero.
659             if (args.fShaderCaps->floatIs32Bits()) {
660                 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
661             } else {
662                 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
663             }
664             if (egp.fUseScale) {
665                 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
666                                          ellipseOffsets.fsIn());
667             } else {
668                 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
669             }
670             fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
671 
672             // for inner curve
673             if (egp.fStroke) {
674                 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
675                                          ellipseRadii.fsIn());
676                 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
677                 if (egp.fUseScale) {
678                     fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
679                                              ellipseOffsets.fsIn(), ellipseRadii.fsIn());
680                 } else {
681                     fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
682                 }
683                 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
684                 if (!args.fShaderCaps->floatIs32Bits()) {
685                     fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
686                 }
687                 if (egp.fUseScale) {
688                     fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
689                                              ellipseOffsets.fsIn());
690                 } else {
691                     fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
692                 }
693                 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
694             }
695 
696             fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
697         }
698 
699         using INHERITED = ProgramImpl;
700 
701         SkMatrix      fLocalMatrix = SkMatrix::InvalidMatrix();
702         UniformHandle fLocalMatrixUniform;
703     };
704 
705     Attribute fInPosition;
706     Attribute fInColor;
707     Attribute fInEllipseOffset;
708     Attribute fInEllipseRadii;
709 
710     SkMatrix fLocalMatrix;
711     bool fStroke;
712     bool fUseScale;
713 
714     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
715 
716     using INHERITED = GrGeometryProcessor;
717 };
718 
719 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
720 
721 #if GR_TEST_UTILS
722 GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
723     bool stroke = d->fRandom->nextBool();
724     bool wideColor = d->fRandom->nextBool();
725     bool useScale = d->fRandom->nextBool();
726     SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
727     return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
728 }
729 #endif
730 
731 ///////////////////////////////////////////////////////////////////////////////
732 
733 /**
734  * The output of this effect is a modulation of the input color and coverage for an ellipse,
735  * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
736  * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
737  * using differentials.
738  *
739  * The result is device-independent and can be used with any affine matrix.
740  */
741 
742 enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
743 
744 class DIEllipseGeometryProcessor : public GrGeometryProcessor {
745 public:
746     static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
747                                      const SkMatrix& viewMatrix, DIEllipseStyle style) {
748         return arena->make([&](void* ptr) {
749             return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
750         });
751     }
752 
753     ~DIEllipseGeometryProcessor() override {}
754 
755     const char* name() const override { return "DIEllipseGeometryProcessor"; }
756 
757     SkString getShaderDfxInfo() const override {
758         SkString format;
759         format.printf("ShaderDfx_DIEllipseGeometry_%d_%d_%d_%d", fStyle,
760             fViewMatrix.isIdentity(), fViewMatrix.isScaleTranslate(), fViewMatrix.hasPerspective());
761         return format;
762     }
763 
764     void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
765         b->addBits(2, static_cast<uint32_t>(fStyle), "style");
766         b->addBits(ProgramImpl::kMatrixKeyBits,
767                    ProgramImpl::ComputeMatrixKey(caps, fViewMatrix),
768                    "viewMatrixType");
769     }
770 
771     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
772         return std::make_unique<Impl>();
773     }
774 
775 private:
776     DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
777                                DIEllipseStyle style)
778             : INHERITED(kDIEllipseGeometryProcessor_ClassID)
779             , fViewMatrix(viewMatrix)
780             , fUseScale(useScale)
781             , fStyle(style) {
782         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
783         fInColor = MakeColorAttribute("inColor", wideColor);
784         if (useScale) {
785             fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
786                                   kFloat3_GrSLType};
787         } else {
788             fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
789                                   kFloat2_GrSLType};
790         }
791         fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
792         this->setVertexAttributes(&fInPosition, 4);
793     }
794 
795     class Impl : public ProgramImpl {
796     public:
797         void setData(const GrGLSLProgramDataManager& pdman,
798                      const GrShaderCaps& shaderCaps,
799                      const GrGeometryProcessor& geomProc) override {
800             const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
801 
802             SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
803         }
804 
805     private:
806         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
807             const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
808             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
809             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
810             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
811 
812             // emit attributes
813             varyingHandler->emitAttributes(diegp);
814 
815             GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
816             GrGLSLVarying offsets0(offsetType);
817             varyingHandler->addVarying("EllipseOffsets0", &offsets0);
818             vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
819 
820             GrGLSLVarying offsets1(kFloat2_GrSLType);
821             varyingHandler->addVarying("EllipseOffsets1", &offsets1);
822             vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
823 
824             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
825             fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
826             varyingHandler->addPassThroughAttribute(diegp.fInColor.asShaderVar(),
827                                                     args.fOutputColor);
828 
829             // Setup position
830             WriteOutputPosition(vertBuilder,
831                                 uniformHandler,
832                                 *args.fShaderCaps,
833                                 gpArgs,
834                                 diegp.fInPosition.name(),
835                                 diegp.fViewMatrix,
836                                 &fViewMatrixUniform);
837             gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
838 
839             // for outer curve
840             fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
841             fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
842             fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
843             fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
844             fragBuilder->codeAppendf(
845                     "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
846                     "                     %s.x*duvdy.x + %s.y*duvdy.y);",
847                     offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
848             if (diegp.fUseScale) {
849                 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
850             }
851 
852             fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
853             // avoid calling inversesqrt on zero.
854             if (args.fShaderCaps->floatIs32Bits()) {
855                 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
856             } else {
857                 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
858             }
859             fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
860             if (diegp.fUseScale) {
861                 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
862             }
863             if (DIEllipseStyle::kHairline == diegp.fStyle) {
864                 // can probably do this with one step
865                 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
866                 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
867             } else {
868                 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
869             }
870 
871             // for inner curve
872             if (DIEllipseStyle::kStroke == diegp.fStyle) {
873                 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
874                 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
875                 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
876                 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
877                 fragBuilder->codeAppendf(
878                         "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
879                         "              %s.x*duvdy.x + %s.y*duvdy.y);",
880                         offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
881                 if (diegp.fUseScale) {
882                     fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
883                 }
884                 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
885                 if (!args.fShaderCaps->floatIs32Bits()) {
886                     fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
887                 }
888                 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
889                 if (diegp.fUseScale) {
890                     fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
891                 }
892                 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
893             }
894 
895             fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
896         }
897 
898         SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
899         UniformHandle fViewMatrixUniform;
900     };
901 
902     Attribute fInPosition;
903     Attribute fInColor;
904     Attribute fInEllipseOffsets0;
905     Attribute fInEllipseOffsets1;
906 
907     SkMatrix fViewMatrix;
908     bool fUseScale;
909     DIEllipseStyle fStyle;
910 
911     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
912 
913     using INHERITED = GrGeometryProcessor;
914 };
915 
916 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
917 
918 #if GR_TEST_UTILS
919 GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
920     bool wideColor = d->fRandom->nextBool();
921     bool useScale = d->fRandom->nextBool();
922     SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
923     auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
924     return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
925 }
926 #endif
927 
928 ///////////////////////////////////////////////////////////////////////////////
929 
930 // We have two possible cases for geometry for a circle:
931 
932 // In the case of a normal fill, we draw geometry for the circle as an octagon.
933 static const uint16_t gFillCircleIndices[] = {
934         // enter the octagon
935         // clang-format off
936         0, 1, 8, 1, 2, 8,
937         2, 3, 8, 3, 4, 8,
938         4, 5, 8, 5, 6, 8,
939         6, 7, 8, 7, 0, 8
940         // clang-format on
941 };
942 
943 // For stroked circles, we use two nested octagons.
944 static const uint16_t gStrokeCircleIndices[] = {
945         // enter the octagon
946         // clang-format off
947         0, 1,  9, 0, 9,   8,
948         1, 2, 10, 1, 10,  9,
949         2, 3, 11, 2, 11, 10,
950         3, 4, 12, 3, 12, 11,
951         4, 5, 13, 4, 13, 12,
952         5, 6, 14, 5, 14, 13,
953         6, 7, 15, 6, 15, 14,
954         7, 0,  8, 7,  8, 15,
955         // clang-format on
956 };
957 
958 // Normalized geometry for octagons that circumscribe and lie on a circle:
959 
960 static constexpr SkScalar kOctOffset = 0.41421356237f;  // sqrt(2) - 1
961 static constexpr SkPoint kOctagonOuter[] = {
962     SkPoint::Make(-kOctOffset, -1),
963     SkPoint::Make( kOctOffset, -1),
964     SkPoint::Make( 1, -kOctOffset),
965     SkPoint::Make( 1,  kOctOffset),
966     SkPoint::Make( kOctOffset, 1),
967     SkPoint::Make(-kOctOffset, 1),
968     SkPoint::Make(-1,  kOctOffset),
969     SkPoint::Make(-1, -kOctOffset),
970 };
971 
972 // cosine and sine of pi/8
973 static constexpr SkScalar kCosPi8 = 0.923579533f;
974 static constexpr SkScalar kSinPi8 = 0.382683432f;
975 static constexpr SkPoint kOctagonInner[] = {
976     SkPoint::Make(-kSinPi8, -kCosPi8),
977     SkPoint::Make( kSinPi8, -kCosPi8),
978     SkPoint::Make( kCosPi8, -kSinPi8),
979     SkPoint::Make( kCosPi8,  kSinPi8),
980     SkPoint::Make( kSinPi8,  kCosPi8),
981     SkPoint::Make(-kSinPi8,  kCosPi8),
982     SkPoint::Make(-kCosPi8,  kSinPi8),
983     SkPoint::Make(-kCosPi8, -kSinPi8),
984 };
985 
986 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
987 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
988 static const int kVertsPerStrokeCircle = 16;
989 static const int kVertsPerFillCircle = 9;
990 
991 static int circle_type_to_vert_count(bool stroked) {
992     return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
993 }
994 
995 static int circle_type_to_index_count(bool stroked) {
996     return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
997 }
998 
999 static const uint16_t* circle_type_to_indices(bool stroked) {
1000     return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1001 }
1002 
1003 ///////////////////////////////////////////////////////////////////////////////
1004 
1005 class CircleOp final : public GrMeshDrawOp {
1006 private:
1007     using Helper = GrSimpleMeshDrawOpHelper;
1008 
1009 public:
1010     DEFINE_OP_CLASS_ID
1011 
1012     /** Optional extra params to render a partial arc rather than a full circle. */
1013     struct ArcParams {
1014         SkScalar fStartAngleRadians;
1015         SkScalar fSweepAngleRadians;
1016         bool fUseCenter;
1017     };
1018 
1019     static GrOp::Owner Make(GrRecordingContext* context,
1020                             GrPaint&& paint,
1021                             const SkMatrix& viewMatrix,
1022                             SkPoint center,
1023                             SkScalar radius,
1024                             const GrStyle& style,
1025                             const ArcParams* arcParams = nullptr) {
1026         SkASSERT(circle_stays_circle(viewMatrix));
1027         if (style.hasPathEffect()) {
1028             return nullptr;
1029         }
1030         const SkStrokeRec& stroke = style.strokeRec();
1031         SkStrokeRec::Style recStyle = stroke.getStyle();
1032         if (arcParams) {
1033             // Arc support depends on the style.
1034             switch (recStyle) {
1035                 case SkStrokeRec::kStrokeAndFill_Style:
1036                     // This produces a strange result that this op doesn't implement.
1037                     return nullptr;
1038                 case SkStrokeRec::kFill_Style:
1039                     // This supports all fills.
1040                     break;
1041                 case SkStrokeRec::kStroke_Style:
1042                     // Strokes that don't use the center point are supported with butt and round
1043                     // caps.
1044                     if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1045                         return nullptr;
1046                     }
1047                     break;
1048                 case SkStrokeRec::kHairline_Style:
1049                     // Hairline only supports butt cap. Round caps could be emulated by slightly
1050                     // extending the angle range if we ever care to.
1051                     if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1052                         return nullptr;
1053                     }
1054                     break;
1055             }
1056         }
1057         return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1058                                                radius, style, arcParams);
1059     }
1060 
1061     CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
1062              const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1063              const ArcParams* arcParams)
1064             : GrMeshDrawOp(ClassID())
1065             , fHelper(processorSet, GrAAType::kCoverage) {
1066         const SkStrokeRec& stroke = style.strokeRec();
1067         SkStrokeRec::Style recStyle = stroke.getStyle();
1068 
1069         fRoundCaps = false;
1070 
1071         viewMatrix.mapPoints(&center, 1);
1072         radius = viewMatrix.mapRadius(radius);
1073         SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
1074 
1075         bool isStrokeOnly =
1076                 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
1077         bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
1078 
1079         SkScalar innerRadius = -SK_ScalarHalf;
1080         SkScalar outerRadius = radius;
1081         SkScalar halfWidth = 0;
1082         if (hasStroke) {
1083             if (SkScalarNearlyZero(strokeWidth)) {
1084                 halfWidth = SK_ScalarHalf;
1085             } else {
1086                 halfWidth = SkScalarHalf(strokeWidth);
1087             }
1088 
1089             outerRadius += halfWidth;
1090             if (isStrokeOnly) {
1091                 innerRadius = radius - halfWidth;
1092             }
1093         }
1094 
1095         // The radii are outset for two reasons. First, it allows the shader to simply perform
1096         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1097         // Second, the outer radius is used to compute the verts of the bounding box that is
1098         // rendered and the outset ensures the box will cover all partially covered by the circle.
1099         outerRadius += SK_ScalarHalf;
1100         innerRadius -= SK_ScalarHalf;
1101         bool stroked = isStrokeOnly && innerRadius > 0.0f;
1102         fViewMatrixIfUsingLocalCoords = viewMatrix;
1103 
1104         // This makes every point fully inside the intersection plane.
1105         static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1106         // This makes every point fully outside the union plane.
1107         static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
1108         static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
1109         SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1110                                             center.fX + outerRadius, center.fY + outerRadius);
1111         if (arcParams) {
1112             // The shader operates in a space where the circle is translated to be centered at the
1113             // origin. Here we compute points on the unit circle at the starting and ending angles.
1114             SkPoint startPoint, stopPoint;
1115             startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1116             startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
1117             SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
1118             stopPoint.fY = SkScalarSin(endAngle);
1119             stopPoint.fX = SkScalarCos(endAngle);
1120 
1121             // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1122             startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1123             stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1124             startPoint.normalize();
1125             stopPoint.normalize();
1126 
1127             // We know the matrix is a similarity here. Detect mirroring which will affect how we
1128             // should orient the clip planes for arcs.
1129             SkASSERT(viewMatrix.isSimilarity());
1130             auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1131                                 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1132             if (upperLeftDet < 0) {
1133                 std::swap(startPoint, stopPoint);
1134             }
1135 
1136             fRoundCaps = style.strokeRec().getWidth() > 0 &&
1137                          style.strokeRec().getCap() == SkPaint::kRound_Cap;
1138             SkPoint roundCaps[2];
1139             if (fRoundCaps) {
1140                 // Compute the cap center points in the normalized space.
1141                 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1142                 roundCaps[0] = startPoint * midRadius;
1143                 roundCaps[1] = stopPoint * midRadius;
1144             } else {
1145                 roundCaps[0] = kUnusedRoundCaps[0];
1146                 roundCaps[1] = kUnusedRoundCaps[1];
1147             }
1148 
1149             // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
1150             // radial lines. We treat round caps the same way, but tack coverage of circles at the
1151             // center of the butts.
1152             // However, in both cases we have to be careful about the half-circle.
1153             // case. In that case the two radial lines are equal and so that edge gets clipped
1154             // twice. Since the shared edge goes through the center we fall back on the !useCenter
1155             // case.
1156             auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1157             bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1158                              !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
1159             if (useCenter) {
1160                 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1161                 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
1162                 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1163                 if (arcParams->fSweepAngleRadians < 0) {
1164                     std::swap(norm0, norm1);
1165                 }
1166                 norm0.negate();
1167                 fClipPlane = true;
1168                 if (absSweep > SK_ScalarPI) {
1169                     fCircles.emplace_back(Circle{
1170                             color,
1171                             innerRadius,
1172                             outerRadius,
1173                             {norm0.fX, norm0.fY, 0.5f},
1174                             {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1175                             {norm1.fX, norm1.fY, 0.5f},
1176                             {roundCaps[0], roundCaps[1]},
1177                             devBounds,
1178                             stroked});
1179                     fClipPlaneIsect = false;
1180                     fClipPlaneUnion = true;
1181                 } else {
1182                     fCircles.emplace_back(Circle{
1183                             color,
1184                             innerRadius,
1185                             outerRadius,
1186                             {norm0.fX, norm0.fY, 0.5f},
1187                             {norm1.fX, norm1.fY, 0.5f},
1188                             {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
1189                             {roundCaps[0], roundCaps[1]},
1190                             devBounds,
1191                             stroked});
1192                     fClipPlaneIsect = true;
1193                     fClipPlaneUnion = false;
1194                 }
1195             } else {
1196                 // We clip to a secant of the original circle.
1197                 startPoint.scale(radius);
1198                 stopPoint.scale(radius);
1199                 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1200                 norm.normalize();
1201                 if (arcParams->fSweepAngleRadians > 0) {
1202                     norm.negate();
1203                 }
1204                 SkScalar d = -norm.dot(startPoint) + 0.5f;
1205 
1206                 fCircles.emplace_back(
1207                         Circle{color,
1208                                innerRadius,
1209                                outerRadius,
1210                                {norm.fX, norm.fY, d},
1211                                {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1212                                {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
1213                                {roundCaps[0], roundCaps[1]},
1214                                devBounds,
1215                                stroked});
1216                 fClipPlane = true;
1217                 fClipPlaneIsect = false;
1218                 fClipPlaneUnion = false;
1219             }
1220         } else {
1221             fCircles.emplace_back(
1222                     Circle{color,
1223                            innerRadius,
1224                            outerRadius,
1225                            {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1226                            {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1227                            {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
1228                            {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
1229                            devBounds,
1230                            stroked});
1231             fClipPlane = false;
1232             fClipPlaneIsect = false;
1233             fClipPlaneUnion = false;
1234         }
1235         // Use the original radius and stroke radius for the bounds so that it does not include the
1236         // AA bloat.
1237         radius += halfWidth;
1238         this->setBounds(
1239                 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1240                 HasAABloat::kYes, IsHairline::kNo);
1241         fVertCount = circle_type_to_vert_count(stroked);
1242         fIndexCount = circle_type_to_index_count(stroked);
1243         fAllFill = !stroked;
1244     }
1245 
1246     const char* name() const override { return "CircleOp"; }
1247 
1248     void visitProxies(const GrVisitProxyFunc& func) const override {
1249         if (fProgramInfo) {
1250             fProgramInfo->visitFPProxies(func);
1251         } else {
1252             fHelper.visitProxies(func);
1253         }
1254     }
1255 
1256     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1257                                       GrClampType clampType) override {
1258         SkPMColor4f* color = &fCircles.front().fColor;
1259         return fHelper.finalizeProcessors(caps, clip, clampType,
1260                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
1261                                           &fWideColor);
1262     }
1263 
1264     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1265 
1266 private:
1267     GrProgramInfo* programInfo() override { return fProgramInfo; }
1268 
1269     void onCreateProgramInfo(const GrCaps* caps,
1270                              SkArenaAlloc* arena,
1271                              const GrSurfaceProxyView& writeView,
1272                              bool usesMSAASurface,
1273                              GrAppliedClip&& appliedClip,
1274                              const GrDstProxyView& dstProxyView,
1275                              GrXferBarrierFlags renderPassXferBarriers,
1276                              GrLoadOp colorLoadOp) override {
1277         SkASSERT(!usesMSAASurface);
1278 
1279         SkMatrix localMatrix;
1280         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1281             return;
1282         }
1283 
1284         GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
1285                                                                 fClipPlaneIsect, fClipPlaneUnion,
1286                                                                 fRoundCaps, fWideColor,
1287                                                                 localMatrix);
1288 
1289         fProgramInfo = fHelper.createProgramInfo(caps,
1290                                                  arena,
1291                                                  writeView,
1292                                                  usesMSAASurface,
1293                                                  std::move(appliedClip),
1294                                                  dstProxyView,
1295                                                  gp,
1296                                                  GrPrimitiveType::kTriangles,
1297                                                  renderPassXferBarriers,
1298                                                  colorLoadOp);
1299     }
1300 
1301     void onPrepareDraws(GrMeshDrawTarget* target) override {
1302         if (!fProgramInfo) {
1303             this->createProgramInfo(target);
1304             if (!fProgramInfo) {
1305                 return;
1306             }
1307         }
1308 
1309         sk_sp<const GrBuffer> vertexBuffer;
1310         int firstVertex;
1311         VertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
1312                                                       fVertCount, &vertexBuffer, &firstVertex)};
1313         if (!vertices) {
1314             SkDebugf("Could not allocate vertices\n");
1315             return;
1316         }
1317 
1318         sk_sp<const GrBuffer> indexBuffer = nullptr;
1319         int firstIndex = 0;
1320         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1321         if (!indices) {
1322             SkDebugf("Could not allocate indices\n");
1323             return;
1324         }
1325 
1326         int currStartVertex = 0;
1327         for (const auto& circle : fCircles) {
1328             SkScalar innerRadius = circle.fInnerRadius;
1329             SkScalar outerRadius = circle.fOuterRadius;
1330             GrVertexColor color(circle.fColor, fWideColor);
1331             const SkRect& bounds = circle.fDevBounds;
1332 
1333             // The inner radius in the vertex data must be specified in normalized space.
1334             innerRadius = innerRadius / outerRadius;
1335             SkPoint radii = { outerRadius, innerRadius };
1336 
1337             SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1338             SkScalar halfWidth = 0.5f * bounds.width();
1339 
1340             SkVector geoClipPlane = { 0, 0 };
1341             SkScalar offsetClipDist = SK_Scalar1;
1342             if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1343                     (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1344                      circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1345                 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1346                 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1347                 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1348                 // the AA can extend just past the center of the circle.
1349                 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1350                                  circle.fIsectPlane[0] - circle.fClipPlane[0]);
1351                 SkAssertResult(geoClipPlane.normalize());
1352                 offsetClipDist = 0.5f / halfWidth;
1353             }
1354 
1355             for (int i = 0; i < 8; ++i) {
1356                 // This clips the normalized offset to the half-plane we computed above. Then we
1357                 // compute the vertex position from this.
1358                 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
1359                 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
1360                 vertices << (center + offset * halfWidth)
1361                          << color
1362                          << offset
1363                          << radii;
1364                 if (fClipPlane) {
1365                     vertices << circle.fClipPlane;
1366                 }
1367                 if (fClipPlaneIsect) {
1368                     vertices << circle.fIsectPlane;
1369                 }
1370                 if (fClipPlaneUnion) {
1371                     vertices << circle.fUnionPlane;
1372                 }
1373                 if (fRoundCaps) {
1374                     vertices << circle.fRoundCapCenters;
1375                 }
1376             }
1377 
1378             if (circle.fStroked) {
1379                 // compute the inner ring
1380 
1381                 for (int i = 0; i < 8; ++i) {
1382                     vertices << (center + kOctagonInner[i] * circle.fInnerRadius)
1383                              << color
1384                              << kOctagonInner[i] * innerRadius
1385                              << radii;
1386                     if (fClipPlane) {
1387                         vertices << circle.fClipPlane;
1388                     }
1389                     if (fClipPlaneIsect) {
1390                         vertices << circle.fIsectPlane;
1391                     }
1392                     if (fClipPlaneUnion) {
1393                         vertices << circle.fUnionPlane;
1394                     }
1395                     if (fRoundCaps) {
1396                         vertices << circle.fRoundCapCenters;
1397                     }
1398                 }
1399             } else {
1400                 // filled
1401                 vertices << center << color << SkPoint::Make(0, 0) << radii;
1402                 if (fClipPlane) {
1403                     vertices << circle.fClipPlane;
1404                 }
1405                 if (fClipPlaneIsect) {
1406                     vertices << circle.fIsectPlane;
1407                 }
1408                 if (fClipPlaneUnion) {
1409                     vertices << circle.fUnionPlane;
1410                 }
1411                 if (fRoundCaps) {
1412                     vertices << circle.fRoundCapCenters;
1413                 }
1414             }
1415 
1416             const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1417             const int primIndexCount = circle_type_to_index_count(circle.fStroked);
1418             for (int i = 0; i < primIndexCount; ++i) {
1419                 *indices++ = primIndices[i] + currStartVertex;
1420             }
1421 
1422             currStartVertex += circle_type_to_vert_count(circle.fStroked);
1423         }
1424 
1425         fMesh = target->allocMesh();
1426         fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
1427                          GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
1428     }
1429 
1430     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
1431         if (!fProgramInfo || !fMesh) {
1432             return;
1433         }
1434 
1435         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1436         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
1437         flushState->drawMesh(*fMesh);
1438     }
1439 
1440     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
1441         CircleOp* that = t->cast<CircleOp>();
1442 
1443         // can only represent 65535 unique vertices with 16-bit indices
1444         if (fVertCount + that->fVertCount > 65536) {
1445             return CombineResult::kCannotCombine;
1446         }
1447 
1448         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1449             return CombineResult::kCannotCombine;
1450         }
1451 
1452         if (fHelper.usesLocalCoords() &&
1453             !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1454                                       that->fViewMatrixIfUsingLocalCoords)) {
1455             return CombineResult::kCannotCombine;
1456         }
1457 
1458         // Because we've set up the ops that don't use the planes with noop values
1459         // we can just accumulate used planes by later ops.
1460         fClipPlane |= that->fClipPlane;
1461         fClipPlaneIsect |= that->fClipPlaneIsect;
1462         fClipPlaneUnion |= that->fClipPlaneUnion;
1463         fRoundCaps |= that->fRoundCaps;
1464         fWideColor |= that->fWideColor;
1465 
1466         fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1467         fVertCount += that->fVertCount;
1468         fIndexCount += that->fIndexCount;
1469         fAllFill = fAllFill && that->fAllFill;
1470         return CombineResult::kMerged;
1471     }
1472 
1473 #if GR_TEST_UTILS
1474     SkString onDumpInfo() const override {
1475         SkString string;
1476         for (int i = 0; i < fCircles.count(); ++i) {
1477             string.appendf(
1478                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1479                     "InnerRad: %.2f, OuterRad: %.2f\n",
1480                     fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1481                     fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1482                     fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1483                     fCircles[i].fOuterRadius);
1484         }
1485         string += fHelper.dumpInfo();
1486         return string;
1487     }
1488 #endif
1489 
1490     struct Circle {
1491         SkPMColor4f fColor;
1492         SkScalar fInnerRadius;
1493         SkScalar fOuterRadius;
1494         SkScalar fClipPlane[3];
1495         SkScalar fIsectPlane[3];
1496         SkScalar fUnionPlane[3];
1497         SkPoint fRoundCapCenters[2];
1498         SkRect fDevBounds;
1499         bool fStroked;
1500     };
1501 
1502     SkMatrix fViewMatrixIfUsingLocalCoords;
1503     Helper fHelper;
1504     SkSTArray<1, Circle, true> fCircles;
1505     int fVertCount;
1506     int fIndexCount;
1507     bool fAllFill;
1508     bool fClipPlane;
1509     bool fClipPlaneIsect;
1510     bool fClipPlaneUnion;
1511     bool fRoundCaps;
1512     bool fWideColor;
1513 
1514     GrSimpleMesh*  fMesh = nullptr;
1515     GrProgramInfo* fProgramInfo = nullptr;
1516 
1517     using INHERITED = GrMeshDrawOp;
1518 };
1519 
1520 class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1521 private:
1522     using Helper = GrSimpleMeshDrawOpHelper;
1523 
1524 public:
1525     DEFINE_OP_CLASS_ID
1526 
1527     static GrOp::Owner Make(GrRecordingContext* context,
1528                             GrPaint&& paint,
1529                             const SkMatrix& viewMatrix,
1530                             SkPoint center,
1531                             SkScalar radius,
1532                             SkScalar strokeWidth,
1533                             SkScalar startAngle,
1534                             SkScalar onAngle,
1535                             SkScalar offAngle,
1536                             SkScalar phaseAngle) {
1537         SkASSERT(circle_stays_circle(viewMatrix));
1538         SkASSERT(strokeWidth < 2 * radius);
1539         return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1540                                                             center, radius, strokeWidth, startAngle,
1541                                                             onAngle, offAngle, phaseAngle);
1542     }
1543 
1544     ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
1545                           const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1546                           SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1547                           SkScalar offAngle, SkScalar phaseAngle)
1548             : GrMeshDrawOp(ClassID())
1549             , fHelper(processorSet, GrAAType::kCoverage) {
1550         SkASSERT(circle_stays_circle(viewMatrix));
1551         viewMatrix.mapPoints(&center, 1);
1552         radius = viewMatrix.mapRadius(radius);
1553         strokeWidth = viewMatrix.mapRadius(strokeWidth);
1554 
1555         // Determine the angle where the circle starts in device space and whether its orientation
1556         // has been reversed.
1557         SkVector start;
1558         bool reflection;
1559         if (!startAngle) {
1560             start = {1, 0};
1561         } else {
1562             start.fY = SkScalarSin(startAngle);
1563             start.fX = SkScalarCos(startAngle);
1564         }
1565         viewMatrix.mapVectors(&start, 1);
1566         startAngle = SkScalarATan2(start.fY, start.fX);
1567         reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1568                       viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1569 
1570         auto totalAngle = onAngle + offAngle;
1571         phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1572 
1573         SkScalar halfWidth = 0;
1574         if (SkScalarNearlyZero(strokeWidth)) {
1575             halfWidth = SK_ScalarHalf;
1576         } else {
1577             halfWidth = SkScalarHalf(strokeWidth);
1578         }
1579 
1580         SkScalar outerRadius = radius + halfWidth;
1581         SkScalar innerRadius = radius - halfWidth;
1582 
1583         // The radii are outset for two reasons. First, it allows the shader to simply perform
1584         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1585         // Second, the outer radius is used to compute the verts of the bounding box that is
1586         // rendered and the outset ensures the box will cover all partially covered by the circle.
1587         outerRadius += SK_ScalarHalf;
1588         innerRadius -= SK_ScalarHalf;
1589         fViewMatrixIfUsingLocalCoords = viewMatrix;
1590 
1591         SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1592                                             center.fX + outerRadius, center.fY + outerRadius);
1593 
1594         // We store whether there is a reflection as a negative total angle.
1595         if (reflection) {
1596             totalAngle = -totalAngle;
1597         }
1598         fCircles.push_back(Circle{
1599             color,
1600             outerRadius,
1601             innerRadius,
1602             onAngle,
1603             totalAngle,
1604             startAngle,
1605             phaseAngle,
1606             devBounds
1607         });
1608         // Use the original radius and stroke radius for the bounds so that it does not include the
1609         // AA bloat.
1610         radius += halfWidth;
1611         this->setBounds(
1612                 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1613                 HasAABloat::kYes, IsHairline::kNo);
1614         fVertCount = circle_type_to_vert_count(true);
1615         fIndexCount = circle_type_to_index_count(true);
1616     }
1617 
1618     const char* name() const override { return "ButtCappedDashedCircleOp"; }
1619 
1620     void visitProxies(const GrVisitProxyFunc& func) const override {
1621         if (fProgramInfo) {
1622             fProgramInfo->visitFPProxies(func);
1623         } else {
1624             fHelper.visitProxies(func);
1625         }
1626     }
1627 
1628     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1629                                       GrClampType clampType) override {
1630         SkPMColor4f* color = &fCircles.front().fColor;
1631         return fHelper.finalizeProcessors(caps, clip, clampType,
1632                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
1633                                           &fWideColor);
1634     }
1635 
1636     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1637 
1638 private:
1639     GrProgramInfo* programInfo() override { return fProgramInfo; }
1640 
1641     void onCreateProgramInfo(const GrCaps* caps,
1642                              SkArenaAlloc* arena,
1643                              const GrSurfaceProxyView& writeView,
1644                              bool usesMSAASurface,
1645                              GrAppliedClip&& appliedClip,
1646                              const GrDstProxyView& dstProxyView,
1647                              GrXferBarrierFlags renderPassXferBarriers,
1648                              GrLoadOp colorLoadOp) override {
1649         SkASSERT(!usesMSAASurface);
1650 
1651         SkMatrix localMatrix;
1652         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1653             return;
1654         }
1655 
1656         // Setup geometry processor
1657         GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
1658                                                                              fWideColor,
1659                                                                              localMatrix);
1660 
1661         fProgramInfo = fHelper.createProgramInfo(caps,
1662                                                  arena,
1663                                                  writeView,
1664                                                  usesMSAASurface,
1665                                                  std::move(appliedClip),
1666                                                  dstProxyView,
1667                                                  gp,
1668                                                  GrPrimitiveType::kTriangles,
1669                                                  renderPassXferBarriers,
1670                                                  colorLoadOp);
1671     }
1672 
1673     void onPrepareDraws(GrMeshDrawTarget* target) override {
1674         if (!fProgramInfo) {
1675             this->createProgramInfo(target);
1676             if (!fProgramInfo) {
1677                 return;
1678             }
1679         }
1680 
1681         sk_sp<const GrBuffer> vertexBuffer;
1682         int firstVertex;
1683         VertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
1684                                                       fVertCount, &vertexBuffer, &firstVertex)};
1685         if (!vertices) {
1686             SkDebugf("Could not allocate vertices\n");
1687             return;
1688         }
1689 
1690         sk_sp<const GrBuffer> indexBuffer;
1691         int firstIndex = 0;
1692         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1693         if (!indices) {
1694             SkDebugf("Could not allocate indices\n");
1695             return;
1696         }
1697 
1698         int currStartVertex = 0;
1699         for (const auto& circle : fCircles) {
1700             // The inner radius in the vertex data must be specified in normalized space so that
1701             // length() can be called with smaller values to avoid precision issues with half
1702             // floats.
1703             auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1704             const SkRect& bounds = circle.fDevBounds;
1705             bool reflect = false;
1706             struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1707                 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1708             };
1709             if (dashParams.totalAngle < 0) {
1710                 reflect = true;
1711                 dashParams.totalAngle = -dashParams.totalAngle;
1712                 dashParams.startAngle = -dashParams.startAngle;
1713             }
1714 
1715             GrVertexColor color(circle.fColor, fWideColor);
1716 
1717             // The bounding geometry for the circle is composed of an outer bounding octagon and
1718             // an inner bounded octagon.
1719 
1720             // Compute the vertices of the outer octagon.
1721             SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1722             SkScalar halfWidth = 0.5f * bounds.width();
1723 
1724             auto reflectY = [=](const SkPoint& p) {
1725                 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
1726             };
1727 
1728             for (int i = 0; i < 8; ++i) {
1729                 vertices << (center + kOctagonOuter[i] * halfWidth)
1730                          << color
1731                          << reflectY(kOctagonOuter[i])
1732                          << circle.fOuterRadius
1733                          << normInnerRadius
1734                          << dashParams;
1735             }
1736 
1737             // Compute the vertices of the inner octagon.
1738             for (int i = 0; i < 8; ++i) {
1739                 vertices << (center + kOctagonInner[i] * circle.fInnerRadius)
1740                          << color
1741                          << (reflectY(kOctagonInner[i]) * normInnerRadius)
1742                          << circle.fOuterRadius
1743                          << normInnerRadius
1744                          << dashParams;
1745             }
1746 
1747             const uint16_t* primIndices = circle_type_to_indices(true);
1748             const int primIndexCount = circle_type_to_index_count(true);
1749             for (int i = 0; i < primIndexCount; ++i) {
1750                 *indices++ = primIndices[i] + currStartVertex;
1751             }
1752 
1753             currStartVertex += circle_type_to_vert_count(true);
1754         }
1755 
1756         fMesh = target->allocMesh();
1757         fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
1758                           GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
1759     }
1760 
1761     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
1762         if (!fProgramInfo || !fMesh) {
1763             return;
1764         }
1765 
1766         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1767         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
1768         flushState->drawMesh(*fMesh);
1769     }
1770 
1771     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
1772         ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1773 
1774         // can only represent 65535 unique vertices with 16-bit indices
1775         if (fVertCount + that->fVertCount > 65536) {
1776             return CombineResult::kCannotCombine;
1777         }
1778 
1779         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1780             return CombineResult::kCannotCombine;
1781         }
1782 
1783         if (fHelper.usesLocalCoords() &&
1784             !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1785                                       that->fViewMatrixIfUsingLocalCoords)) {
1786             return CombineResult::kCannotCombine;
1787         }
1788 
1789         fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1790         fVertCount += that->fVertCount;
1791         fIndexCount += that->fIndexCount;
1792         fWideColor |= that->fWideColor;
1793         return CombineResult::kMerged;
1794     }
1795 
1796 #if GR_TEST_UTILS
1797     SkString onDumpInfo() const override {
1798         SkString string;
1799         for (int i = 0; i < fCircles.count(); ++i) {
1800             string.appendf(
1801                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1802                     "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1803                     "Phase: %.2f\n",
1804                     fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1805                     fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1806                     fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1807                     fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1808                     fCircles[i].fPhaseAngle);
1809         }
1810         string += fHelper.dumpInfo();
1811         return string;
1812     }
1813 #endif
1814 
1815     struct Circle {
1816         SkPMColor4f fColor;
1817         SkScalar fOuterRadius;
1818         SkScalar fInnerRadius;
1819         SkScalar fOnAngle;
1820         SkScalar fTotalAngle;
1821         SkScalar fStartAngle;
1822         SkScalar fPhaseAngle;
1823         SkRect fDevBounds;
1824     };
1825 
1826     SkMatrix fViewMatrixIfUsingLocalCoords;
1827     Helper fHelper;
1828     SkSTArray<1, Circle, true> fCircles;
1829     int fVertCount;
1830     int fIndexCount;
1831     bool fWideColor;
1832 
1833     GrSimpleMesh*  fMesh = nullptr;
1834     GrProgramInfo* fProgramInfo = nullptr;
1835 
1836     using INHERITED = GrMeshDrawOp;
1837 };
1838 
1839 ///////////////////////////////////////////////////////////////////////////////
1840 
1841 class EllipseOp : public GrMeshDrawOp {
1842 private:
1843     using Helper = GrSimpleMeshDrawOpHelper;
1844 
1845     struct DeviceSpaceParams {
1846         SkPoint fCenter;
1847         SkScalar fXRadius;
1848         SkScalar fYRadius;
1849         SkScalar fInnerXRadius;
1850         SkScalar fInnerYRadius;
1851     };
1852 
1853 public:
1854     DEFINE_OP_CLASS_ID
1855 
1856     static GrOp::Owner Make(GrRecordingContext* context,
1857                             GrPaint&& paint,
1858                             const SkMatrix& viewMatrix,
1859                             const SkRect& ellipse,
1860                             const SkStrokeRec& stroke) {
1861         DeviceSpaceParams params;
1862         // do any matrix crunching before we reset the draw state for device coords
1863         params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1864         viewMatrix.mapPoints(&params.fCenter, 1);
1865         SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1866         SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1867         params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1868                                       viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1869         params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1870                                       viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
1871 
1872         // do (potentially) anisotropic mapping of stroke
1873         SkVector scaledStroke;
1874         SkScalar strokeWidth = stroke.getWidth();
1875         scaledStroke.fX = SkScalarAbs(
1876                 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1877         scaledStroke.fY = SkScalarAbs(
1878                 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
1879 
1880         SkStrokeRec::Style style = stroke.getStyle();
1881         bool isStrokeOnly =
1882                 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1883         bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1884 
1885         params.fInnerXRadius = 0;
1886         params.fInnerYRadius = 0;
1887         if (hasStroke) {
1888             if (SkScalarNearlyZero(scaledStroke.length())) {
1889                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1890             } else {
1891                 scaledStroke.scale(SK_ScalarHalf);
1892             }
1893 
1894             // we only handle thick strokes for near-circular ellipses
1895             if (scaledStroke.length() > SK_ScalarHalf &&
1896                 (0.5f * params.fXRadius > params.fYRadius ||
1897                  0.5f * params.fYRadius > params.fXRadius)) {
1898                 return nullptr;
1899             }
1900 
1901             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1902             if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1903                         (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1904                 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1905                         (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
1906                 return nullptr;
1907             }
1908 
1909             // this is legit only if scale & translation (which should be the case at the moment)
1910             if (isStrokeOnly) {
1911                 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1912                 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
1913             }
1914 
1915             params.fXRadius += scaledStroke.fX;
1916             params.fYRadius += scaledStroke.fY;
1917         }
1918 
1919         // For large ovals with low precision floats, we fall back to the path renderer.
1920         // To compute the AA at the edge we divide by the gradient, which is clamped to a
1921         // minimum value to avoid divides by zero. With large ovals and low precision this
1922         // leads to blurring at the edge of the oval.
1923         const SkScalar kMaxOvalRadius = 16384;
1924         if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1925             (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1926             return nullptr;
1927         }
1928 
1929         return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
1930                                                 params, stroke);
1931     }
1932 
1933     EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
1934               const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
1935               const SkStrokeRec& stroke)
1936             : INHERITED(ClassID())
1937             , fHelper(processorSet, GrAAType::kCoverage)
1938             , fUseScale(false) {
1939         SkStrokeRec::Style style = stroke.getStyle();
1940         bool isStrokeOnly =
1941                 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1942 
1943         fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1944                                        params.fInnerXRadius, params.fInnerYRadius,
1945                                        SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1946                                                         params.fCenter.fY - params.fYRadius,
1947                                                         params.fCenter.fX + params.fXRadius,
1948                                                         params.fCenter.fY + params.fYRadius)});
1949 
1950         this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
1951 
1952         fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1953         fViewMatrixIfUsingLocalCoords = viewMatrix;
1954     }
1955 
1956     const char* name() const override { return "EllipseOp"; }
1957 
1958     void visitProxies(const GrVisitProxyFunc& func) const override {
1959         if (fProgramInfo) {
1960             fProgramInfo->visitFPProxies(func);
1961         } else {
1962             fHelper.visitProxies(func);
1963         }
1964     }
1965 
1966     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1967                                       GrClampType clampType) override {
1968         fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1969                     !caps.shaderCaps()->hasLowFragmentPrecision();
1970         SkPMColor4f* color = &fEllipses.front().fColor;
1971         return fHelper.finalizeProcessors(caps, clip, clampType,
1972                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
1973                                           &fWideColor);
1974     }
1975 
1976     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1977 
1978 private:
1979     GrProgramInfo* programInfo() override { return fProgramInfo; }
1980 
1981     void onCreateProgramInfo(const GrCaps* caps,
1982                              SkArenaAlloc* arena,
1983                              const GrSurfaceProxyView& writeView,
1984                              bool usesMSAASurface,
1985                              GrAppliedClip&& appliedClip,
1986                              const GrDstProxyView& dstProxyView,
1987                              GrXferBarrierFlags renderPassXferBarriers,
1988                              GrLoadOp colorLoadOp) override {
1989         SkMatrix localMatrix;
1990         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1991             return;
1992         }
1993 
1994         GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1995                                                                  fUseScale, localMatrix);
1996 
1997         fProgramInfo = fHelper.createProgramInfo(caps,
1998                                                  arena,
1999                                                  writeView,
2000                                                  usesMSAASurface,
2001                                                  std::move(appliedClip),
2002                                                  dstProxyView,
2003                                                  gp,
2004                                                  GrPrimitiveType::kTriangles,
2005                                                  renderPassXferBarriers,
2006                                                  colorLoadOp);
2007     }
2008 
2009     void onPrepareDraws(GrMeshDrawTarget* target) override {
2010         if (!fProgramInfo) {
2011             this->createProgramInfo(target);
2012             if (!fProgramInfo) {
2013                 return;
2014             }
2015         }
2016 
2017         QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
2018         VertexWriter verts{helper.vertices()};
2019         if (!verts) {
2020             SkDebugf("Could not allocate vertices\n");
2021             return;
2022         }
2023 
2024         // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2025         // full sample coverage.
2026         float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2027 
2028         for (const auto& ellipse : fEllipses) {
2029             GrVertexColor color(ellipse.fColor, fWideColor);
2030             SkScalar xRadius = ellipse.fXRadius;
2031             SkScalar yRadius = ellipse.fYRadius;
2032 
2033             // Compute the reciprocals of the radii here to save time in the shader
2034             struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2035                 SkScalarInvert(xRadius),
2036                 SkScalarInvert(yRadius),
2037                 SkScalarInvert(ellipse.fInnerXRadius),
2038                 SkScalarInvert(ellipse.fInnerYRadius)
2039             };
2040             SkScalar xMaxOffset = xRadius + aaBloat;
2041             SkScalar yMaxOffset = yRadius + aaBloat;
2042 
2043             if (!fStroked) {
2044                 // For filled ellipses we map a unit circle in the vertex attributes rather than
2045                 // computing an ellipse and modifying that distance, so we normalize to 1
2046                 xMaxOffset /= xRadius;
2047                 yMaxOffset /= yRadius;
2048             }
2049 
2050             // The inner radius in the vertex data must be specified in normalized space.
2051             verts.writeQuad(VertexWriter::TriStripFromRect(
2052                                     ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
2053                             color,
2054                             origin_centered_tri_strip(xMaxOffset, yMaxOffset),
2055                             VertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
2056                             invRadii);
2057         }
2058         fMesh = helper.mesh();
2059     }
2060 
2061     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2062         if (!fProgramInfo || !fMesh) {
2063             return;
2064         }
2065 
2066         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2067         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
2068         flushState->drawMesh(*fMesh);
2069     }
2070 
2071     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
2072         EllipseOp* that = t->cast<EllipseOp>();
2073 
2074         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2075             return CombineResult::kCannotCombine;
2076         }
2077 
2078         if (fStroked != that->fStroked) {
2079             return CombineResult::kCannotCombine;
2080         }
2081 
2082         if (fHelper.usesLocalCoords() &&
2083             !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2084                                       that->fViewMatrixIfUsingLocalCoords)) {
2085             return CombineResult::kCannotCombine;
2086         }
2087 
2088         fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
2089         fWideColor |= that->fWideColor;
2090         return CombineResult::kMerged;
2091     }
2092 
2093 #if GR_TEST_UTILS
2094     SkString onDumpInfo() const override {
2095         SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2096         for (const auto& geo : fEllipses) {
2097             string.appendf(
2098                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2099                     "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2100                     geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2101                     geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2102                     geo.fInnerXRadius, geo.fInnerYRadius);
2103         }
2104         string += fHelper.dumpInfo();
2105         return string;
2106     }
2107 #endif
2108 
2109     struct Ellipse {
2110         SkPMColor4f fColor;
2111         SkScalar fXRadius;
2112         SkScalar fYRadius;
2113         SkScalar fInnerXRadius;
2114         SkScalar fInnerYRadius;
2115         SkRect fDevBounds;
2116     };
2117 
2118     SkMatrix fViewMatrixIfUsingLocalCoords;
2119     Helper fHelper;
2120     bool fStroked;
2121     bool fWideColor;
2122     bool fUseScale;
2123     SkSTArray<1, Ellipse, true> fEllipses;
2124 
2125     GrSimpleMesh*  fMesh = nullptr;
2126     GrProgramInfo* fProgramInfo = nullptr;
2127 
2128     using INHERITED = GrMeshDrawOp;
2129 };
2130 
2131 /////////////////////////////////////////////////////////////////////////////////////////////////
2132 
2133 class DIEllipseOp : public GrMeshDrawOp {
2134 private:
2135     using Helper = GrSimpleMeshDrawOpHelper;
2136 
2137     struct DeviceSpaceParams {
2138         SkPoint fCenter;
2139         SkScalar fXRadius;
2140         SkScalar fYRadius;
2141         SkScalar fInnerXRadius;
2142         SkScalar fInnerYRadius;
2143         DIEllipseStyle fStyle;
2144     };
2145 
2146 public:
2147     DEFINE_OP_CLASS_ID
2148 
2149     static GrOp::Owner Make(GrRecordingContext* context,
2150                             GrPaint&& paint,
2151                             const SkMatrix& viewMatrix,
2152                             const SkRect& ellipse,
2153                             const SkStrokeRec& stroke) {
2154         DeviceSpaceParams params;
2155         params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2156         params.fXRadius = SkScalarHalf(ellipse.width());
2157         params.fYRadius = SkScalarHalf(ellipse.height());
2158 
2159         SkStrokeRec::Style style = stroke.getStyle();
2160         params.fStyle = (SkStrokeRec::kStroke_Style == style)
2161                                 ? DIEllipseStyle::kStroke
2162                                 : (SkStrokeRec::kHairline_Style == style)
2163                                           ? DIEllipseStyle::kHairline
2164                                           : DIEllipseStyle::kFill;
2165 
2166         params.fInnerXRadius = 0;
2167         params.fInnerYRadius = 0;
2168         if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2169             SkScalar strokeWidth = stroke.getWidth();
2170 
2171             if (SkScalarNearlyZero(strokeWidth)) {
2172                 strokeWidth = SK_ScalarHalf;
2173             } else {
2174                 strokeWidth *= SK_ScalarHalf;
2175             }
2176 
2177             // we only handle thick strokes for near-circular ellipses
2178             if (strokeWidth > SK_ScalarHalf &&
2179                 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2180                  SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
2181                 return nullptr;
2182             }
2183 
2184             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2185             if (strokeWidth * (params.fYRadius * params.fYRadius) <
2186                 (strokeWidth * strokeWidth) * params.fXRadius) {
2187                 return nullptr;
2188             }
2189             if (strokeWidth * (params.fXRadius * params.fXRadius) <
2190                 (strokeWidth * strokeWidth) * params.fYRadius) {
2191                 return nullptr;
2192             }
2193 
2194             // set inner radius (if needed)
2195             if (SkStrokeRec::kStroke_Style == style) {
2196                 params.fInnerXRadius = params.fXRadius - strokeWidth;
2197                 params.fInnerYRadius = params.fYRadius - strokeWidth;
2198             }
2199 
2200             params.fXRadius += strokeWidth;
2201             params.fYRadius += strokeWidth;
2202         }
2203 
2204         // For large ovals with low precision floats, we fall back to the path renderer.
2205         // To compute the AA at the edge we divide by the gradient, which is clamped to a
2206         // minimum value to avoid divides by zero. With large ovals and low precision this
2207         // leads to blurring at the edge of the oval.
2208         const SkScalar kMaxOvalRadius = 16384;
2209         if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2210             (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2211             return nullptr;
2212         }
2213 
2214         if (DIEllipseStyle::kStroke == params.fStyle &&
2215             (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2216             params.fStyle = DIEllipseStyle::kFill;
2217         }
2218         return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
2219     }
2220 
2221     DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
2222                 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
2223             : INHERITED(ClassID())
2224             , fHelper(processorSet, GrAAType::kCoverage)
2225             , fUseScale(false) {
2226         // This expands the outer rect so that after CTM we end up with a half-pixel border
2227         SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2228         SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2229         SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2230         SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2231         SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2232         SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
2233 
2234         fEllipses.emplace_back(
2235                 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2236                         params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2237                         SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2238                                          params.fCenter.fY - params.fYRadius,
2239                                          params.fCenter.fX + params.fXRadius,
2240                                          params.fCenter.fY + params.fYRadius)});
2241         this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2242                                    IsHairline::kNo);
2243     }
2244 
2245     const char* name() const override { return "DIEllipseOp"; }
2246 
2247     void visitProxies(const GrVisitProxyFunc& func) const override {
2248         if (fProgramInfo) {
2249             fProgramInfo->visitFPProxies(func);
2250         } else {
2251             fHelper.visitProxies(func);
2252         }
2253     }
2254 
2255     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2256                                       GrClampType clampType) override {
2257         fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2258                     !caps.shaderCaps()->hasLowFragmentPrecision();
2259         SkPMColor4f* color = &fEllipses.front().fColor;
2260         return fHelper.finalizeProcessors(caps, clip, clampType,
2261                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
2262                                           &fWideColor);
2263     }
2264 
2265     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2266 
2267 private:
2268     GrProgramInfo* programInfo() override { return fProgramInfo; }
2269 
2270     void onCreateProgramInfo(const GrCaps* caps,
2271                              SkArenaAlloc* arena,
2272                              const GrSurfaceProxyView& writeView,
2273                              bool usesMSAASurface,
2274                              GrAppliedClip&& appliedClip,
2275                              const GrDstProxyView& dstProxyView,
2276                              GrXferBarrierFlags renderPassXferBarriers,
2277                              GrLoadOp colorLoadOp) override {
2278         GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2279                                                                    this->viewMatrix(),
2280                                                                    this->style());
2281 
2282         fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
2283                                                  std::move(appliedClip), dstProxyView, gp,
2284                                                  GrPrimitiveType::kTriangles,
2285                                                  renderPassXferBarriers, colorLoadOp);
2286     }
2287 
2288     void onPrepareDraws(GrMeshDrawTarget* target) override {
2289         if (!fProgramInfo) {
2290             this->createProgramInfo(target);
2291         }
2292 
2293         QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
2294         VertexWriter verts{helper.vertices()};
2295         if (!verts) {
2296             return;
2297         }
2298 
2299         for (const auto& ellipse : fEllipses) {
2300             GrVertexColor color(ellipse.fColor, fWideColor);
2301             SkScalar xRadius = ellipse.fXRadius;
2302             SkScalar yRadius = ellipse.fYRadius;
2303 
2304             // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2305             // full sample coverage.
2306             float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2307             SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2308                                                            ellipse.fGeoDy * aaBloat);
2309 
2310             // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2311             // occurs at x^2 + y^2 == 1.
2312             float outerCoordX = drawBounds.width() / (xRadius * 2);
2313             float outerCoordY = drawBounds.height() / (yRadius * 2);
2314 
2315             // By default, constructed so that inner coord is (0, 0) for all points
2316             float innerCoordX = 0;
2317             float innerCoordY = 0;
2318 
2319             // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2320             // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
2321             if (DIEllipseStyle::kStroke == this->style()) {
2322                 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2323                 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
2324             }
2325 
2326             verts.writeQuad(VertexWriter::TriStripFromRect(drawBounds),
2327                             color,
2328                             origin_centered_tri_strip(outerCoordX, outerCoordY),
2329                             VertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
2330                             origin_centered_tri_strip(innerCoordX, innerCoordY));
2331         }
2332         fMesh = helper.mesh();
2333     }
2334 
2335     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2336         if (!fProgramInfo || !fMesh) {
2337             return;
2338         }
2339 
2340         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2341         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
2342         flushState->drawMesh(*fMesh);
2343     }
2344 
2345     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
2346         DIEllipseOp* that = t->cast<DIEllipseOp>();
2347         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2348             return CombineResult::kCannotCombine;
2349         }
2350 
2351         if (this->style() != that->style()) {
2352             return CombineResult::kCannotCombine;
2353         }
2354 
2355         // TODO rewrite to allow positioning on CPU
2356         if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
2357             return CombineResult::kCannotCombine;
2358         }
2359 
2360         fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
2361         fWideColor |= that->fWideColor;
2362         return CombineResult::kMerged;
2363     }
2364 
2365 #if GR_TEST_UTILS
2366     SkString onDumpInfo() const override {
2367         SkString string;
2368         for (const auto& geo : fEllipses) {
2369             string.appendf(
2370                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2371                     "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2372                     "GeoDY: %.2f\n",
2373                     geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2374                     geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2375                     geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2376         }
2377         string += fHelper.dumpInfo();
2378         return string;
2379     }
2380 #endif
2381 
2382     const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2383     DIEllipseStyle style() const { return fEllipses[0].fStyle; }
2384 
2385     struct Ellipse {
2386         SkMatrix fViewMatrix;
2387         SkPMColor4f fColor;
2388         SkScalar fXRadius;
2389         SkScalar fYRadius;
2390         SkScalar fInnerXRadius;
2391         SkScalar fInnerYRadius;
2392         SkScalar fGeoDx;
2393         SkScalar fGeoDy;
2394         DIEllipseStyle fStyle;
2395         SkRect fBounds;
2396     };
2397 
2398     Helper fHelper;
2399     bool fWideColor;
2400     bool fUseScale;
2401     SkSTArray<1, Ellipse, true> fEllipses;
2402 
2403     GrSimpleMesh*  fMesh = nullptr;
2404     GrProgramInfo* fProgramInfo = nullptr;
2405 
2406     using INHERITED = GrMeshDrawOp;
2407 };
2408 
2409 ///////////////////////////////////////////////////////////////////////////////
2410 
2411 // We have three possible cases for geometry for a roundrect.
2412 //
2413 // In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2414 //    ____________
2415 //   |_|________|_|
2416 //   | |        | |
2417 //   | |        | |
2418 //   | |        | |
2419 //   |_|________|_|
2420 //   |_|________|_|
2421 //
2422 // For strokes, we don't draw the center quad.
2423 //
2424 // For circular roundrects, in the case where the stroke width is greater than twice
2425 // the corner radius (overstroke), we add additional geometry to mark out the rectangle
2426 // in the center. The shared vertices are duplicated so we can set a different outer radius
2427 // for the fill calculation.
2428 //    ____________
2429 //   |_|________|_|
2430 //   | |\ ____ /| |
2431 //   | | |    | | |
2432 //   | | |____| | |
2433 //   |_|/______\|_|
2434 //   |_|________|_|
2435 //
2436 // We don't draw the center quad from the fill rect in this case.
2437 //
2438 // For filled rrects that need to provide a distance vector we resuse the overstroke
2439 // geometry but make the inner rect degenerate (either a point or a horizontal or
2440 // vertical line).
2441 
2442 static const uint16_t gOverstrokeRRectIndices[] = {
2443         // clang-format off
2444         // overstroke quads
2445         // we place this at the beginning so that we can skip these indices when rendering normally
2446         16, 17, 19, 16, 19, 18,
2447         19, 17, 23, 19, 23, 21,
2448         21, 23, 22, 21, 22, 20,
2449         22, 16, 18, 22, 18, 20,
2450 
2451         // corners
2452         0, 1, 5, 0, 5, 4,
2453         2, 3, 7, 2, 7, 6,
2454         8, 9, 13, 8, 13, 12,
2455         10, 11, 15, 10, 15, 14,
2456 
2457         // edges
2458         1, 2, 6, 1, 6, 5,
2459         4, 5, 9, 4, 9, 8,
2460         6, 7, 11, 6, 11, 10,
2461         9, 10, 14, 9, 14, 13,
2462 
2463         // center
2464         // we place this at the end so that we can ignore these indices when not rendering as filled
2465         5, 6, 10, 5, 10, 9,
2466         // clang-format on
2467 };
2468 
2469 // fill and standard stroke indices skip the overstroke "ring"
2470 static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
2471 
2472 // overstroke count is arraysize minus the center indices
2473 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2474 // fill count skips overstroke indices and includes center
2475 static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
2476 // stroke count is fill count minus center indices
2477 static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2478 static const int kVertsPerStandardRRect = 16;
2479 static const int kVertsPerOverstrokeRRect = 24;
2480 
2481 enum RRectType {
2482     kFill_RRectType,
2483     kStroke_RRectType,
2484     kOverstroke_RRectType,
2485 };
2486 
2487 static int rrect_type_to_vert_count(RRectType type) {
2488     switch (type) {
2489         case kFill_RRectType:
2490         case kStroke_RRectType:
2491             return kVertsPerStandardRRect;
2492         case kOverstroke_RRectType:
2493             return kVertsPerOverstrokeRRect;
2494     }
2495     SK_ABORT("Invalid type");
2496 }
2497 
2498 static int rrect_type_to_index_count(RRectType type) {
2499     switch (type) {
2500         case kFill_RRectType:
2501             return kIndicesPerFillRRect;
2502         case kStroke_RRectType:
2503             return kIndicesPerStrokeRRect;
2504         case kOverstroke_RRectType:
2505             return kIndicesPerOverstrokeRRect;
2506     }
2507     SK_ABORT("Invalid type");
2508 }
2509 
2510 static const uint16_t* rrect_type_to_indices(RRectType type) {
2511     switch (type) {
2512         case kFill_RRectType:
2513         case kStroke_RRectType:
2514             return gStandardRRectIndices;
2515         case kOverstroke_RRectType:
2516             return gOverstrokeRRectIndices;
2517     }
2518     SK_ABORT("Invalid type");
2519 }
2520 
2521 ///////////////////////////////////////////////////////////////////////////////////////////////////
2522 
2523 // For distance computations in the interior of filled rrects we:
2524 //
2525 //   add a interior degenerate (point or line) rect
2526 //   each vertex of that rect gets -outerRad as its radius
2527 //      this makes the computation of the distance to the outer edge be negative
2528 //      negative values are caught and then handled differently in the GP's onEmitCode
2529 //   each vertex is also given the normalized x & y distance from the interior rect's edge
2530 //      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2531 
2532 class CircularRRectOp : public GrMeshDrawOp {
2533 private:
2534     using Helper = GrSimpleMeshDrawOpHelper;
2535 
2536 public:
2537     DEFINE_OP_CLASS_ID
2538 
2539     // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2540     // whether the rrect is only stroked or stroked and filled.
2541     static GrOp::Owner Make(GrRecordingContext* context,
2542                             GrPaint&& paint,
2543                             const SkMatrix& viewMatrix,
2544                             const SkRect& devRect,
2545                             float devRadius,
2546                             float devStrokeWidth,
2547                             bool strokeOnly) {
2548         return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
2549                                                       devRect, devRadius,
2550                                                       devStrokeWidth, strokeOnly);
2551     }
2552     CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
2553                     const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2554                     float devStrokeWidth, bool strokeOnly)
2555             : INHERITED(ClassID())
2556             , fViewMatrixIfUsingLocalCoords(viewMatrix)
2557             , fHelper(processorSet, GrAAType::kCoverage) {
2558         SkRect bounds = devRect;
2559         SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2560         SkScalar innerRadius = 0.0f;
2561         SkScalar outerRadius = devRadius;
2562         SkScalar halfWidth = 0;
2563         RRectType type = kFill_RRectType;
2564         if (devStrokeWidth > 0) {
2565             if (SkScalarNearlyZero(devStrokeWidth)) {
2566                 halfWidth = SK_ScalarHalf;
2567             } else {
2568                 halfWidth = SkScalarHalf(devStrokeWidth);
2569             }
2570 
2571             if (strokeOnly) {
2572                 // Outset stroke by 1/4 pixel
2573                 devStrokeWidth += 0.25f;
2574                 // If stroke is greater than width or height, this is still a fill
2575                 // Otherwise we compute stroke params
2576                 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
2577                     innerRadius = devRadius - halfWidth;
2578                     type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
2579                 }
2580             }
2581             outerRadius += halfWidth;
2582             bounds.outset(halfWidth, halfWidth);
2583         }
2584 
2585         // The radii are outset for two reasons. First, it allows the shader to simply perform
2586         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2587         // Second, the outer radius is used to compute the verts of the bounding box that is
2588         // rendered and the outset ensures the box will cover all partially covered by the rrect
2589         // corners.
2590         outerRadius += SK_ScalarHalf;
2591         innerRadius -= SK_ScalarHalf;
2592 
2593         this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
2594 
2595         // Expand the rect for aa to generate correct vertices.
2596         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2597 
2598         fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
2599         fVertCount = rrect_type_to_vert_count(type);
2600         fIndexCount = rrect_type_to_index_count(type);
2601         fAllFill = (kFill_RRectType == type);
2602     }
2603 
2604     const char* name() const override { return "CircularRRectOp"; }
2605 
2606     void visitProxies(const GrVisitProxyFunc& func) const override {
2607         if (fProgramInfo) {
2608             fProgramInfo->visitFPProxies(func);
2609         } else {
2610             fHelper.visitProxies(func);
2611         }
2612     }
2613 
2614     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2615                                       GrClampType clampType) override {
2616         SkPMColor4f* color = &fRRects.front().fColor;
2617         return fHelper.finalizeProcessors(caps, clip, clampType,
2618                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
2619                                           &fWideColor);
2620     }
2621 
2622     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2623 
2624 private:
2625     static void FillInOverstrokeVerts(VertexWriter& verts, const SkRect& bounds, SkScalar smInset,
2626                                       SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2627                                       SkScalar innerRadius, const GrVertexColor& color) {
2628         SkASSERT(smInset < bigInset);
2629 
2630         // TL
2631         verts << (bounds.fLeft + smInset) << (bounds.fTop + smInset)
2632               << color
2633               << xOffset << 0.0f
2634               << outerRadius << innerRadius;
2635 
2636         // TR
2637         verts << (bounds.fRight - smInset) << (bounds.fTop + smInset)
2638               << color
2639               << xOffset << 0.0f
2640               << outerRadius << innerRadius;
2641 
2642         verts << (bounds.fLeft + bigInset) << (bounds.fTop + bigInset)
2643               << color
2644               << 0.0f << 0.0f
2645               << outerRadius << innerRadius;
2646 
2647         verts << (bounds.fRight - bigInset) << (bounds.fTop + bigInset)
2648               << color
2649               << 0.0f << 0.0f
2650               << outerRadius << innerRadius;
2651 
2652         verts << (bounds.fLeft + bigInset) << (bounds.fBottom - bigInset)
2653               << color
2654               << 0.0f << 0.0f
2655               << outerRadius << innerRadius;
2656 
2657         verts << (bounds.fRight - bigInset) << (bounds.fBottom - bigInset)
2658               << color
2659               << 0.0f << 0.0f
2660               << outerRadius << innerRadius;
2661 
2662         // BL
2663         verts << (bounds.fLeft + smInset) << (bounds.fBottom - smInset)
2664               << color
2665               << xOffset << 0.0f
2666               << outerRadius << innerRadius;
2667 
2668         // BR
2669         verts << (bounds.fRight - smInset) << (bounds.fBottom - smInset)
2670               << color
2671               << xOffset << 0.0f
2672               << outerRadius << innerRadius;
2673     }
2674 
2675     GrProgramInfo* programInfo() override { return fProgramInfo; }
2676 
2677     void onCreateProgramInfo(const GrCaps* caps,
2678                              SkArenaAlloc* arena,
2679                              const GrSurfaceProxyView& writeView,
2680                              bool usesMSAASurface,
2681                              GrAppliedClip&& appliedClip,
2682                              const GrDstProxyView& dstProxyView,
2683                              GrXferBarrierFlags renderPassXferBarriers,
2684                              GrLoadOp colorLoadOp) override {
2685         SkASSERT(!usesMSAASurface);
2686 
2687         // Invert the view matrix as a local matrix (if any other processors require coords).
2688         SkMatrix localMatrix;
2689         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
2690             return;
2691         }
2692 
2693         GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
2694                                                                 false, false, false, false,
2695                                                                 fWideColor, localMatrix);
2696 
2697         fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
2698                                                  std::move(appliedClip), dstProxyView, gp,
2699                                                  GrPrimitiveType::kTriangles,
2700                                                  renderPassXferBarriers, colorLoadOp);
2701     }
2702 
2703     void onPrepareDraws(GrMeshDrawTarget* target) override {
2704         if (!fProgramInfo) {
2705             this->createProgramInfo(target);
2706             if (!fProgramInfo) {
2707                 return;
2708             }
2709         }
2710 
2711         sk_sp<const GrBuffer> vertexBuffer;
2712         int firstVertex;
2713 
2714         VertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
2715                                                      fVertCount, &vertexBuffer, &firstVertex)};
2716         if (!verts) {
2717             SkDebugf("Could not allocate vertices\n");
2718             return;
2719         }
2720 
2721         sk_sp<const GrBuffer> indexBuffer;
2722         int firstIndex = 0;
2723         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2724         if (!indices) {
2725             SkDebugf("Could not allocate indices\n");
2726             return;
2727         }
2728 
2729         int currStartVertex = 0;
2730         for (const auto& rrect : fRRects) {
2731             GrVertexColor color(rrect.fColor, fWideColor);
2732             SkScalar outerRadius = rrect.fOuterRadius;
2733             const SkRect& bounds = rrect.fDevBounds;
2734 
2735             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2736                                    bounds.fBottom - outerRadius, bounds.fBottom};
2737 
2738             SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
2739             // The inner radius in the vertex data must be specified in normalized space.
2740             // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
2741             SkScalar innerRadius = rrect.fType != kFill_RRectType
2742                                            ? rrect.fInnerRadius / rrect.fOuterRadius
2743                                            : -1.0f / rrect.fOuterRadius;
2744             for (int i = 0; i < 4; ++i) {
2745                 verts << bounds.fLeft << yCoords[i]
2746                       << color
2747                       << -1.0f << yOuterRadii[i]
2748                       << outerRadius << innerRadius;
2749 
2750                 verts << (bounds.fLeft + outerRadius) << yCoords[i]
2751                       << color
2752                       << 0.0f << yOuterRadii[i]
2753                       << outerRadius << innerRadius;
2754 
2755                 verts << (bounds.fRight - outerRadius) << yCoords[i]
2756                       << color
2757                       << 0.0f << yOuterRadii[i]
2758                       << outerRadius << innerRadius;
2759 
2760                 verts << bounds.fRight << yCoords[i]
2761                       << color
2762                       << 1.0f << yOuterRadii[i]
2763                       << outerRadius << innerRadius;
2764             }
2765             // Add the additional vertices for overstroked rrects.
2766             // Effectively this is an additional stroked rrect, with its
2767             // outer radius = outerRadius - innerRadius, and inner radius = 0.
2768             // This will give us correct AA in the center and the correct
2769             // distance to the outer edge.
2770             //
2771             // Also, the outer offset is a constant vector pointing to the right, which
2772             // guarantees that the distance value along the outer rectangle is constant.
2773             if (kOverstroke_RRectType == rrect.fType) {
2774                 SkASSERT(rrect.fInnerRadius <= 0.0f);
2775 
2776                 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
2777                 // this is the normalized distance from the outer rectangle of this
2778                 // geometry to the outer edge
2779                 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
2780 
2781                 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
2782                                       overstrokeOuterRadius, 0.0f, color);
2783             }
2784 
2785             const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2786             const int primIndexCount = rrect_type_to_index_count(rrect.fType);
2787             for (int i = 0; i < primIndexCount; ++i) {
2788                 *indices++ = primIndices[i] + currStartVertex;
2789             }
2790 
2791             currStartVertex += rrect_type_to_vert_count(rrect.fType);
2792         }
2793 
2794         fMesh = target->allocMesh();
2795         fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
2796                           GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
2797     }
2798 
2799     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2800         if (!fProgramInfo || !fMesh) {
2801             return;
2802         }
2803 
2804         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2805         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
2806         flushState->drawMesh(*fMesh);
2807     }
2808 
2809     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
2810         CircularRRectOp* that = t->cast<CircularRRectOp>();
2811 
2812         // can only represent 65535 unique vertices with 16-bit indices
2813         if (fVertCount + that->fVertCount > 65536) {
2814             return CombineResult::kCannotCombine;
2815         }
2816 
2817         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2818             return CombineResult::kCannotCombine;
2819         }
2820 
2821         if (fHelper.usesLocalCoords() &&
2822             !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2823                                       that->fViewMatrixIfUsingLocalCoords)) {
2824             return CombineResult::kCannotCombine;
2825         }
2826 
2827         fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
2828         fVertCount += that->fVertCount;
2829         fIndexCount += that->fIndexCount;
2830         fAllFill = fAllFill && that->fAllFill;
2831         fWideColor = fWideColor || that->fWideColor;
2832         return CombineResult::kMerged;
2833     }
2834 
2835 #if GR_TEST_UTILS
2836     SkString onDumpInfo() const override {
2837         SkString string;
2838         for (int i = 0; i < fRRects.count(); ++i) {
2839             string.appendf(
2840                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2841                     "InnerRad: %.2f, OuterRad: %.2f\n",
2842                     fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2843                     fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2844                     fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2845                     fRRects[i].fOuterRadius);
2846         }
2847         string += fHelper.dumpInfo();
2848         return string;
2849     }
2850 #endif
2851 
2852     struct RRect {
2853         SkPMColor4f fColor;
2854         SkScalar fInnerRadius;
2855         SkScalar fOuterRadius;
2856         SkRect fDevBounds;
2857         RRectType fType;
2858     };
2859 
2860     SkMatrix fViewMatrixIfUsingLocalCoords;
2861     Helper fHelper;
2862     int fVertCount;
2863     int fIndexCount;
2864     bool fAllFill;
2865     bool fWideColor;
2866     SkSTArray<1, RRect, true> fRRects;
2867 
2868     GrSimpleMesh*  fMesh = nullptr;
2869     GrProgramInfo* fProgramInfo = nullptr;
2870 
2871     using INHERITED = GrMeshDrawOp;
2872 };
2873 
2874 static const int kNumRRectsInIndexBuffer = 256;
2875 
2876 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2877 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2878 static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2879                                                     GrResourceProvider* resourceProvider) {
2880     GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2881     GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2882     switch (type) {
2883         case kFill_RRectType:
2884             return resourceProvider->findOrCreatePatternedIndexBuffer(
2885                     gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2886                     kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2887         case kStroke_RRectType:
2888             return resourceProvider->findOrCreatePatternedIndexBuffer(
2889                     gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2890                     kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2891         default:
2892             SkASSERT(false);
2893             return nullptr;
2894     }
2895 }
2896 
2897 class EllipticalRRectOp : public GrMeshDrawOp {
2898 private:
2899     using Helper = GrSimpleMeshDrawOpHelper;
2900 
2901 public:
2902     DEFINE_OP_CLASS_ID
2903 
2904     // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2905     // whether the rrect is only stroked or stroked and filled.
2906     static GrOp::Owner Make(GrRecordingContext* context,
2907                             GrPaint&& paint,
2908                             const SkMatrix& viewMatrix,
2909                             const SkRect& devRect,
2910                             float devXRadius,
2911                             float devYRadius,
2912                             SkVector devStrokeWidths,
2913                             bool strokeOnly) {
2914         SkASSERT(devXRadius >= 0.5 || strokeOnly);
2915         SkASSERT(devYRadius >= 0.5 || strokeOnly);
2916         SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2917         SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2918         if (devStrokeWidths.fX > 0) {
2919             if (SkScalarNearlyZero(devStrokeWidths.length())) {
2920                 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2921             } else {
2922                 devStrokeWidths.scale(SK_ScalarHalf);
2923             }
2924 
2925             // we only handle thick strokes for near-circular ellipses
2926             if (devStrokeWidths.length() > SK_ScalarHalf &&
2927                 (SK_ScalarHalf * devXRadius > devYRadius ||
2928                  SK_ScalarHalf * devYRadius > devXRadius)) {
2929                 return nullptr;
2930             }
2931 
2932             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2933             if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2934                 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
2935                 return nullptr;
2936             }
2937             if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2938                 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
2939                 return nullptr;
2940             }
2941         }
2942         return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
2943                                                         viewMatrix, devRect,
2944                                                         devXRadius, devYRadius, devStrokeWidths,
2945                                                         strokeOnly);
2946     }
2947 
2948     EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
2949                       const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2950                       float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
2951             : INHERITED(ClassID())
2952             , fHelper(processorSet, GrAAType::kCoverage)
2953             , fUseScale(false) {
2954         SkScalar innerXRadius = 0.0f;
2955         SkScalar innerYRadius = 0.0f;
2956         SkRect bounds = devRect;
2957         bool stroked = false;
2958         if (devStrokeHalfWidths.fX > 0) {
2959             // this is legit only if scale & translation (which should be the case at the moment)
2960             if (strokeOnly) {
2961                 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2962                 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
2963                 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2964             }
2965 
2966             devXRadius += devStrokeHalfWidths.fX;
2967             devYRadius += devStrokeHalfWidths.fY;
2968             bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
2969         }
2970 
2971         fStroked = stroked;
2972         fViewMatrixIfUsingLocalCoords = viewMatrix;
2973         this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
2974         fRRects.emplace_back(
2975                 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2976     }
2977 
2978     const char* name() const override { return "EllipticalRRectOp"; }
2979 
2980     void visitProxies(const GrVisitProxyFunc& func) const override {
2981         if (fProgramInfo) {
2982             fProgramInfo->visitFPProxies(func);
2983         } else {
2984             fHelper.visitProxies(func);
2985         }
2986     }
2987 
2988     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2989                                       GrClampType clampType) override {
2990         fUseScale = !caps.shaderCaps()->floatIs32Bits();
2991         SkPMColor4f* color = &fRRects.front().fColor;
2992         return fHelper.finalizeProcessors(caps, clip, clampType,
2993                                           GrProcessorAnalysisCoverage::kSingleChannel, color,
2994                                           &fWideColor);
2995     }
2996 
2997     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2998 
2999 private:
3000     GrProgramInfo* programInfo() override { return fProgramInfo; }
3001 
3002     void onCreateProgramInfo(const GrCaps* caps,
3003                              SkArenaAlloc* arena,
3004                              const GrSurfaceProxyView& writeView,
3005                              bool usesMSAASurface,
3006                              GrAppliedClip&& appliedClip,
3007                              const GrDstProxyView& dstProxyView,
3008                              GrXferBarrierFlags renderPassXferBarriers,
3009                              GrLoadOp colorLoadOp) override {
3010         SkMatrix localMatrix;
3011         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
3012             return;
3013         }
3014 
3015         GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
3016                                                                  fUseScale, localMatrix);
3017 
3018         fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
3019                                                  std::move(appliedClip), dstProxyView, gp,
3020                                                  GrPrimitiveType::kTriangles,
3021                                                  renderPassXferBarriers, colorLoadOp);
3022     }
3023 
3024     void onPrepareDraws(GrMeshDrawTarget* target) override {
3025         if (!fProgramInfo) {
3026             this->createProgramInfo(target);
3027             if (!fProgramInfo) {
3028                 return;
3029             }
3030         }
3031 
3032         // drop out the middle quad if we're stroked
3033         int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
3034         sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3035                 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
3036 
3037         if (!indexBuffer) {
3038             SkDebugf("Could not allocate indices\n");
3039             return;
3040         }
3041         PatternHelper helper(target, GrPrimitiveType::kTriangles,
3042                              fProgramInfo->geomProc().vertexStride(),
3043                              std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
3044                              fRRects.count(), kNumRRectsInIndexBuffer);
3045         VertexWriter verts{helper.vertices()};
3046         if (!verts) {
3047             SkDebugf("Could not allocate vertices\n");
3048             return;
3049         }
3050 
3051         for (const auto& rrect : fRRects) {
3052             GrVertexColor color(rrect.fColor, fWideColor);
3053             // Compute the reciprocals of the radii here to save time in the shader
3054             float reciprocalRadii[4] = {
3055                 SkScalarInvert(rrect.fXRadius),
3056                 SkScalarInvert(rrect.fYRadius),
3057                 SkScalarInvert(rrect.fInnerXRadius),
3058                 SkScalarInvert(rrect.fInnerYRadius)
3059             };
3060 
3061             // If the stroke width is exactly double the radius, the inner radii will be zero.
3062             // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3063             reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3064             reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3065 
3066             // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3067             // full sample coverage.
3068             float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3069 
3070             // Extend out the radii to antialias.
3071             SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3072             SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
3073 
3074             SkScalar xMaxOffset = xOuterRadius;
3075             SkScalar yMaxOffset = yOuterRadius;
3076             if (!fStroked) {
3077                 // For filled rrects we map a unit circle in the vertex attributes rather than
3078                 // computing an ellipse and modifying that distance, so we normalize to 1.
3079                 xMaxOffset /= rrect.fXRadius;
3080                 yMaxOffset /= rrect.fYRadius;
3081             }
3082 
3083             const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
3084 
3085             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3086                                    bounds.fBottom - yOuterRadius, bounds.fBottom};
3087             SkScalar yOuterOffsets[4] = {yMaxOffset,
3088                                          SK_ScalarNearlyZero,  // we're using inversesqrt() in
3089                                                                // shader, so can't be exactly 0
3090                                          SK_ScalarNearlyZero, yMaxOffset};
3091 
3092             auto maybeScale = VertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
3093             for (int i = 0; i < 4; ++i) {
3094                 verts << bounds.fLeft << yCoords[i]
3095                       << color
3096                       << xMaxOffset << yOuterOffsets[i]
3097                       << maybeScale
3098                       << reciprocalRadii;
3099 
3100                 verts << (bounds.fLeft + xOuterRadius) << yCoords[i]
3101                       << color
3102                       << SK_ScalarNearlyZero << yOuterOffsets[i]
3103                       << maybeScale
3104                       << reciprocalRadii;
3105 
3106                 verts << (bounds.fRight - xOuterRadius) << yCoords[i]
3107                       << color
3108                       << SK_ScalarNearlyZero << yOuterOffsets[i]
3109                       << maybeScale
3110                       << reciprocalRadii;
3111 
3112                 verts << bounds.fRight << yCoords[i]
3113                       << color
3114                       << xMaxOffset << yOuterOffsets[i]
3115                       << maybeScale
3116                       << reciprocalRadii;
3117             }
3118         }
3119         fMesh = helper.mesh();
3120     }
3121 
3122     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
3123         if (!fProgramInfo || !fMesh) {
3124             return;
3125         }
3126 
3127         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3128         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
3129         flushState->drawMesh(*fMesh);
3130     }
3131 
3132     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
3133         EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
3134 
3135         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
3136             return CombineResult::kCannotCombine;
3137         }
3138 
3139         if (fStroked != that->fStroked) {
3140             return CombineResult::kCannotCombine;
3141         }
3142 
3143         if (fHelper.usesLocalCoords() &&
3144             !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3145                                       that->fViewMatrixIfUsingLocalCoords)) {
3146             return CombineResult::kCannotCombine;
3147         }
3148 
3149         fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
3150         fWideColor = fWideColor || that->fWideColor;
3151         return CombineResult::kMerged;
3152     }
3153 
3154 #if GR_TEST_UTILS
3155     SkString onDumpInfo() const override {
3156         SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3157         for (const auto& geo : fRRects) {
3158             string.appendf(
3159                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3160                     "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3161                     geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3162                     geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3163                     geo.fInnerXRadius, geo.fInnerYRadius);
3164         }
3165         string += fHelper.dumpInfo();
3166         return string;
3167     }
3168 #endif
3169 
3170     struct RRect {
3171         SkPMColor4f fColor;
3172         SkScalar fXRadius;
3173         SkScalar fYRadius;
3174         SkScalar fInnerXRadius;
3175         SkScalar fInnerYRadius;
3176         SkRect fDevBounds;
3177     };
3178 
3179     SkMatrix fViewMatrixIfUsingLocalCoords;
3180     Helper fHelper;
3181     bool fStroked;
3182     bool fWideColor;
3183     bool fUseScale;
3184     SkSTArray<1, RRect, true> fRRects;
3185 
3186     GrSimpleMesh*  fMesh = nullptr;
3187     GrProgramInfo* fProgramInfo = nullptr;
3188 
3189     using INHERITED = GrMeshDrawOp;
3190 };
3191 
3192 GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3193                                                  GrPaint&& paint,
3194                                                  const SkMatrix& viewMatrix,
3195                                                  const SkRRect& rrect,
3196                                                  const SkStrokeRec& stroke,
3197                                                  const GrShaderCaps* shaderCaps) {
3198     SkASSERT(viewMatrix.rectStaysRect());
3199     SkASSERT(viewMatrix.isSimilarity());
3200     SkASSERT(rrect.isSimple());
3201     SkASSERT(!rrect.isOval());
3202     SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3203 
3204     // RRect ops only handle simple, but not too simple, rrects.
3205     // Do any matrix crunching before we reset the draw state for device coords.
3206     const SkRect& rrectBounds = rrect.getBounds();
3207     SkRect bounds;
3208     viewMatrix.mapRect(&bounds, rrectBounds);
3209 
3210     SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3211     SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3212                                                   viewMatrix[SkMatrix::kMSkewY]));
3213 
3214     // Do mapping of stroke. Use -1 to indicate fill-only draws.
3215     SkScalar scaledStroke = -1;
3216     SkScalar strokeWidth = stroke.getWidth();
3217     SkStrokeRec::Style style = stroke.getStyle();
3218 
3219     bool isStrokeOnly =
3220         SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3221     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3222 
3223     if (hasStroke) {
3224         if (SkStrokeRec::kHairline_Style == style) {
3225             scaledStroke = SK_Scalar1;
3226         } else {
3227             scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3228                                                       viewMatrix[SkMatrix::kMSkewY]));
3229         }
3230     }
3231 
3232     // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3233     // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3234     // patch will have fractional coverage. This only matters when the interior is actually filled.
3235     // We could consider falling back to rect rendering here, since a tiny radius is
3236     // indistinguishable from a square corner.
3237     if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3238         return nullptr;
3239     }
3240 
3241     return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3242                                  scaledStroke, isStrokeOnly);
3243 }
3244 
3245 GrOp::Owner make_rrect_op(GrRecordingContext* context,
3246                           GrPaint&& paint,
3247                           const SkMatrix& viewMatrix,
3248                           const SkRRect& rrect,
3249                           const SkStrokeRec& stroke) {
3250     SkASSERT(viewMatrix.rectStaysRect());
3251     SkASSERT(rrect.isSimple());
3252     SkASSERT(!rrect.isOval());
3253 
3254     // RRect ops only handle simple, but not too simple, rrects.
3255     // Do any matrix crunching before we reset the draw state for device coords.
3256     const SkRect& rrectBounds = rrect.getBounds();
3257     SkRect bounds;
3258     viewMatrix.mapRect(&bounds, rrectBounds);
3259 
3260     SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
3261     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3262                                    viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3263     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3264                                    viewMatrix[SkMatrix::kMScaleY] * radii.fY);
3265 
3266     SkStrokeRec::Style style = stroke.getStyle();
3267 
3268     // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3269     SkVector scaledStroke = {-1, -1};
3270     SkScalar strokeWidth = stroke.getWidth();
3271 
3272     bool isStrokeOnly =
3273             SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3274     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3275 
3276     if (hasStroke) {
3277         if (SkStrokeRec::kHairline_Style == style) {
3278             scaledStroke.set(1, 1);
3279         } else {
3280             scaledStroke.fX = SkScalarAbs(
3281                     strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3282             scaledStroke.fY = SkScalarAbs(
3283                     strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
3284         }
3285 
3286         // if half of strokewidth is greater than radius, we don't handle that right now
3287         if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3288              SK_ScalarHalf * scaledStroke.fY > yRadius)) {
3289             return nullptr;
3290         }
3291     }
3292 
3293     // The matrix may have a rotation by an odd multiple of 90 degrees.
3294     if (viewMatrix.getScaleX() == 0) {
3295         std::swap(xRadius, yRadius);
3296         std::swap(scaledStroke.fX, scaledStroke.fY);
3297     }
3298 
3299     // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3300     // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3301     // patch will have fractional coverage. This only matters when the interior is actually filled.
3302     // We could consider falling back to rect rendering here, since a tiny radius is
3303     // indistinguishable from a square corner.
3304     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
3305         return nullptr;
3306     }
3307 
3308     // if the corners are circles, use the circle renderer
3309     return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3310                                    xRadius, yRadius, scaledStroke, isStrokeOnly);
3311 }
3312 
3313 GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3314                                          GrPaint&& paint,
3315                                          const SkMatrix& viewMatrix,
3316                                          const SkRRect& rrect,
3317                                          const SkStrokeRec& stroke,
3318                                          const GrShaderCaps* shaderCaps) {
3319     if (rrect.isOval()) {
3320         return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
3321                           GrStyle(stroke, nullptr), shaderCaps);
3322     }
3323 
3324     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
3325         return nullptr;
3326     }
3327 
3328     return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
3329 }
3330 
3331 ///////////////////////////////////////////////////////////////////////////////
3332 
3333 GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3334                                           GrPaint&& paint,
3335                                           const SkMatrix& viewMatrix,
3336                                           const SkRect& oval,
3337                                           const GrStyle& style,
3338                                           const GrShaderCaps* shaderCaps) {
3339     SkScalar width = oval.width();
3340     SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3341              circle_stays_circle(viewMatrix));
3342 
3343     auto r = width / 2.f;
3344     SkPoint center = { oval.centerX(), oval.centerY() };
3345     if (style.hasNonDashPathEffect()) {
3346         return nullptr;
3347     } else if (style.isDashed()) {
3348         if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3349             style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3350             return nullptr;
3351         }
3352         auto onInterval = style.dashIntervals()[0];
3353         auto offInterval = style.dashIntervals()[1];
3354         if (offInterval == 0) {
3355             GrStyle strokeStyle(style.strokeRec(), nullptr);
3356             return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3357                               strokeStyle, shaderCaps);
3358         } else if (onInterval == 0) {
3359             // There is nothing to draw but we have no way to indicate that here.
3360             return nullptr;
3361         }
3362         auto angularOnInterval = onInterval / r;
3363         auto angularOffInterval = offInterval / r;
3364         auto phaseAngle = style.dashPhase() / r;
3365         // Currently this function doesn't accept ovals with different start angles, though
3366         // it could.
3367         static const SkScalar kStartAngle = 0.f;
3368         return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3369                                            style.strokeRec().getWidth(), kStartAngle,
3370                                            angularOnInterval, angularOffInterval, phaseAngle);
3371     }
3372     return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3373 }
3374 
3375 GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3376                                         GrPaint&& paint,
3377                                         const SkMatrix& viewMatrix,
3378                                         const SkRect& oval,
3379                                         const GrStyle& style,
3380                                         const GrShaderCaps* shaderCaps) {
3381     if (style.pathEffect()) {
3382         return nullptr;
3383     }
3384 
3385     // prefer the device space ellipse op for batchability
3386     if (viewMatrix.rectStaysRect()) {
3387         return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
3388     }
3389 
3390     // Otherwise, if we have shader derivative support, render as device-independent
3391     if (shaderCaps->shaderDerivativeSupport()) {
3392         SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3393         SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3394         SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3395         SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3396         // Check for near-degenerate matrix
3397         if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
3398             return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
3399                                      style.strokeRec());
3400         }
3401     }
3402 
3403     return nullptr;
3404 }
3405 
3406 ///////////////////////////////////////////////////////////////////////////////
3407 
3408 GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3409                                        GrPaint&& paint,
3410                                        const SkMatrix& viewMatrix,
3411                                        const SkRect& oval, SkScalar startAngle,
3412                                        SkScalar sweepAngle, bool useCenter,
3413                                        const GrStyle& style,
3414                                        const GrShaderCaps* shaderCaps) {
3415     SkASSERT(!oval.isEmpty());
3416     SkASSERT(sweepAngle);
3417     SkScalar width = oval.width();
3418     if (SkScalarAbs(sweepAngle) >= 360.f) {
3419         return nullptr;
3420     }
3421     if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3422         return nullptr;
3423     }
3424     SkPoint center = {oval.centerX(), oval.centerY()};
3425     CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3426                                      useCenter};
3427     return CircleOp::Make(context, std::move(paint), viewMatrix,
3428                           center, width / 2.f, style, &arcParams);
3429 }
3430 
3431 ///////////////////////////////////////////////////////////////////////////////
3432 
3433 #if GR_TEST_UTILS
3434 
3435 GR_DRAW_OP_TEST_DEFINE(CircleOp) {
3436     if (numSamples > 1) {
3437         return nullptr;
3438     }
3439 
3440     do {
3441         SkScalar rotate = random->nextSScalar1() * 360.f;
3442         SkScalar translateX = random->nextSScalar1() * 1000.f;
3443         SkScalar translateY = random->nextSScalar1() * 1000.f;
3444         SkScalar scale;
3445         do {
3446             scale = random->nextSScalar1() * 100.f;
3447         } while (scale == 0);
3448         SkMatrix viewMatrix;
3449         viewMatrix.setRotate(rotate);
3450         viewMatrix.postTranslate(translateX, translateY);
3451         viewMatrix.postScale(scale, scale);
3452         SkRect circle = GrTest::TestSquare(random);
3453         SkPoint center = {circle.centerX(), circle.centerY()};
3454         SkScalar radius = circle.width() / 2.f;
3455         SkStrokeRec stroke = GrTest::TestStrokeRec(random);
3456         CircleOp::ArcParams arcParamsTmp;
3457         const CircleOp::ArcParams* arcParams = nullptr;
3458         if (random->nextBool()) {
3459             arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
3460             arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3461             arcParamsTmp.fUseCenter = random->nextBool();
3462             arcParams = &arcParamsTmp;
3463         }
3464         GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3465                                         center, radius,
3466                                         GrStyle(stroke, nullptr), arcParams);
3467         if (op) {
3468             return op;
3469         }
3470         assert_alive(paint);
3471     } while (true);
3472 }
3473 
3474 GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3475     if (numSamples > 1) {
3476         return nullptr;
3477     }
3478 
3479     SkScalar rotate = random->nextSScalar1() * 360.f;
3480     SkScalar translateX = random->nextSScalar1() * 1000.f;
3481     SkScalar translateY = random->nextSScalar1() * 1000.f;
3482     SkScalar scale;
3483     do {
3484         scale = random->nextSScalar1() * 100.f;
3485     } while (scale == 0);
3486     SkMatrix viewMatrix;
3487     viewMatrix.setRotate(rotate);
3488     viewMatrix.postTranslate(translateX, translateY);
3489     viewMatrix.postScale(scale, scale);
3490     SkRect circle = GrTest::TestSquare(random);
3491     SkPoint center = {circle.centerX(), circle.centerY()};
3492     SkScalar radius = circle.width() / 2.f;
3493     SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3494     SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3495     SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3496     SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3497     SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
3498     return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3499                                        center, radius, strokeWidth,
3500                                        startAngle, onAngle, offAngle, phase);
3501 }
3502 
3503 GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
3504     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
3505     SkRect ellipse = GrTest::TestSquare(random);
3506     return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3507                            GrTest::TestStrokeRec(random));
3508 }
3509 
3510 GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
3511     SkMatrix viewMatrix = GrTest::TestMatrix(random);
3512     SkRect ellipse = GrTest::TestSquare(random);
3513     return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3514                              GrTest::TestStrokeRec(random));
3515 }
3516 
3517 GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3518     do {
3519         SkScalar rotate = random->nextSScalar1() * 360.f;
3520         SkScalar translateX = random->nextSScalar1() * 1000.f;
3521         SkScalar translateY = random->nextSScalar1() * 1000.f;
3522         SkScalar scale;
3523         do {
3524             scale = random->nextSScalar1() * 100.f;
3525         } while (scale == 0);
3526         SkMatrix viewMatrix;
3527         viewMatrix.setRotate(rotate);
3528         viewMatrix.postTranslate(translateX, translateY);
3529         viewMatrix.postScale(scale, scale);
3530         SkRect rect = GrTest::TestRect(random);
3531         SkScalar radius = random->nextRangeF(0.1f, 10.f);
3532         SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3533         if (rrect.isOval()) {
3534             continue;
3535         }
3536         GrOp::Owner op =
3537                 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3538                                                      GrTest::TestStrokeRec(random), nullptr);
3539         if (op) {
3540             return op;
3541         }
3542         assert_alive(paint);
3543     } while (true);
3544 }
3545 
3546 GR_DRAW_OP_TEST_DEFINE(RRectOp) {
3547     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
3548     const SkRRect& rrect = GrTest::TestRRectSimple(random);
3549     return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
3550                          GrTest::TestStrokeRec(random));
3551 }
3552 
3553 #endif
3554