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