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