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