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