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