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