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