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