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